Merge pull request #9033 from rjmccall/rvalue-pointer-conversions

This commit is contained in:
swift-ci
2017-04-26 12:51:44 -07:00
committed by GitHub
11 changed files with 1197 additions and 229 deletions

View File

@@ -0,0 +1,344 @@
//===- ExternalUnion.h - A union with an external discriminator -*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file defines the ExternalUnion class, which allows clients to
// conveniently define unions of possibly non-trivial types whose
// discriminator will be provided externally.
//
// It's the client's responsibility to call the appropriate
// "special members" within its own special members.
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_BASIC_EXTERNALUNION_H
#define SWIFT_BASIC_EXTERNALUNION_H
#include "llvm/Support/Compiler.h"
#include <type_traits>
#include <utility>
#include <assert.h>
namespace swift {
template <size_t... Values>
struct static_max;
template <size_t Value>
struct static_max<Value> {
static const size_t value = Value;
};
template <size_t Head, size_t... Tail>
struct static_max<Head, Tail...> {
static const size_t value =
(Head > static_max<Tail...>::value ? Head : static_max<Tail...>::value);
};
template <class T, class... Members>
struct indexOf;
template <class T>
struct indexOf<T> {
constexpr static const int value = -1;
};
template <class T, class U, class... Members>
struct indexOf<T, U, Members...> {
private:
constexpr static const int indexInTail = indexOf<T, Members...>::value;
public:
constexpr static const int value =
(std::is_same<T, U>::value ? 0 :
indexInTail != -1 ? indexInTail + 1 : -1);
};
template <class... Members>
struct SpecialMembers;
/// An external union whose discriminator is just a position in
/// the template arguments.
///
/// The external union itself is a trivial type, and it is the
/// responsibility of the client to call the "special member functions"
/// at the appropriate time.
template <class... Members>
class BasicExternalUnion {
/// The value storage.
LLVM_ALIGNAS(static_max<alignof(Members)...>::value)
char Storage[static_max<sizeof(Members)...>::value];
public:
/// Construct a union member in-place.
template <class T, class... Args>
T &emplaceWithoutIndex(int index, Args &&... args) {
constexpr int typeIndex = indexOf<T, Members...>::value;
static_assert(typeIndex != -1, "type not in union");
return *(::new ((void*) &Storage) T(std::forward<Args>(args)...));
}
/// Construct a union member in-place.
template <class T, class... Args>
T &emplace(int index, Args &&... args) {
constexpr int typeIndex = indexOf<T, Members...>::value;
static_assert(typeIndex != -1, "type not in union");
assert(index == typeIndex && "current kind is wrong for value");
return *(::new ((void*) &Storage) T(std::forward<Args>(args)...));
}
/// Return a reference to a union member.
template <class T>
T &getWithoutIndex() {
constexpr int typeIndex = indexOf<T, Members...>::value;
static_assert(typeIndex != -1, "type not in union");
return reinterpret_cast<T &>(Storage);
}
/// Return a reference to a union member.
template <class T>
const T &getWithoutIndex() const {
constexpr int typeIndex = indexOf<T, Members...>::value;
static_assert(typeIndex != -1, "type not in union");
return reinterpret_cast<const T &>(Storage);
}
/// Return a reference to a union member, asserting that the current
/// kind matches the type being extracted.
template <class T>
T &get(int index) {
constexpr int typeIndex = indexOf<T, Members...>::value;
static_assert(typeIndex != -1, "type not in union");
assert(index == typeIndex && "current kind is wrong for access");
return reinterpret_cast<T &>(Storage);
}
/// Return a reference to a union member, asserting that the current
/// kind matches the type being extracted.
template <class T>
const T &get(int index) const {
constexpr int typeIndex = indexOf<T, Members...>::value;
static_assert(typeIndex != -1, "type not in union");
assert(index == typeIndex && "current kind is wrong for access");
return reinterpret_cast<const T &>(Storage);
}
/// Copy-construct the union from another union.
void copyConstruct(int index, const BasicExternalUnion &other) {
if (index != -1) {
SpecialMembers<Members...>::copyConstruct(Storage, index, other.Storage);
}
}
/// Move-construct the union from another union.
void moveConstruct(int index, BasicExternalUnion &&other) {
if (index != -1) {
SpecialMembers<Members...>::moveConstruct(Storage, index, other.Storage);
}
}
/// Copy-assign the union from another union.
void copyAssign(int thisIndex, int otherIndex,
const BasicExternalUnion &other) {
if (this == &other) {
// do nothing
} else if (thisIndex == otherIndex) {
if (thisIndex != -1) {
SpecialMembers<Members...>::copyAssignSame(thisIndex, Storage,
other.Storage);
}
} else {
destruct(thisIndex, Storage);
copyConstruct(otherIndex, other);
}
}
/// Move-assign the union from another union.
void moveAssign(int thisIndex, int otherIndex,
BasicExternalUnion &&other) {
assert(this != &other && "move-constructing value into itself?");
if (thisIndex == otherIndex) {
if (thisIndex != -1) {
SpecialMembers<Members...>::moveAssignSame(thisIndex, Storage,
other.Storage);
}
} else {
destruct(thisIndex);
moveConstruct(otherIndex, std::move(other));
}
}
/// Destroy the union from another union.
void destruct(int index) {
if (index != -1) {
SpecialMembers<Members...>::destruct(index, Storage);
}
}
};
/// An external union whose membership is determined by a kind type
/// whose members are not necessarily 1-1 with the members of the union.
///
/// Clients must provide a function which translates the kind type
/// into an index into the union's Members list, or -1 for kind values
/// which do not require data in the union.
template <class Kind, int (&GetIndexForKind)(Kind), class... Members>
class ExternalUnion {
BasicExternalUnion<Members...> Union;
public:
/// Construct a union member in-place.
template <class T, class... Args>
T &emplace(Kind kind, Args &&... args) {
#ifndef NDEBUG
return Union.template emplace<T>(GetIndexForKind(kind),
std::forward<Args>(args)...);
#else
return Union.template emplaceWithoutIndex<T>(std::forward<Args>(args)...);
#endif
}
/// Return a reference to a union member, asserting that the current
/// kind is right.
template <class T>
T &get(Kind kind) {
#ifndef NDEBUG
return Union.template get<T>(GetIndexForKind(kind));
#else
return Union.template getWithoutIndex<T>();
#endif
}
/// Return a reference to a union member, asserting that the current
/// kind is right.
template <class T>
const T &get(Kind kind) const {
#ifndef NDEBUG
return Union.template get<T>(GetIndexForKind(kind));
#else
return Union.template getWithoutIndex<T>();
#endif
}
/// Copy-construct the union from another union.
void copyConstruct(Kind kind, const ExternalUnion &other) {
Union.copyConstruct(GetIndexForKind(kind), other.Union);
}
/// Move-construct the union from another union.
void moveConstruct(Kind kind, ExternalUnion &&other) {
Union.moveConstruct(GetIndexForKind(kind), std::move(other.Union));
}
/// Copy-assign the union from another union.
void copyAssign(Kind thisKind, Kind otherKind, const ExternalUnion &other) {
Union.copyAssign(GetIndexForKind(thisKind), GetIndexForKind(otherKind),
other.Union);
}
/// Move-assign the union from another union.
void moveAssign(Kind thisKind, Kind otherKind, ExternalUnion &&other) {
Union.moveAssign(GetIndexForKind(thisKind), GetIndexForKind(otherKind),
std::move(other.Union));
}
/// Destroy the union from another union.
void destruct(Kind kind) {
Union.destruct(GetIndexForKind(kind));
}
};
/// A helper class for defining special members.
template <>
struct SpecialMembers<> {
LLVM_ATTRIBUTE_ALWAYS_INLINE
static void copyConstruct(void *self, int index, const void *other) {
llvm_unreachable("bad index");
}
LLVM_ATTRIBUTE_ALWAYS_INLINE
static void moveConstruct(void *self, int index, void *other) {
llvm_unreachable("bad index");
}
LLVM_ATTRIBUTE_ALWAYS_INLINE
static void copyAssignSame(int index, void *self, const void *other) {
llvm_unreachable("bad index");
}
LLVM_ATTRIBUTE_ALWAYS_INLINE
static void moveAssignSame(int index, void *self, void *other) {
llvm_unreachable("bad index");
}
LLVM_ATTRIBUTE_ALWAYS_INLINE
static void destruct(int index, void *self) {
llvm_unreachable("bad index");
}
};
template <class T, class... Others>
struct SpecialMembers<T, Others...> {
LLVM_ATTRIBUTE_ALWAYS_INLINE
static void copyConstruct(void *self, int index, const void *other) {
if (index == 0) {
::new (self) T(*static_cast<const T *>(other));
} else {
SpecialMembers<Others...>::copyConstruct(self, index - 1, other);
}
}
LLVM_ATTRIBUTE_ALWAYS_INLINE
static void moveConstruct(void *self, int index, void *other) {
if (index == 0) {
::new (self) T(std::move(*static_cast<T *>(other)));
} else {
SpecialMembers<Others...>::moveConstruct(self, index - 1, other);
}
}
LLVM_ATTRIBUTE_ALWAYS_INLINE
static void copyAssignSame(int index, void *self, const void *other) {
if (index == 0) {
*static_cast<T*>(self) = *static_cast<const T *>(other);
} else {
SpecialMembers<Others...>::copyAssignSame(index - 1, self, other);
}
}
LLVM_ATTRIBUTE_ALWAYS_INLINE
static void moveAssignSame(int index, void *self, void *other) {
if (index == 0) {
*static_cast<T*>(self) = std::move(*static_cast<T *>(other));
} else {
SpecialMembers<Others...>::moveAssignSame(index - 1, self, other);
}
}
LLVM_ATTRIBUTE_ALWAYS_INLINE
static void destruct(int index, void *self) {
if (index == 0) {
static_cast<T*>(self)->T::~T();
} else {
SpecialMembers<Others...>::destruct(index - 1, self);
}
}
};
} // end namespace swift
#endif // SWIFT_BASIC_CLUSTEREDBITVECTOR_H

View File

@@ -521,6 +521,9 @@ public:
/// Get the builtin word type as a SILType;
static SILType getBuiltinWordType(const ASTContext &C);
/// Given a value type, return an optional type wrapping it.
static SILType getOptionalType(SILType valueType);
/// Get the standard exception type.
static SILType getExceptionType(const ASTContext &C);

View File

@@ -1361,19 +1361,53 @@ public:
}
void updateExprToPointerWhitelist(Expr *Base, Expr *Arg) {
auto handleSubExpr = [&](Expr *SubExpr) {
// if we have an inject into optional, strip it off.
if (auto *InjectIntoOpt = dyn_cast<InjectIntoOptionalExpr>(SubExpr)) {
SubExpr = InjectIntoOpt->getSubExpr();
auto handleSubExpr = [&](Expr *origSubExpr) {
auto subExpr = origSubExpr;
unsigned optionalDepth = 0;
auto checkIsBindOptional = [&](Expr *expr) {
for (unsigned depth = optionalDepth; depth; --depth) {
if (auto bind = dyn_cast<BindOptionalExpr>(expr)) {
expr = bind->getSubExpr();
} else {
Out << "malformed optional pointer conversion\n";
origSubExpr->print(Out);
Out << '\n';
abort();
}
}
};
// These outer entities will be interleaved in multi-level optionals.
while (true) {
// Look through optional evaluations.
if (auto *optionalEval = dyn_cast<OptionalEvaluationExpr>(subExpr)) {
subExpr = optionalEval->getSubExpr();
optionalDepth++;
continue;
}
// Look through injections into Optional<Pointer>.
if (auto *injectIntoOpt = dyn_cast<InjectIntoOptionalExpr>(subExpr)) {
subExpr = injectIntoOpt->getSubExpr();
continue;
}
break;
}
if (auto *InOutToPtr = dyn_cast<InOutToPointerExpr>(SubExpr)) {
WhitelistedInOutToPointerExpr.insert(InOutToPtr);
// Whitelist inout-to-pointer conversions.
if (auto *inOutToPtr = dyn_cast<InOutToPointerExpr>(subExpr)) {
WhitelistedInOutToPointerExpr.insert(inOutToPtr);
checkIsBindOptional(inOutToPtr->getSubExpr());
return;
}
if (auto *ArrayToPtr = dyn_cast<ArrayToPointerExpr>(SubExpr)) {
WhitelistedArrayToPointerExpr.insert(ArrayToPtr);
// Whitelist array-to-pointer conversions.
if (auto *arrayToPtr = dyn_cast<ArrayToPointerExpr>(subExpr)) {
WhitelistedArrayToPointerExpr.insert(arrayToPtr);
checkIsBindOptional(arrayToPtr->getSubExpr());
return;
}
};

View File

@@ -64,6 +64,13 @@ SILType SILType::getBuiltinWordType(const ASTContext &C) {
return getPrimitiveObjectType(CanType(BuiltinIntegerType::getWordType(C)));
}
SILType SILType::getOptionalType(SILType type) {
auto &ctx = type.getSwiftRValueType()->getASTContext();
auto optType = BoundGenericEnumType::get(ctx.getOptionalDecl(), Type(),
{ type.getSwiftRValueType() });
return getPrimitiveType(CanType(optType), type.getCategory());
}
bool SILType::isTrivial(SILModule &M) const {
return M.getTypeLowering(*this).isTrivial();
}

View File

@@ -145,6 +145,26 @@ ManagedValue ManagedValue::formalAccessBorrow(SILGenFunction &SGF,
return SGF.emitFormalEvaluationManagedBeginBorrow(loc, getValue());
}
ManagedValue ManagedValue::materialize(SILGenFunction &SGF,
SILLocation loc) const {
auto temporary = SGF.emitTemporaryAllocation(loc, getType());
bool hadCleanup = hasCleanup();
// The temporary memory is +0 if the value was.
if (hadCleanup) {
SGF.B.emitStoreValueOperation(loc, forward(SGF), temporary,
StoreOwnershipQualifier::Init);
// SEMANTIC SIL TODO: This should really be called a temporary LValue.
return ManagedValue::forOwnedAddressRValue(temporary,
SGF.enterDestroyCleanup(temporary));
} else {
auto object = SGF.emitManagedBeginBorrow(loc, getValue());
SGF.emitManagedStoreBorrow(loc, object.getValue(), temporary);
return ManagedValue::forBorrowedAddressRValue(temporary);
}
}
void ManagedValue::print(raw_ostream &os) const {
if (SILValue v = getValue()) {
v->print(os);

View File

@@ -270,6 +270,10 @@ public:
return isLValue() ? *this : ManagedValue::forUnmanaged(getValue());
}
/// Given a scalar value, materialize it into memory with the
/// exact same level of cleanup it had before.
ManagedValue materialize(SILGenFunction &SGF, SILLocation loc) const;
/// Disable the cleanup for this value.
void forwardCleanup(SILGenFunction &SGF) const;

View File

@@ -26,6 +26,7 @@
#include "swift/AST/ForeignErrorConvention.h"
#include "swift/AST/Module.h"
#include "swift/AST/SubstitutionMap.h"
#include "swift/Basic/ExternalUnion.h"
#include "swift/Basic/Range.h"
#include "swift/Basic/STLExtras.h"
#include "swift/Basic/Unicode.h"
@@ -1937,7 +1938,32 @@ static CanType claimNextParamClause(CanAnyFunctionType &type) {
return result;
}
class InOutArgument {
namespace {
/// The original argument expression for some sort of complex
/// argument emission.
class OriginalArgument {
llvm::PointerIntPair<Expr*, 1, bool> ExprAndIsIndirect;
public:
OriginalArgument() = default;
OriginalArgument(Expr *expr, bool indirect)
: ExprAndIsIndirect(expr, indirect) {}
Expr *getExpr() const { return ExprAndIsIndirect.getPointer(); }
bool isIndirect() const { return ExprAndIsIndirect.getInt(); }
};
/// A delayed argument. Call arguments are evaluated in two phases:
/// a formal evaluation phase and a formal access phase. The primary
/// example of this is an l-value that is passed by reference, where
/// the access to the l-value does not begin until the formal access
/// phase, but there are other examples, generally relating to pointer
/// conversions.
///
/// A DelayedArgument represents the part of evaluating an argument
/// that's been delayed until the formal access phase.
class DelayedArgument {
public:
enum KindTy {
/// This is a true inout argument.
@@ -1949,41 +1975,130 @@ public:
/// This is a borrowed indirect argument.
BorrowIndirect,
LastLVKindWithoutExtra = BorrowIndirect,
/// The l-value needs to be converted to a pointer type.
LValueToPointer,
/// An array l-value needs to be converted to a pointer type.
ArrayToPointer,
LValueArrayToPointer,
LastLVKind = LValueArrayToPointer,
/// An array r-value needs to be converted to a pointer type.
RValueArrayToPointer,
/// A string r-value needs to be converted to a pointer type.
RValueStringToPointer,
};
private:
KindTy Kind;
LValue LV;
SILLocation Loc;
Expr *OriginalExpr;
union {
SILGenFunction::PointerAccessInfo PointerInfo;
SILGenFunction::ArrayAccessInfo ArrayInfo;
struct LValueStorage {
LValue LV;
SILLocation Loc;
LValueStorage(LValue &&lv, SILLocation loc) : LV(std::move(lv)), Loc(loc) {}
};
struct RValueStorage {
ManagedValue RV;
RValueStorage(ManagedValue rv) : RV(rv) {}
};
public:
InOutArgument(KindTy kind, LValue &&lv, SILLocation loc)
: Kind(kind), LV(std::move(lv)), Loc(loc) {}
InOutArgument(SILGenFunction::PointerAccessInfo pointerInfo,
LValue &&lv, SILLocation loc, Expr *originalExpr)
: Kind(LValueToPointer), LV(std::move(lv)), Loc(loc),
OriginalExpr(originalExpr) {
PointerInfo = pointerInfo;
static int getUnionIndexForValue(KindTy kind) {
return (kind <= LastLVKind ? 0 : 1);
}
InOutArgument(SILGenFunction::ArrayAccessInfo arrayInfo,
LValue &&lv, SILLocation loc, Expr *originalExpr)
: Kind(ArrayToPointer), LV(std::move(lv)), Loc(loc),
OriginalExpr(originalExpr) {
ArrayInfo = arrayInfo;
/// Storage for either the l-value or the r-value.
ExternalUnion<KindTy, getUnionIndexForValue,
LValueStorage, RValueStorage> Value;
LValueStorage &LV() { return Value.get<LValueStorage>(Kind); }
const LValueStorage &LV() const { return Value.get<LValueStorage>(Kind); }
RValueStorage &RV() { return Value.get<RValueStorage>(Kind); }
const RValueStorage &RV() const { return Value.get<RValueStorage>(Kind); }
/// The original argument expression, which will be emitted down
/// to the point from which the l-value or r-value was generated.
OriginalArgument Original;
using PointerAccessInfo = SILGenFunction::PointerAccessInfo;
using ArrayAccessInfo = SILGenFunction::ArrayAccessInfo;
static int getUnionIndexForExtra(KindTy kind) {
switch (kind) {
case LValueToPointer:
return 0;
case LValueArrayToPointer:
case RValueArrayToPointer:
return 1;
default:
return -1;
}
}
ExternalUnion<KindTy, getUnionIndexForExtra,
PointerAccessInfo, ArrayAccessInfo> Extra;
public:
DelayedArgument(KindTy kind, LValue &&lv, SILLocation loc)
: Kind(kind) {
assert(kind <= LastLVKindWithoutExtra &&
"this constructor should only be used for simple l-value kinds");
Value.emplace<LValueStorage>(Kind, std::move(lv), loc);
}
DelayedArgument(KindTy kind, ManagedValue rv, OriginalArgument original)
: Kind(kind), Original(original) {
Value.emplace<RValueStorage>(Kind, rv);
}
DelayedArgument(SILGenFunction::PointerAccessInfo pointerInfo,
LValue &&lv, SILLocation loc, OriginalArgument original)
: Kind(LValueToPointer), Original(original) {
Value.emplace<LValueStorage>(Kind, std::move(lv), loc);
Extra.emplace<PointerAccessInfo>(Kind, pointerInfo);
}
DelayedArgument(SILGenFunction::ArrayAccessInfo arrayInfo,
LValue &&lv, SILLocation loc, OriginalArgument original)
: Kind(LValueArrayToPointer), Original(original) {
Value.emplace<LValueStorage>(Kind, std::move(lv), loc);
Extra.emplace<ArrayAccessInfo>(Kind, arrayInfo);
}
DelayedArgument(KindTy kind,
SILGenFunction::ArrayAccessInfo arrayInfo,
ManagedValue rv, OriginalArgument original)
: Kind(kind), Original(original) {
Value.emplace<RValueStorage>(Kind, rv);
Extra.emplace<ArrayAccessInfo>(Kind, arrayInfo);
}
DelayedArgument(DelayedArgument &&other)
: Kind(other.Kind), Original(other.Original) {
Value.moveConstruct(Kind, std::move(other.Value));
Extra.moveConstruct(Kind, std::move(other.Extra));
}
DelayedArgument &operator=(DelayedArgument &&other) {
Value.moveAssign(Kind, other.Kind, std::move(other.Value));
Extra.moveAssign(Kind, other.Kind, std::move(other.Extra));
Kind = other.Kind;
Original = other.Original;
return *this;
}
~DelayedArgument() {
Extra.destruct(Kind);
Value.destruct(Kind);
}
bool isSimpleInOut() const { return Kind == InOut; }
SILLocation getLocation() const { return Loc; }
SILLocation getInOutLocation() const {
assert(isSimpleInOut());
return LV().Loc;
}
ManagedValue emit(SILGenFunction &SGF) {
switch (Kind) {
@@ -1994,9 +2109,10 @@ public:
case BorrowIndirect:
return emitBorrowIndirect(SGF);
case LValueToPointer:
return emitLValueToPointer(SGF);
case ArrayToPointer:
return emitArrayToPointer(SGF);
case LValueArrayToPointer:
case RValueArrayToPointer:
case RValueStringToPointer:
return finishOriginalArgument(SGF);
}
llvm_unreachable("bad kind");
}
@@ -2012,62 +2128,171 @@ private:
ManagedValue emitBorrowDirect(SILGenFunction &SGF) {
ManagedValue address = emitAddress(SGF, AccessKind::Read);
return SGF.B.createLoadBorrow(Loc, address);
}
ManagedValue emitLValueToPointer(SILGenFunction &SGF) {
auto value = SGF.emitLValueToPointer(Loc, std::move(LV), PointerInfo);
return finishOriginalExpr(SGF, value, OriginalExpr);
}
ManagedValue emitArrayToPointer(SILGenFunction &SGF) {
auto value = SGF.emitArrayToPointer(Loc, std::move(LV), ArrayInfo);
return finishOriginalExpr(SGF, value, OriginalExpr);
return SGF.B.createLoadBorrow(LV().Loc, address);
}
ManagedValue emitAddress(SILGenFunction &SGF, AccessKind accessKind) {
auto tsanKind =
(accessKind == AccessKind::Read ? TSanKind::None : TSanKind::InoutAccess);
return SGF.emitAddressOfLValue(Loc, std::move(LV), accessKind, tsanKind);
return SGF.emitAddressOfLValue(LV().Loc, std::move(LV().LV),
accessKind, tsanKind);
}
ManagedValue finishOriginalExpr(SILGenFunction &SGF, ManagedValue innerValue,
Expr *expr) {
/// Replay the original argument expression.
ManagedValue finishOriginalArgument(SILGenFunction &SGF) {
auto results = finishOriginalExpr(SGF, Original.getExpr());
auto value = results.first; // just let the owner go
if (Original.isIndirect() && !value.getType().isAddress()) {
value = value.materialize(SGF, Original.getExpr());
}
return value;
}
// (value, owner)
std::pair<ManagedValue, ManagedValue>
finishOriginalExpr(SILGenFunction &SGF, Expr *expr) {
// This needs to handle all of the recursive cases from
// ArgEmission::maybeEmitAsAccess.
// ArgEmission::maybeEmitDelayed.
expr = expr->getSemanticsProvidingExpr();
// Handle injections into optionals.
if (auto inject = dyn_cast<InjectIntoOptionalExpr>(expr)) {
auto value = finishOriginalExpr(SGF, innerValue, inject->getSubExpr());
auto ownedValue =
finishOriginalExpr(SGF, inject->getSubExpr());
auto &optionalTL = SGF.getTypeLowering(expr->getType());
return SGF.emitInjectOptional(inject, optionalTL, SGFContext(),
[&](SGFContext ctx) { return value; });
auto optValue = SGF.emitInjectOptional(inject, optionalTL, SGFContext(),
[&](SGFContext ctx) { return ownedValue.first; });
return {optValue, ownedValue.second};
}
// Handle try!.
if (auto forceTry = dyn_cast<ForceTryExpr>(expr)) {
// Handle throws from the accessor? But what if the writeback throws?
SILGenFunction::ForceTryEmission emission(SGF, forceTry);
return finishOriginalExpr(SGF, innerValue, forceTry->getSubExpr());
return finishOriginalExpr(SGF, forceTry->getSubExpr());
}
// Handle optional evaluations.
if (auto optEval = dyn_cast<OptionalEvaluationExpr>(expr)) {
return finishOptionalEvaluation(SGF, optEval);
}
// Done with the recursive cases. Make sure we handled everything.
assert(isa<InOutToPointerExpr>(expr) ||
isa<ArrayToPointerExpr>(expr));
return innerValue;
isa<ArrayToPointerExpr>(expr) ||
isa<StringToPointerExpr>(expr));
switch (Kind) {
case InOut:
case BorrowDirect:
case BorrowIndirect:
llvm_unreachable("no original expr to finish in these cases");
case LValueToPointer:
return {SGF.emitLValueToPointer(LV().Loc, std::move(LV().LV),
Extra.get<PointerAccessInfo>(Kind)),
/*owner*/ ManagedValue()};
case LValueArrayToPointer:
return SGF.emitArrayToPointer(LV().Loc, std::move(LV().LV),
Extra.get<ArrayAccessInfo>(Kind));
case RValueArrayToPointer: {
auto pointerExpr = cast<ArrayToPointerExpr>(expr);
auto optArrayValue = RV().RV;
auto arrayValue = emitBindOptionals(SGF, optArrayValue,
pointerExpr->getSubExpr());
return SGF.emitArrayToPointer(pointerExpr, arrayValue,
Extra.get<ArrayAccessInfo>(Kind));
}
case RValueStringToPointer: {
auto pointerExpr = cast<StringToPointerExpr>(expr);
auto optStringValue = RV().RV;
auto stringValue =
emitBindOptionals(SGF, optStringValue, pointerExpr->getSubExpr());
return SGF.emitStringToPointer(pointerExpr, stringValue,
pointerExpr->getType());
}
}
llvm_unreachable("bad kind");
}
ManagedValue emitBindOptionals(SILGenFunction &SGF, ManagedValue optValue,
Expr *expr) {
expr = expr->getSemanticsProvidingExpr();
auto bind = dyn_cast<BindOptionalExpr>(expr);
// If we don't find a bind, the value isn't optional.
if (!bind) return optValue;
// Recurse.
optValue = emitBindOptionals(SGF, optValue, bind->getSubExpr());
// Check whether the value is non-nil.
SGF.emitBindOptional(bind, optValue, bind->getDepth());
// Extract the non-optional value.
auto &optTL = SGF.getTypeLowering(optValue.getType());
auto value = SGF.emitUncheckedGetOptionalValueFrom(bind, optValue, optTL);
return value;
}
std::pair<ManagedValue, ManagedValue>
finishOptionalEvaluation(SILGenFunction &SGF, OptionalEvaluationExpr *eval) {
SmallVector<ManagedValue, 2> results;
SGF.emitOptionalEvaluation(eval, eval->getType(), results, SGFContext(),
[&](SmallVectorImpl<ManagedValue> &results, SGFContext C) {
// Recurse.
auto values = finishOriginalExpr(SGF, eval->getSubExpr());
// Our primary result is the value.
results.push_back(values.first);
// Our secondary result is the owner, if we have one.
if (auto owner = values.second) results.push_back(owner);
});
assert(results.size() == 1 || results.size() == 2);
ManagedValue value = results[0];
ManagedValue owner;
if (results.size() == 2) {
owner = results[1];
// Create a new value-dependence here if the primary result is
// trivial.
auto &valueTL = SGF.getTypeLowering(value.getType());
if (valueTL.isTrivial()) {
SILValue dependentValue =
SGF.B.createMarkDependence(eval, value.forward(SGF),
owner.getValue());
value = SGF.emitManagedRValueWithCleanup(dependentValue, valueTL);
}
}
return {value, owner};
}
};
/// Begin all the formal accesses for a set of inout arguments.
static void beginInOutFormalAccesses(SILGenFunction &SGF,
MutableArrayRef<InOutArgument> inoutArgs,
} // end anonymous namespace
/// Perform the formal-access phase of call argument emission by emitting
/// all of the delayed arguments.
static void emitDelayedArguments(SILGenFunction &SGF,
MutableArrayRef<DelayedArgument> delayedArgs,
MutableArrayRef<SmallVector<ManagedValue, 4>> args) {
assert(!inoutArgs.empty());
assert(!delayedArgs.empty());
SmallVector<std::pair<SILValue, SILLocation>, 4> emittedInoutArgs;
auto inoutNext = inoutArgs.begin();
auto delayedNext = delayedArgs.begin();
// The assumption we make is that 'args' and 'inoutArgs' were built
// up in parallel, with empty spots being dropped into 'args'
@@ -2078,14 +2303,22 @@ static void beginInOutFormalAccesses(SILGenFunction &SGF,
for (ManagedValue &siteArg : siteArgs) {
if (siteArg) continue;
auto value = inoutNext->emit(SGF);
assert(delayedNext != delayedArgs.end());
auto &delayedArg = *delayedNext;
// Emit the delayed argument and replace it in the arguments array.
auto value = delayedArg.emit(SGF);
siteArg = value;
if (inoutNext->isSimpleInOut()) {
// Remember all the simple inouts we emitted so we can perform
// a basic inout-aliasing analysis.
// This should be completely obviated by static enforcement.
if (delayedArg.isSimpleInOut()) {
emittedInoutArgs.push_back({value.getValue(),
inoutNext->getLocation()});
delayedArg.getInOutLocation()});
}
if (++inoutNext == inoutArgs.end())
if (++delayedNext == delayedArgs.end())
goto done;
}
}
@@ -2111,29 +2344,6 @@ done:
}
}
/// Given a scalar value, materialize it into memory with the
/// exact same level of cleanup it had before.
static ManagedValue emitMaterializeIntoTemporary(SILGenFunction &SGF,
SILLocation loc,
ManagedValue object) {
auto temporary = SGF.emitTemporaryAllocation(loc, object.getType());
bool hadCleanup = object.hasCleanup();
// The temporary memory is +0 if the value was.
if (hadCleanup) {
SGF.B.emitStoreValueOperation(loc, object.forward(SGF), temporary,
StoreOwnershipQualifier::Init);
// SEMANTIC SIL TODO: This should really be called a temporary LValue.
return ManagedValue::forOwnedAddressRValue(temporary,
SGF.enterDestroyCleanup(temporary));
} else {
object = SGF.emitManagedBeginBorrow(loc, object.getValue());
SGF.emitManagedStoreBorrow(loc, object.getValue(), temporary);
return ManagedValue::forBorrowedAddressRValue(temporary);
}
}
namespace {
/// A destination for an argument other than just "onto to the end
@@ -2388,23 +2598,23 @@ class ArgEmitter {
ClaimedParamsRef ParamInfos;
SmallVectorImpl<ManagedValue> &Args;
/// Track any inout arguments that are emitted. Each corresponds
/// Track any delayed arguments that are emitted. Each corresponds
/// in order to a "hole" (a null value) in Args.
SmallVectorImpl<InOutArgument> &InOutArguments;
SmallVectorImpl<DelayedArgument> &DelayedArguments;
Optional<ArgSpecialDestArray> SpecialDests;
public:
ArgEmitter(SILGenFunction &SGF, SILFunctionTypeRepresentation Rep,
ClaimedParamsRef paramInfos,
SmallVectorImpl<ManagedValue> &args,
SmallVectorImpl<InOutArgument> &inoutArgs,
SmallVectorImpl<DelayedArgument> &delayedArgs,
const Optional<ForeignErrorConvention> &foreignError,
ImportAsMemberStatus foreignSelf,
Optional<ArgSpecialDestArray> specialDests = None)
: SGF(SGF), Rep(Rep), ForeignError(foreignError),
ForeignSelf(foreignSelf),
ParamInfos(paramInfos),
Args(args), InOutArguments(inoutArgs), SpecialDests(specialDests) {
Args(args), DelayedArguments(delayedArgs), SpecialDests(specialDests) {
assert(!specialDests || specialDests->size() == paramInfos.size());
}
@@ -2586,11 +2796,24 @@ private:
// If no abstraction is required, try to honor the emission contexts.
if (!contexts.RequiresReabstraction) {
auto loc = arg.getLocation();
result = std::move(arg).getAsSingleValue(SGF, contexts.ForEmission);
// Peephole certain argument emissions.
if (arg.isExpr()) {
auto expr = std::move(arg).asKnownExpr();
// Try the peepholes.
if (maybeEmitDelayed(expr, OriginalArgument(expr, /*indirect*/ true)))
return;
// Otherwise, just use the default logic.
result = SGF.emitRValueAsSingleValue(expr, contexts.ForEmission);
} else {
result = std::move(arg).getAsSingleValue(SGF, contexts.ForEmission);
}
// If it's not already in memory, put it there.
if (!result.getType().isAddress()) {
result = emitMaterializeIntoTemporary(SGF, loc, result);
result = result.materialize(SGF, loc);
}
// Otherwise, simultaneously emit and reabstract.
@@ -2652,7 +2875,7 @@ private:
// Leave an empty space in the ManagedValue sequence and
// remember that we had an inout argument.
InOutArguments.emplace_back(InOutArgument::InOut, std::move(lv), loc);
DelayedArguments.emplace_back(DelayedArgument::InOut, std::move(lv), loc);
Args.push_back(ManagedValue());
return;
}
@@ -2679,7 +2902,7 @@ private:
auto expr = std::move(arg).asKnownExpr();
// Try the peepholes.
if (maybeEmitAsAccess(expr, expr))
if (maybeEmitDelayed(expr, OriginalArgument(expr, /*indirect*/ false)))
return;
// Otherwise, just use the default logic.
@@ -2707,42 +2930,39 @@ private:
Args.push_back(value);
}
bool maybeEmitAsAccess(Expr *expr, Expr *originalExpr) {
bool maybeEmitDelayed(Expr *expr, OriginalArgument original) {
expr = expr->getSemanticsProvidingExpr();
// Delay accessing inout-to-pointer arguments until the call.
if (auto inoutToPointer = dyn_cast<InOutToPointerExpr>(expr)) {
auto info = SGF.getPointerAccessInfo(inoutToPointer->getType());
LValue lv = SGF.emitLValue(inoutToPointer->getSubExpr(), info.AccessKind);
InOutArguments.emplace_back(info, std::move(lv), inoutToPointer,
originalExpr);
Args.push_back(ManagedValue());
return true;
return emitDelayedConversion(inoutToPointer, original);
}
// Delay accessing array-to-pointer arguments until the call.
if (auto arrayToPointer = dyn_cast<ArrayToPointerExpr>(expr)) {
auto arrayExpr = arrayToPointer->getSubExpr();
if (auto inoutType = arrayExpr->getType()->getAs<InOutType>()) {
auto info = SGF.getArrayAccessInfo(arrayToPointer->getType(),
inoutType->getObjectType());
LValue lv = SGF.emitLValue(arrayExpr, info.AccessKind);
InOutArguments.emplace_back(info, std::move(lv), arrayToPointer,
originalExpr);
Args.push_back(ManagedValue());
return true;
}
return emitDelayedConversion(arrayToPointer, original);
}
// TODO: if the array comes from a storage reference, borrow it.
return false;
// Delay accessing string-to-pointer arguments until the call.
if (auto stringToPointer = dyn_cast<StringToPointerExpr>(expr)) {
return emitDelayedConversion(stringToPointer, original);
}
// Any recursive cases we handle here need to be handled in
// InOutArgument::finishOriginalExpr.
// DelayedArgument::finishOriginalExpr.
// Handle optional evaluations.
if (auto optional = dyn_cast<OptionalEvaluationExpr>(expr)) {
// The validity of just recursing here depends on the fact
// that we only return true for the specific conversions above,
// which are constrained by the ASTVerifier to only appear in
// specific forms.
return maybeEmitDelayed(optional->getSubExpr(), original);
}
// Handle injections into optionals.
if (auto inject = dyn_cast<InjectIntoOptionalExpr>(expr)) {
return maybeEmitAsAccess(inject->getSubExpr(), originalExpr);
return maybeEmitDelayed(inject->getSubExpr(), original);
}
// Handle try! expressions.
@@ -2750,12 +2970,71 @@ private:
// Any expressions in the l-value must be routed appropriately.
SILGenFunction::ForceTryEmission emission(SGF, forceTry);
return maybeEmitAsAccess(forceTry->getSubExpr(), originalExpr);
return maybeEmitDelayed(forceTry->getSubExpr(), original);
}
return false;
}
bool emitDelayedConversion(InOutToPointerExpr *pointerExpr,
OriginalArgument original) {
auto info = SGF.getPointerAccessInfo(pointerExpr->getType());
LValue lv = SGF.emitLValue(pointerExpr->getSubExpr(), info.AccessKind);
DelayedArguments.emplace_back(info, std::move(lv), pointerExpr, original);
Args.push_back(ManagedValue());
return true;
}
bool emitDelayedConversion(ArrayToPointerExpr *pointerExpr,
OriginalArgument original) {
auto arrayExpr = pointerExpr->getSubExpr();
// If the source of the conversion is an inout, emit the l-value
// but delay the formal access.
if (auto inoutType = arrayExpr->getType()->getAs<InOutType>()) {
auto info = SGF.getArrayAccessInfo(pointerExpr->getType(),
inoutType->getObjectType());
LValue lv = SGF.emitLValue(arrayExpr, info.AccessKind);
DelayedArguments.emplace_back(info, std::move(lv), pointerExpr,
original);
Args.push_back(ManagedValue());
return true;
}
// Otherwise, it's an r-value conversion.
auto info = SGF.getArrayAccessInfo(pointerExpr->getType(),
arrayExpr->getType());
auto rvalueExpr = lookThroughBindOptionals(arrayExpr);
ManagedValue value = SGF.emitRValueAsSingleValue(rvalueExpr);
DelayedArguments.emplace_back(DelayedArgument::RValueArrayToPointer,
info, value, original);
Args.push_back(ManagedValue());
return true;
}
/// Emit an rvalue-array-to-pointer conversion as a delayed argument.
bool emitDelayedConversion(StringToPointerExpr *pointerExpr,
OriginalArgument original) {
auto rvalueExpr = lookThroughBindOptionals(pointerExpr->getSubExpr());
ManagedValue value = SGF.emitRValueAsSingleValue(rvalueExpr);
DelayedArguments.emplace_back(DelayedArgument::RValueStringToPointer,
value, original);
Args.push_back(ManagedValue());
return true;
}
static Expr *lookThroughBindOptionals(Expr *expr) {
while (true) {
expr = expr->getSemanticsProvidingExpr();
if (auto bind = dyn_cast<BindOptionalExpr>(expr)) {
expr = bind->getSubExpr();
} else {
return expr;
}
}
}
ManagedValue emitSubstToOrigArgument(ArgumentSource &&arg,
SILType loweredSubstArgType,
AbstractionPattern origParamType,
@@ -3098,7 +3377,7 @@ struct ElementExtent {
ArrayRef<ManagedValue> Args;
/// The inout arguments which feed this tuple element.
/// This is set in the second pass.
MutableArrayRef<InOutArgument> InOutArgs;
MutableArrayRef<DelayedArgument> DelayedArgs;
ElementExtent()
: HasDestIndex(false)
@@ -3139,7 +3418,7 @@ class TupleShuffleArgEmitter {
// Used by flattenPatternFromInnerExtendIntoInnerParams and
// splitInnerArgumentsCorrectly.
SmallVector<ManagedValue, 8> innerArgs;
SmallVector<InOutArgument, 2> innerInOutArgs;
SmallVector<DelayedArgument, 2> innerDelayedArgs;
public:
TupleShuffleArgEmitter(TupleShuffleExpr *e, ArrayRef<TupleTypeElt> innerElts,
@@ -3302,7 +3581,7 @@ void TupleShuffleArgEmitter::flattenPatternFromInnerExtendIntoInnerParams(
void TupleShuffleArgEmitter::splitInnerArgumentsCorrectly(ArgEmitter &parent) {
ArrayRef<ManagedValue> nextArgs = innerArgs;
MutableArrayRef<InOutArgument> nextInOutArgs = innerInOutArgs;
MutableArrayRef<DelayedArgument> nextDelayedArgs = innerDelayedArgs;
for (auto &extent : innerExtents) {
auto length = extent.Params.size();
@@ -3311,18 +3590,18 @@ void TupleShuffleArgEmitter::splitInnerArgumentsCorrectly(ArgEmitter &parent) {
nextArgs = nextArgs.slice(length);
// Claim the correct number of inout arguments as well.
unsigned numInOut = 0;
size_t numDelayed = 0;
for (auto arg : extent.Args) {
assert(!arg.isInContext() || extent.HasDestIndex);
if (!arg)
numInOut++;
numDelayed++;
}
extent.InOutArgs = nextInOutArgs.slice(0, numInOut);
nextInOutArgs = nextInOutArgs.slice(numInOut);
extent.DelayedArgs = nextDelayedArgs.slice(0, numDelayed);
nextDelayedArgs = nextDelayedArgs.slice(numDelayed);
}
assert(nextArgs.empty() && "didn't claim all args");
assert(nextInOutArgs.empty() && "didn't claim all inout args");
assert(nextDelayedArgs.empty() && "didn't claim all inout args");
}
void TupleShuffleArgEmitter::emitDefaultArgsAndFinalize(ArgEmitter &parent) {
@@ -3343,8 +3622,8 @@ void TupleShuffleArgEmitter::emitDefaultArgsAndFinalize(ArgEmitter &parent) {
// Move the appropriate inner arguments over as outer arguments.
parent.Args.append(extent.Args.begin(), extent.Args.end());
for (auto &inoutArg : extent.InOutArgs)
parent.InOutArguments.push_back(std::move(inoutArg));
for (auto &delayedArg : extent.DelayedArgs)
parent.DelayedArguments.push_back(std::move(delayedArg));
continue;
}
@@ -3428,7 +3707,7 @@ void TupleShuffleArgEmitter::emit(ArgEmitter &parent) {
// Emit the inner expression.
if (!innerParams.empty()) {
ArgEmitter(parent.SGF, parent.Rep, ClaimedParamsRef(innerParams), innerArgs,
innerInOutArgs,
innerDelayedArgs,
/*foreign error*/ None, /*foreign self*/ ImportAsMemberStatus(),
(innerSpecialDests ? ArgSpecialDestArray(*innerSpecialDests)
: Optional<ArgSpecialDestArray>()))
@@ -3741,13 +4020,13 @@ namespace {
/// Evaluate arguments and begin any inout formal accesses.
void emit(SILGenFunction &SGF, AbstractionPattern origParamType,
ParamLowering &lowering, SmallVectorImpl<ManagedValue> &args,
SmallVectorImpl<InOutArgument> &inoutArgs,
SmallVectorImpl<DelayedArgument> &delayedArgs,
const Optional<ForeignErrorConvention> &foreignError,
const ImportAsMemberStatus &foreignSelf) && {
auto params = lowering.claimParams(origParamType, getSubstArgType(),
foreignError, foreignSelf);
ArgEmitter emitter(SGF, lowering.Rep, params, args, inoutArgs,
ArgEmitter emitter(SGF, lowering.Rep, params, args, delayedArgs,
foreignError, foreignSelf);
emitter.emitTopLevel(std::move(ArgValue), origParamType);
}
@@ -4328,7 +4607,7 @@ void CallEmission::emitArgumentsForNormalApply(
SmallVectorImpl<ManagedValue> &uncurriedArgs,
Optional<SILLocation> &uncurriedLoc, CanFunctionType &formalApplyType) {
SmallVector<SmallVector<ManagedValue, 4>, 2> args;
SmallVector<InOutArgument, 2> inoutArgs;
SmallVector<DelayedArgument, 2> delayedArgs;
auto expectedUncurriedOrigFormalType =
getUncurriedOrigFormalType(origFormalType);
(void)expectedUncurriedOrigFormalType;
@@ -4368,7 +4647,7 @@ void CallEmission::emitArgumentsForNormalApply(
bool isParamSite = &site == &uncurriedSites.back();
std::move(site).emit(SGF, origParamType, paramLowering, args.back(),
inoutArgs,
delayedArgs,
// Claim the foreign error with the method
// formal params.
isParamSite ? foreignError : None,
@@ -4383,9 +4662,10 @@ void CallEmission::emitArgumentsForNormalApply(
expectedUncurriedOrigFormalType.getType() &&
"getUncurriedOrigFormalType and emitArgumentsForNormalCall are out of "
"sync");
// Begin the formal accesses to any inout arguments we have.
if (!inoutArgs.empty()) {
beginInOutFormalAccesses(SGF, inoutArgs, args);
// Emit any delayed arguments: formal accesses to inout arguments, etc.
if (!delayedArgs.empty()) {
emitDelayedArguments(SGF, delayedArgs, args);
}
// Uncurry the arguments in calling convention order.
@@ -4419,7 +4699,7 @@ RValue CallEmission::applyRemainingCallSites(
ParamLowering paramLowering(substFnType, SGF);
SmallVector<ManagedValue, 4> siteArgs;
SmallVector<InOutArgument, 2> inoutArgs;
SmallVector<DelayedArgument, 2> delayedArgs;
// TODO: foreign errors for block or function pointer values?
assert(substFnType->hasErrorResult() ||
@@ -4443,10 +4723,10 @@ RValue CallEmission::applyRemainingCallSites(
ArgumentScope argScope(SGF, loc);
std::move(extraSites[i])
.emit(SGF, origParamType, paramLowering, siteArgs, inoutArgs,
.emit(SGF, origParamType, paramLowering, siteArgs, delayedArgs,
foreignError, foreignSelf);
if (!inoutArgs.empty()) {
beginInOutFormalAccesses(SGF, inoutArgs, siteArgs);
if (!delayedArgs.empty()) {
emitDelayedArguments(SGF, delayedArgs, siteArgs);
}
ApplyOptions options = ApplyOptions::None;
@@ -5030,7 +5310,7 @@ ArgumentSource AccessorBaseArgPreparer::prepareAccessorObjectBaseArg() {
isNonClassProtocolMember(accessor.getDecl()))) &&
"passing unmaterialized r-value as inout argument");
base = emitMaterializeIntoTemporary(SGF, loc, base);
base = base.materialize(SGF, loc);
if (selfParam.isIndirectInOut()) {
// Drop the cleanup if we have one.
auto baseLV = ManagedValue::forLValue(base.getValue());

View File

@@ -3701,11 +3701,10 @@ namespace {
/// emitOptimizedOptionalEvaluation - Look for cases where we can short-circuit
/// evaluation of an OptionalEvaluationExpr by pattern matching the AST.
///
static bool emitOptimizedOptionalEvaluation(OptionalEvaluationExpr *E,
SILValue &LoadableResult,
Initialization *optInit,
RValueEmitter &RVE) {
auto &SGF = RVE.SGF;
static bool emitOptimizedOptionalEvaluation(SILGenFunction &SGF,
OptionalEvaluationExpr *E,
ManagedValue &result,
SGFContext ctx) {
// It is a common occurrence to get conversions back and forth from T! to T?.
// Peephole these by looking for a subexpression that is a BindOptionalExpr.
// If we see one, we can produce a single instruction, which doesn't require
@@ -3727,16 +3726,41 @@ static bool emitOptimizedOptionalEvaluation(OptionalEvaluationExpr *E,
// SIL defines away abstraction differences between T? and T!,
// so we can just emit the sub-initialization normally.
if (optInit)
SGF.emitExprInto(BO->getSubExpr(), optInit);
else
LoadableResult = SGF.emitRValueAsSingleValue(BO->getSubExpr()).forward(SGF);
result = SGF.emitRValueAsSingleValue(BO->getSubExpr(), ctx);
return true;
}
RValue RValueEmitter::visitOptionalEvaluationExpr(OptionalEvaluationExpr *E,
SGFContext C) {
auto &optTL = SGF.getTypeLowering(E->getType());
SmallVector<ManagedValue, 1> results;
SGF.emitOptionalEvaluation(E, E->getType(), results, C,
[&](SmallVectorImpl<ManagedValue> &results, SGFContext primaryC) {
ManagedValue result;
if (!emitOptimizedOptionalEvaluation(SGF, E, result, primaryC)) {
result = SGF.emitRValueAsSingleValue(E->getSubExpr(), primaryC);
}
assert(results.empty());
results.push_back(result);
});
assert(results.size() == 1);
if (results[0].isInContext()) {
return RValue();
} else {
return RValue(SGF, E, results[0]);
}
}
void SILGenFunction::emitOptionalEvaluation(SILLocation loc, Type optType,
SmallVectorImpl<ManagedValue> &results,
SGFContext C,
llvm::function_ref<void(SmallVectorImpl<ManagedValue> &,
SGFContext primaryC)>
generateNormalResults) {
assert(results.empty());
auto &optTL = getTypeLowering(optType);
Initialization *optInit = C.getEmitInto();
bool usingProvidedContext =
@@ -3745,7 +3769,7 @@ RValue RValueEmitter::visitOptionalEvaluationExpr(OptionalEvaluationExpr *E,
// Form the optional using address operations if the type is address-only or
// if we already have an address to use.
bool isByAddress = ((usingProvidedContext || optTL.isAddressOnly()) &&
SGF.silConv.useLoweredAddresses());
silConv.useLoweredAddresses());
std::unique_ptr<TemporaryInitialization> optTemp;
if (!isByAddress) {
@@ -3756,7 +3780,7 @@ RValue RValueEmitter::visitOptionalEvaluationExpr(OptionalEvaluationExpr *E,
// Allocate the temporary for the Optional<T> if we didn't get one from the
// context. This needs to happen outside of the cleanups scope we're about
// to push.
optTemp = SGF.emitTemporary(E, optTL);
optTemp = emitTemporary(loc, optTL);
optInit = optTemp.get();
}
assert(isByAddress == (optInit != nullptr));
@@ -3764,46 +3788,69 @@ RValue RValueEmitter::visitOptionalEvaluationExpr(OptionalEvaluationExpr *E,
// Acquire the address to emit into outside of the cleanups scope.
SILValue optAddr;
if (isByAddress)
optAddr = optInit->getAddressForInPlaceInitialization(SGF, E);
optAddr = optInit->getAddressForInPlaceInitialization(*this, loc);
// Enter a cleanups scope.
FullExpr scope(SGF.Cleanups, E);
FullExpr scope(Cleanups, CleanupLocation::get(loc));
// Inside of the cleanups scope, create a new initialization to
// emit into optAddr.
std::unique_ptr<TemporaryInitialization> normalInit;
if (isByAddress) {
normalInit = SGF.useBufferAsTemporary(optAddr, optTL);
normalInit = useBufferAsTemporary(optAddr, optTL);
}
// Install a new optional-failure destination just outside of the
// cleanups scope.
SILBasicBlock *failureBB = SGF.createBasicBlock();
RestoreOptionalFailureDest restoreFailureDest(SGF,
JumpDest(failureBB, SGF.Cleanups.getCleanupsDepth(), E));
SILBasicBlock *failureBB = createBasicBlock();
RestoreOptionalFailureDest
restoreFailureDest(*this, JumpDest(failureBB, Cleanups.getCleanupsDepth(),
CleanupLocation::get(loc)));
SILValue normalArgument = nullptr;
if (emitOptimizedOptionalEvaluation(E, normalArgument,
normalInit.get(), *this)) {
// Already emitted code for this.
} else if (normalInit) {
// Emit the operand into the temporary.
SGF.emitExprInto(E->getSubExpr(), normalInit.get());
} else {
normalArgument = SGF.emitRValueAsSingleValue(E->getSubExpr()).forward(SGF);
generateNormalResults(results, SGFContext(normalInit.get()));
assert(results.size() >= 1 && "didn't include a normal result");
assert(results[0].isInContext() ||
results[0].getType().getObjectType()
== optTL.getLoweredType().getObjectType());
// If we're emitting into the context, make sure the normal value is there.
if (normalInit && !results[0].isInContext()) {
normalInit->copyOrInitValueInto(*this, loc, results[0], /*init*/ true);
normalInit->finishInitialization(*this);
results[0] = ManagedValue::forInContext();
}
assert(isByAddress == (normalArgument == nullptr) &&
"shouldn't have emitted as scalar in this case");
// We fell out of the normal result, which generated a T? as either
// a scalar in normalArgument or directly into normalInit.
// If we're using by-address initialization, we must've emitted into
// normalInit. Forward its cleanup before popping the scope.
if (isByAddress) {
normalInit->getManagedAddress().forward(SGF);
normalInit->getManagedAddress().forward(*this);
normalInit.reset(); // Make sure we don't use this anymore.
} else {
assert(!results[0].isInContext());
results[0].forward(*this);
}
// For all the secondary results, forward their cleanups and make sure
// they're of optional type so that we can inject nil into them in
// the failure path.
// (Should this be controllable by the client?)
for (auto &result : MutableArrayRef<ManagedValue>(results).slice(1)) {
assert(!result.isInContext() && "secondary result was in context");
auto resultTy = result.getType();
assert(resultTy.isObject() && "secondary result wasn't an object");
// Forward the cleanup.
SILValue value = result.forward(*this);
// If it's not already an optional type, make it optional.
if (!resultTy.getAnyOptionalObjectType()) {
resultTy = SILType::getOptionalType(resultTy);
value = B.createOptionalSome(loc, value, resultTy);
result = ManagedValue::forUnmanaged(value);
}
}
// This concludes the conditional scope.
@@ -3817,67 +3864,95 @@ RValue RValueEmitter::visitOptionalEvaluationExpr(OptionalEvaluationExpr *E,
// Remove the dead failureBB.
failureBB->eraseFromParent();
// Just re-manage normalArgument if we're not using address-based IRGen.
if (!isByAddress)
return RValue(SGF, E,
SGF.emitManagedRValueWithCleanup(normalArgument, optTL));
// Just re-manage all the secondary results.
for (auto &result : MutableArrayRef<ManagedValue>(results).slice(1)) {
result = emitManagedRValueWithCleanup(result.getValue());
}
// Just re-manage the main result if we're not using address-based IRGen.
if (!isByAddress) {
results[0] = emitManagedRValueWithCleanup(results[0].getValue(), optTL);
return;
}
// Otherwise, we must have emitted into normalInit, which means that,
// now that we're out of the cleanups scope, we need to finish optInit.
optInit->finishInitialization(SGF);
assert(results[0].isInContext());
optInit->finishInitialization(*this);
// If optInit came from the SGFContext, then we've successfully emitted
// into that.
if (usingProvidedContext)
return RValue();
if (usingProvidedContext) return;
// Otherwise, we must have emitted into optTemp.
return RValue(SGF, E, optTemp->getManagedAddress());
assert(optTemp);
results[0] = optTemp->getManagedAddress();
return;
}
// Okay, we do have uses of the failure block, so we'll need to merge
// control paths.
SILBasicBlock *contBB = SGF.createBasicBlock();
SILBasicBlock *contBB = createBasicBlock();
// Branch to the continuation block.
if (isByAddress)
SGF.B.createBranch(E, contBB);
else
SGF.B.createBranch(E, contBB, normalArgument);
SmallVector<SILValue, 4> bbArgs;
if (!isByAddress)
bbArgs.push_back(results[0].getValue());
for (const auto &result : llvm::makeArrayRef(results).slice(1))
bbArgs.push_back(result.getValue());
// Branch to the continuation block.
B.createBranch(loc, contBB, bbArgs);
// In the failure block, inject nil into the result.
SGF.B.emitBlock(failureBB);
B.emitBlock(failureBB);
// Note that none of the code here introduces any cleanups.
// If it did, we'd need to push a scope.
bbArgs.clear();
if (isByAddress) {
SGF.emitInjectOptionalNothingInto(E, optAddr, optTL);
SGF.B.createBranch(E, contBB);
emitInjectOptionalNothingInto(loc, optAddr, optTL);
} else {
auto branchArg = SGF.getOptionalNoneValue(E, optTL);
SGF.B.createBranch(E, contBB, branchArg);
bbArgs.push_back(getOptionalNoneValue(loc, optTL));
}
for (const auto &result : llvm::makeArrayRef(results).slice(1)) {
auto resultTy = result.getType();
bbArgs.push_back(getOptionalNoneValue(loc, getTypeLowering(resultTy)));
}
B.createBranch(loc, contBB, bbArgs);
// Emit the continuation block.
SGF.B.emitBlock(contBB);
B.emitBlock(contBB);
// If this was done in SSA registers, then the value is provided as an
// argument to the block; manage it and return.
if (!isByAddress) {
// Create a PHI for the optional result if desired.
if (isByAddress) {
assert(results[0].isInContext());
} else {
auto arg = contBB->createPHIArgument(optTL.getLoweredType(),
ValueOwnershipKind::Owned);
return RValue(SGF, E, SGF.emitManagedRValueWithCleanup(arg, optTL));
results[0] = emitManagedRValueWithCleanup(arg, optTL);
}
// Otherwise, we need to manage the value in optInit.
optInit->finishInitialization(SGF);
// If we emitted into the provided context, we're done.
if (usingProvidedContext) {
return RValue();
// Create PHIs for all the secondary results and manage them.
for (auto &result : MutableArrayRef<ManagedValue>(results).slice(1)) {
auto arg = contBB->createPHIArgument(result.getType(),
ValueOwnershipKind::Owned);
result = emitManagedRValueWithCleanup(arg);
}
// We may need to manage the value in optInit.
if (!isByAddress) return;
assert(results[0].isInContext());
optInit->finishInitialization(*this);
// If we didn't emit into the provided context, the primary result
// is really a temporary.
if (usingProvidedContext) return;
assert(optTemp);
return RValue(SGF, E, optTemp->getManagedAddress());
results[0] = optTemp->getManagedAddress();
}
RValue RValueEmitter::visitForceValueExpr(ForceValueExpr *E, SGFContext C) {
@@ -4213,10 +4288,9 @@ ManagedValue SILGenFunction::emitLValueToPointer(SILLocation loc, LValue &&lv,
.getAsSingleValue(*this, loc);
}
static ManagedValue emitArrayToPointer(SILGenFunction &SGF,
SILLocation loc,
ManagedValue array,
SILGenFunction::ArrayAccessInfo accessInfo) {
static std::pair<ManagedValue, ManagedValue>
emitArrayToPointer(SILGenFunction &SGF, SILLocation loc, ManagedValue array,
SILGenFunction::ArrayAccessInfo accessInfo) {
auto &ctx = SGF.getASTContext();
FuncDecl *converter;
@@ -4251,8 +4325,13 @@ static ManagedValue emitArrayToPointer(SILGenFunction &SGF,
.getAll(resultScalars);
assert(resultScalars.size() == 2);
// Mark the dependence of the pointer on the owner value.
auto owner = resultScalars[0];
auto pointer = resultScalars[1].forward(SGF);
pointer = SGF.B.createMarkDependence(loc, pointer, owner.getValue());
// The owner's already in its own cleanup. Return the pointer.
return resultScalars[1];
return {ManagedValue::forTrivialObjectRValue(pointer), owner};
}
RValue RValueEmitter::visitArrayToPointerExpr(ArrayToPointerExpr *E,
@@ -4273,7 +4352,7 @@ RValue RValueEmitter::visitArrayToPointerExpr(ArrayToPointerExpr *E,
array = SGF.emitRValueAsSingleValue(subExpr);
}
auto pointer = ::emitArrayToPointer(SGF, E, array, accessInfo);
auto pointer = ::emitArrayToPointer(SGF, E, array, accessInfo).first;
return RValue(SGF, E, pointer);
}
@@ -4283,34 +4362,54 @@ SILGenFunction::getArrayAccessInfo(Type pointerType, Type arrayType) {
return { pointerType, arrayType, pointerAccessInfo.AccessKind };
}
ManagedValue SILGenFunction::emitArrayToPointer(SILLocation loc, LValue &&lv,
ArrayAccessInfo accessInfo) {
std::pair<ManagedValue, ManagedValue>
SILGenFunction::emitArrayToPointer(SILLocation loc, LValue &&lv,
ArrayAccessInfo accessInfo) {
auto array =
emitAddressOfLValue(loc, std::move(lv), accessInfo.AccessKind);
return ::emitArrayToPointer(*this, loc, array, accessInfo);
}
std::pair<ManagedValue, ManagedValue>
SILGenFunction::emitArrayToPointer(SILLocation loc, ManagedValue array,
ArrayAccessInfo accessInfo) {
return ::emitArrayToPointer(*this, loc, array, accessInfo);
}
RValue RValueEmitter::visitStringToPointerExpr(StringToPointerExpr *E,
SGFContext C) {
auto &Ctx = SGF.getASTContext();
FuncDecl *converter = Ctx.getConvertConstStringToUTF8PointerArgument(nullptr);
// Get the original value.
ManagedValue orig = SGF.emitRValueAsSingleValue(E->getSubExpr());
// Perform the conversion.
auto results = SGF.emitStringToPointer(E, orig, E->getType());
// Implicitly leave the owner managed and return the pointer.
return RValue(SGF, E, results.first);
}
std::pair<ManagedValue, ManagedValue>
SILGenFunction::emitStringToPointer(SILLocation loc, ManagedValue stringValue,
Type pointerType) {
auto &Ctx = getASTContext();
FuncDecl *converter = Ctx.getConvertConstStringToUTF8PointerArgument(nullptr);
// Invoke the conversion intrinsic, which will produce an owner-pointer pair.
auto subMap = E->getType()->getCanonicalType()
->getContextSubstitutionMap(SGF.SGM.M.getSwiftModule(),
SGF.getPointerProtocol());
auto subMap = pointerType->getContextSubstitutionMap(SGM.M.getSwiftModule(),
getPointerProtocol());
SmallVector<ManagedValue, 2> results;
SGF.emitApplyOfLibraryIntrinsic(E, converter, subMap, orig, C).getAll(results);
emitApplyOfLibraryIntrinsic(loc, converter, subMap, stringValue, SGFContext())
.getAll(results);
assert(results.size() == 2);
// Implicitly leave the owner managed and return the pointer.
// FIXME: should this be using mark_dependence?
auto pointer = results[1];
return RValue(SGF, E, pointer);
// Mark the dependence of the pointer on the owner value.
auto owner = results[0];
auto pointer = results[1].forward(*this);
pointer = B.createMarkDependence(loc, pointer, owner.getValue());
return {ManagedValue::forTrivialObjectRValue(pointer), owner};
}
RValue RValueEmitter::visitPointerToPointerExpr(PointerToPointerExpr *E,
SGFContext C) {
auto &Ctx = SGF.getASTContext();

View File

@@ -1512,6 +1512,13 @@ public:
void emitBindOptional(SILLocation loc, ManagedValue optionalAddrOrValue,
unsigned depth);
void emitOptionalEvaluation(SILLocation loc, Type optionalType,
SmallVectorImpl<ManagedValue> &results,
SGFContext C,
llvm::function_ref<void(SmallVectorImpl<ManagedValue> &,
SGFContext primaryC)>
generateNormalResults);
//===--------------------------------------------------------------------===//
// Bridging thunks
//===--------------------------------------------------------------------===//
@@ -1751,8 +1758,17 @@ public:
swift::AccessKind AccessKind;
};
ArrayAccessInfo getArrayAccessInfo(Type pointerType, Type arrayType);
ManagedValue emitArrayToPointer(SILLocation loc, LValue &&lvalue,
ArrayAccessInfo accessInfo);
std::pair<ManagedValue,ManagedValue>
emitArrayToPointer(SILLocation loc, LValue &&lvalue,
ArrayAccessInfo accessInfo);
std::pair<ManagedValue,ManagedValue>
emitArrayToPointer(SILLocation loc, ManagedValue arrayValue,
ArrayAccessInfo accessInfo);
std::pair<ManagedValue,ManagedValue>
emitStringToPointer(SILLocation loc, ManagedValue stringValue,
Type pointerType);
class ForceTryEmission {
SILGenFunction &SGF;

View File

@@ -5101,6 +5101,8 @@ Expr *ExprRewriter::coerceOptionalToOptional(Expr *expr, Type toType,
Type fromValueType = fromType->getAnyOptionalObjectType();
Type toValueType = toType->getAnyOptionalObjectType();
// FIXME: this depth is wrong, it needs to be the depth of
// OptionalEvaluationExprs introduced by the call to coerceToType below.
expr =
cs.cacheType(new (tc.Context) BindOptionalExpr(expr,
expr->getSourceRange().End,

View File

@@ -9,12 +9,16 @@ func sideEffect1() -> Int { return 1 }
func sideEffect2() -> Int { return 2 }
func takesMutablePointer(_ x: UnsafeMutablePointer<Int>) {}
func takesConstPointer(_ x: UnsafePointer<Int>) {}
func takesOptConstPointer(_ x: UnsafePointer<Int>?, and: Int) {}
func takesOptOptConstPointer(_ x: UnsafePointer<Int>??, and: Int) {}
func takesMutablePointer(_ x: UnsafeMutablePointer<Int>, and: Int) {}
func takesConstPointer(_ x: UnsafePointer<Int>, and: Int) {}
func takesMutableVoidPointer(_ x: UnsafeMutableRawPointer) {}
func takesConstVoidPointer(_ x: UnsafeRawPointer) {}
func takesMutableRawPointer(_ x: UnsafeMutableRawPointer) {}
func takesConstRawPointer(_ x: UnsafeRawPointer) {}
func takesOptConstRawPointer(_ x: UnsafeRawPointer?, and: Int) {}
func takesOptOptConstRawPointer(_ x: UnsafeRawPointer??, and: Int) {}
// CHECK-LABEL: sil hidden @_T018pointer_conversion0A9ToPointerySpySiG_SPySiGSvtF
// CHECK: bb0([[MP:%.*]] : $UnsafeMutablePointer<Int>, [[CP:%.*]] : $UnsafePointer<Int>, [[MRP:%.*]] : $UnsafeMutableRawPointer):
@@ -88,7 +92,8 @@ func arrayToPointer() {
// CHECK: [[CONVERT_MUTABLE:%.*]] = function_ref @_T0s37_convertMutableArrayToPointerArgument{{[_0-9a-zA-Z]*}}F
// CHECK: [[OWNER:%.*]] = apply [[CONVERT_MUTABLE]]<Int, UnsafeMutablePointer<Int>>([[POINTER_BUF:%[0-9]*]],
// CHECK: [[POINTER:%.*]] = load [trivial] [[POINTER_BUF]]
// CHECK: apply [[TAKES_MUTABLE_POINTER]]([[POINTER]])
// CHECK: [[DEPENDENT:%.*]] = mark_dependence [[POINTER]] : $UnsafeMutablePointer<Int> on [[OWNER]]
// CHECK: apply [[TAKES_MUTABLE_POINTER]]([[DEPENDENT]])
// CHECK: destroy_value [[OWNER]]
takesConstPointer(ints)
@@ -96,7 +101,8 @@ func arrayToPointer() {
// CHECK: [[CONVERT_CONST:%.*]] = function_ref @_T0s35_convertConstArrayToPointerArgument{{[_0-9a-zA-Z]*}}F
// CHECK: [[OWNER:%.*]] = apply [[CONVERT_CONST]]<Int, UnsafePointer<Int>>([[POINTER_BUF:%[0-9]*]],
// CHECK: [[POINTER:%.*]] = load [trivial] [[POINTER_BUF]]
// CHECK: apply [[TAKES_CONST_POINTER]]([[POINTER]])
// CHECK: [[DEPENDENT:%.*]] = mark_dependence [[POINTER]] : $UnsafePointer<Int> on [[OWNER]]
// CHECK: apply [[TAKES_CONST_POINTER]]([[DEPENDENT]])
// CHECK: destroy_value [[OWNER]]
takesMutableRawPointer(&ints)
@@ -104,7 +110,8 @@ func arrayToPointer() {
// CHECK: [[CONVERT_MUTABLE:%.*]] = function_ref @_T0s37_convertMutableArrayToPointerArgument{{[_0-9a-zA-Z]*}}F
// CHECK: [[OWNER:%.*]] = apply [[CONVERT_MUTABLE]]<Int, UnsafeMutableRawPointer>([[POINTER_BUF:%[0-9]*]],
// CHECK: [[POINTER:%.*]] = load [trivial] [[POINTER_BUF]]
// CHECK: apply [[TAKES_MUTABLE_RAW_POINTER]]([[POINTER]])
// CHECK: [[DEPENDENT:%.*]] = mark_dependence [[POINTER]] : $UnsafeMutableRawPointer on [[OWNER]]
// CHECK: apply [[TAKES_MUTABLE_RAW_POINTER]]([[DEPENDENT]])
// CHECK: destroy_value [[OWNER]]
takesConstRawPointer(ints)
@@ -112,7 +119,8 @@ func arrayToPointer() {
// CHECK: [[CONVERT_CONST:%.*]] = function_ref @_T0s35_convertConstArrayToPointerArgument{{[_0-9a-zA-Z]*}}F
// CHECK: [[OWNER:%.*]] = apply [[CONVERT_CONST]]<Int, UnsafeRawPointer>([[POINTER_BUF:%[0-9]*]],
// CHECK: [[POINTER:%.*]] = load [trivial] [[POINTER_BUF]]
// CHECK: apply [[TAKES_CONST_RAW_POINTER]]([[POINTER]])
// CHECK: [[DEPENDENT:%.*]] = mark_dependence [[POINTER]] : $UnsafeRawPointer on [[OWNER]]
// CHECK: apply [[TAKES_CONST_RAW_POINTER]]([[DEPENDENT]])
// CHECK: destroy_value [[OWNER]]
}
@@ -123,7 +131,8 @@ func stringToPointer(_ s: String) {
// CHECK: [[CONVERT_STRING:%.*]] = function_ref @_T0s40_convertConstStringToUTF8PointerArgument{{[_0-9a-zA-Z]*}}F
// CHECK: [[OWNER:%.*]] = apply [[CONVERT_STRING]]<UnsafeRawPointer>([[POINTER_BUF:%[0-9]*]],
// CHECK: [[POINTER:%.*]] = load [trivial] [[POINTER_BUF]]
// CHECK: apply [[TAKES_CONST_VOID_POINTER]]([[POINTER]])
// CHECK: [[DEPENDENT:%.*]] = mark_dependence [[POINTER]] : $UnsafeRawPointer on [[OWNER]]
// CHECK: apply [[TAKES_CONST_VOID_POINTER]]([[DEPENDENT]])
// CHECK: destroy_value [[OWNER]]
takesConstRawPointer(s)
@@ -131,7 +140,8 @@ func stringToPointer(_ s: String) {
// CHECK: [[CONVERT_STRING:%.*]] = function_ref @_T0s40_convertConstStringToUTF8PointerArgument{{[_0-9a-zA-Z]*}}F
// CHECK: [[OWNER:%.*]] = apply [[CONVERT_STRING]]<UnsafeRawPointer>([[POINTER_BUF:%[0-9]*]],
// CHECK: [[POINTER:%.*]] = load [trivial] [[POINTER_BUF]]
// CHECK: apply [[TAKES_CONST_RAW_POINTER]]([[POINTER]])
// CHECK: [[DEPENDENT:%.*]] = mark_dependence [[POINTER]] : $UnsafeRawPointer on [[OWNER]]
// CHECK: apply [[TAKES_CONST_RAW_POINTER]]([[DEPENDENT]])
// CHECK: destroy_value [[OWNER]]
}
@@ -267,3 +277,152 @@ func inoutPointerOrdering() {
// CHECK: end_access [[ACCESS]]
takesConstPointer(&array[sideEffect1()], and: sideEffect2())
}
// rdar://problem/31542269
// CHECK-LABEL: sil hidden @_T018pointer_conversion20optArrayToOptPointerySaySiGSg5array_tF
func optArrayToOptPointer(array: [Int]?) {
// CHECK: [[TAKES:%.*]] = function_ref @_T018pointer_conversion20takesOptConstPointerySPySiGSg_Si3andtF
// CHECK: [[BORROW:%.*]] = begin_borrow %0
// CHECK: [[COPY:%.*]] = copy_value [[BORROW]]
// CHECK: [[SIDE1:%.*]] = function_ref @_T018pointer_conversion11sideEffect1SiyF
// CHECK: [[RESULT1:%.*]] = apply [[SIDE1]]()
// CHECK: [[T0:%.*]] = select_enum [[COPY]]
// CHECK: cond_br [[T0]], [[SOME_BB:bb[0-9]+]], [[NONE_BB:bb[0-9]+]]
// CHECK: [[SOME_BB]]:
// CHECK: [[SOME_VALUE:%.*]] = unchecked_enum_data [[COPY]]
// CHECK: [[CONVERT:%.*]] = function_ref @_T0s35_convertConstArrayToPointerArguments9AnyObject_pSg_q_tSayxGs01_E0R_r0_lF
// CHECK: [[TEMP:%.*]] = alloc_stack $UnsafePointer<Int>
// CHECK: [[OWNER:%.*]] = apply [[CONVERT]]<Int, UnsafePointer<Int>>([[TEMP:%.*]], [[SOME_VALUE]])
// CHECK: [[PTR:%.*]] = load [trivial] [[TEMP]]
// CHECK: [[DEP:%.*]] = mark_dependence [[PTR]] : $UnsafePointer<Int> on [[OWNER]]
// CHECK: [[OPTPTR:%.*]] = enum $Optional<UnsafePointer<Int>>, #Optional.some!enumelt.1, [[DEP]]
// CHECK: dealloc_stack [[TEMP]]
// CHECK: br [[CONT_BB:bb[0-9]+]]([[OPTPTR]] : $Optional<UnsafePointer<Int>>, [[OWNER]] : $Optional<AnyObject>)
// CHECK: [[CONT_BB]]([[OPTPTR:%.*]] : $Optional<UnsafePointer<Int>>, [[OWNER:%.*]] : $Optional<AnyObject>):
// CHECK: [[OPTDEP:%.*]] = mark_dependence [[OPTPTR]] : $Optional<UnsafePointer<Int>> on [[OWNER]]
// CHECK: apply [[TAKES]]([[OPTDEP]], [[RESULT1]])
// CHECK: destroy_value [[OWNER]]
// CHECK: end_borrow [[BORROW]]
// CHECK: destroy_value %0
// CHECK: [[NONE_BB]]:
// CHECK: [[NO_VALUE:%.*]] = enum $Optional<UnsafePointer<Int>>, #Optional.none
// CHECK: [[NO_OWNER:%.*]] = enum $Optional<AnyObject>, #Optional.none
// CHECK: br [[CONT_BB]]([[NO_VALUE]] : $Optional<UnsafePointer<Int>>, [[NO_OWNER]] : $Optional<AnyObject>)
takesOptConstPointer(array, and: sideEffect1())
}
// CHECK-LABEL: sil hidden @_T018pointer_conversion013optOptArrayTodD7PointerySaySiGSgSg5array_tF
func optOptArrayToOptOptPointer(array: [Int]??) {
// CHECK: [[TAKES:%.*]] = function_ref @_T018pointer_conversion08takesOptD12ConstPointerySPySiGSgSg_Si3andtF
// CHECK: [[BORROW:%.*]] = begin_borrow %0
// CHECK: [[COPY:%.*]] = copy_value [[BORROW]]
// CHECK: [[SIDE1:%.*]] = function_ref @_T018pointer_conversion11sideEffect1SiyF
// CHECK: [[RESULT1:%.*]] = apply [[SIDE1]]()
// CHECK: [[T0:%.*]] = select_enum [[COPY]]
// FIXME: this should really go somewhere that will make nil, not some(nil)
// CHECK: cond_br [[T0]], [[SOME_BB:bb[0-9]+]], [[NONE_BB:bb[0-9]+]]
// CHECK: [[SOME_BB]]:
// CHECK: [[SOME_VALUE:%.*]] = unchecked_enum_data [[COPY]]
// CHECK: [[T0:%.*]] = select_enum [[SOME_VALUE]]
// CHECK: cond_br [[T0]], [[SOME_SOME_BB:bb[0-9]+]], [[SOME_NONE_BB:bb[0-9]+]]
// CHECK: [[SOME_NONE_BB]]:
// CHECK: destroy_value [[SOME_VALUE]]
// CHECK: br [[NONE_BB]]
// CHECK: [[SOME_SOME_BB]]:
// CHECK: [[SOME_SOME_VALUE:%.*]] = unchecked_enum_data [[SOME_VALUE]]
// CHECK: [[CONVERT:%.*]] = function_ref @_T0s35_convertConstArrayToPointerArguments9AnyObject_pSg_q_tSayxGs01_E0R_r0_lF
// CHECK: [[TEMP:%.*]] = alloc_stack $UnsafePointer<Int>
// CHECK: [[OWNER:%.*]] = apply [[CONVERT]]<Int, UnsafePointer<Int>>([[TEMP:%.*]], [[SOME_SOME_VALUE]])
// CHECK: [[PTR:%.*]] = load [trivial] [[TEMP]]
// CHECK: [[DEP:%.*]] = mark_dependence [[PTR]] : $UnsafePointer<Int> on [[OWNER]]
// CHECK: [[OPTPTR:%.*]] = enum $Optional<UnsafePointer<Int>>, #Optional.some!enumelt.1, [[DEP]]
// CHECK: dealloc_stack [[TEMP]]
// CHECK: br [[SOME_CONT_BB:bb[0-9]+]]([[OPTPTR]] : $Optional<UnsafePointer<Int>>, [[OWNER]] : $Optional<AnyObject>)
// CHECK: [[SOME_CONT_BB]]([[OPTPTR:%.*]] : $Optional<UnsafePointer<Int>>, [[OWNER:%.*]] : $Optional<AnyObject>):
// CHECK: [[OPTDEP:%.*]] = mark_dependence [[OPTPTR]] : $Optional<UnsafePointer<Int>> on [[OWNER]]
// CHECK: [[OPTOPTPTR:%.*]] = enum $Optional<Optional<UnsafePointer<Int>>>, #Optional.some!enumelt.1, [[OPTDEP]]
// CHECK: [[OPTOPTDEP:%.*]] = mark_dependence [[OPTOPTPTR]] : $Optional<Optional<UnsafePointer<Int>>> on [[OWNER]]
// CHECK: apply [[TAKES]]([[OPTOPTDEP]], [[RESULT1]])
// CHECK: destroy_value [[OWNER]]
// CHECK: end_borrow [[BORROW]]
// CHECK: destroy_value %0
// CHECK: [[NONE_BB]]:
// CHECK: [[NO_VALUE:%.*]] = enum $Optional<UnsafePointer<Int>>, #Optional.none
// CHECK: [[NO_OWNER:%.*]] = enum $Optional<AnyObject>, #Optional.none
// CHECK: br [[SOME_CONT_BB]]([[NO_VALUE]] : $Optional<UnsafePointer<Int>>, [[NO_OWNER]] : $Optional<AnyObject>)
takesOptOptConstPointer(array, and: sideEffect1())
}
// CHECK-LABEL: sil hidden @_T018pointer_conversion21optStringToOptPointerySSSg6string_tF
func optStringToOptPointer(string: String?) {
// CHECK: [[TAKES:%.*]] = function_ref @_T018pointer_conversion23takesOptConstRawPointerySVSg_Si3andtF
// CHECK: [[BORROW:%.*]] = begin_borrow %0
// CHECK: [[COPY:%.*]] = copy_value [[BORROW]]
// CHECK: [[SIDE1:%.*]] = function_ref @_T018pointer_conversion11sideEffect1SiyF
// CHECK: [[RESULT1:%.*]] = apply [[SIDE1]]()
// CHECK: [[T0:%.*]] = select_enum [[COPY]]
// CHECK: cond_br [[T0]], [[SOME_BB:bb[0-9]+]], [[NONE_BB:bb[0-9]+]]
// CHECK: [[SOME_BB]]:
// CHECK: [[SOME_VALUE:%.*]] = unchecked_enum_data [[COPY]]
// CHECK: [[CONVERT:%.*]] = function_ref @_T0s40_convertConstStringToUTF8PointerArguments9AnyObject_pSg_xtSSs01_F0RzlF
// CHECK: [[TEMP:%.*]] = alloc_stack $UnsafeRawPointer
// CHECK: [[OWNER:%.*]] = apply [[CONVERT]]<UnsafeRawPointer>([[TEMP:%.*]], [[SOME_VALUE]])
// CHECK: [[PTR:%.*]] = load [trivial] [[TEMP]]
// CHECK: [[DEP:%.*]] = mark_dependence [[PTR]] : $UnsafeRawPointer on [[OWNER]]
// CHECK: [[OPTPTR:%.*]] = enum $Optional<UnsafeRawPointer>, #Optional.some!enumelt.1, [[DEP]]
// CHECK: dealloc_stack [[TEMP]]
// CHECK: br [[CONT_BB:bb[0-9]+]]([[OPTPTR]] : $Optional<UnsafeRawPointer>, [[OWNER]] : $Optional<AnyObject>)
// CHECK: [[CONT_BB]]([[OPTPTR:%.*]] : $Optional<UnsafeRawPointer>, [[OWNER:%.*]] : $Optional<AnyObject>):
// CHECK: [[OPTDEP:%.*]] = mark_dependence [[OPTPTR]] : $Optional<UnsafeRawPointer> on [[OWNER]]
// CHECK: apply [[TAKES]]([[OPTDEP]], [[RESULT1]])
// CHECK: destroy_value [[OWNER]]
// CHECK: end_borrow [[BORROW]]
// CHECK: destroy_value %0
// CHECK: [[NONE_BB]]:
// CHECK: [[NO_VALUE:%.*]] = enum $Optional<UnsafeRawPointer>, #Optional.none
// CHECK: [[NO_OWNER:%.*]] = enum $Optional<AnyObject>, #Optional.none
// CHECK: br [[CONT_BB]]([[NO_VALUE]] : $Optional<UnsafeRawPointer>, [[NO_OWNER]] : $Optional<AnyObject>)
takesOptConstRawPointer(string, and: sideEffect1())
}
// CHECK-LABEL: sil hidden @_T018pointer_conversion014optOptStringTodD7PointerySSSgSg6string_tF
func optOptStringToOptOptPointer(string: String??) {
// CHECK: [[TAKES:%.*]] = function_ref @_T018pointer_conversion08takesOptD15ConstRawPointerySVSgSg_Si3andtF
// CHECK: [[BORROW:%.*]] = begin_borrow %0
// CHECK: [[COPY:%.*]] = copy_value [[BORROW]]
// CHECK: [[SIDE1:%.*]] = function_ref @_T018pointer_conversion11sideEffect1SiyF
// CHECK: [[RESULT1:%.*]] = apply [[SIDE1]]()
// CHECK: [[T0:%.*]] = select_enum [[COPY]]
// FIXME: this should really go somewhere that will make nil, not some(nil)
// CHECK: cond_br [[T0]], [[SOME_BB:bb[0-9]+]], [[NONE_BB:bb[0-9]+]]
// CHECK: [[SOME_BB]]:
// CHECK: [[SOME_VALUE:%.*]] = unchecked_enum_data [[COPY]]
// CHECK: [[T0:%.*]] = select_enum [[SOME_VALUE]]
// CHECK: cond_br [[T0]], [[SOME_SOME_BB:bb[0-9]+]], [[SOME_NONE_BB:bb[0-9]+]]
// CHECK: [[SOME_NONE_BB]]:
// CHECK: destroy_value [[SOME_VALUE]]
// CHECK: br [[NONE_BB]]
// CHECK: [[SOME_SOME_BB]]:
// CHECK: [[SOME_SOME_VALUE:%.*]] = unchecked_enum_data [[SOME_VALUE]]
// CHECK: [[CONVERT:%.*]] = function_ref @_T0s40_convertConstStringToUTF8PointerArguments9AnyObject_pSg_xtSSs01_F0RzlF
// CHECK: [[TEMP:%.*]] = alloc_stack $UnsafeRawPointer
// CHECK: [[OWNER:%.*]] = apply [[CONVERT]]<UnsafeRawPointer>([[TEMP:%.*]], [[SOME_SOME_VALUE]])
// CHECK: [[PTR:%.*]] = load [trivial] [[TEMP]]
// CHECK: [[DEP:%.*]] = mark_dependence [[PTR]] : $UnsafeRawPointer on [[OWNER]]
// CHECK: [[OPTPTR:%.*]] = enum $Optional<UnsafeRawPointer>, #Optional.some!enumelt.1, [[DEP]]
// CHECK: dealloc_stack [[TEMP]]
// CHECK: br [[SOME_CONT_BB:bb[0-9]+]]([[OPTPTR]] : $Optional<UnsafeRawPointer>, [[OWNER]] : $Optional<AnyObject>)
// CHECK: [[SOME_CONT_BB]]([[OPTPTR:%.*]] : $Optional<UnsafeRawPointer>, [[OWNER:%.*]] : $Optional<AnyObject>):
// CHECK: [[OPTDEP:%.*]] = mark_dependence [[OPTPTR]] : $Optional<UnsafeRawPointer> on [[OWNER]]
// CHECK: [[OPTOPTPTR:%.*]] = enum $Optional<Optional<UnsafeRawPointer>>, #Optional.some!enumelt.1, [[OPTDEP]]
// CHECK: [[OPTOPTDEP:%.*]] = mark_dependence [[OPTOPTPTR]] : $Optional<Optional<UnsafeRawPointer>> on [[OWNER]]
// CHECK: apply [[TAKES]]([[OPTOPTDEP]], [[RESULT1]])
// CHECK: destroy_value [[OWNER]]
// CHECK: end_borrow [[BORROW]]
// CHECK: destroy_value %0
// CHECK: [[NONE_BB]]:
// CHECK: [[NO_VALUE:%.*]] = enum $Optional<UnsafeRawPointer>, #Optional.none
// CHECK: [[NO_OWNER:%.*]] = enum $Optional<AnyObject>, #Optional.none
// CHECK: br [[SOME_CONT_BB]]([[NO_VALUE]] : $Optional<UnsafeRawPointer>, [[NO_OWNER]] : $Optional<AnyObject>)
takesOptOptConstRawPointer(string, and: sideEffect1())
}