[SILGen] Bootstrapping opaque values (#7113)

[NFC] Add -enable-sil-opaque-values frontend option.

This will be used to change the SIL-level calling convention for opaque values,
such as generics and resilient structs, to pass-by-value.  Under this flag,
opaque values have SSA lifetimes, managed by copy_value and destroy_value.

This will make it easier to optimize copies and verify ownership.

* [SILGen] type lowering support for opaque values.

Add OpaqueValueTypeLowering.
Under EnableSILOpaqueValues, lower address-only types as opaque values.

* [SIL] Fix ValueOwnershipKind to support opaque SIL values.

* Test case: SILGen opaque value support for Parameter/ResultConvention.

* [SILGen] opaque value support for function arguments.

* Future Test case: SILGen opaque value specialDest arguments.

* Future Test case: SILGen opaque values: emitOpenExistential.

* Test case: SIL parsing support for EnableSILOpaqueValues.

* SILGen opaque values: prepareArchetypeCallee.

* [SIL Verify] allow copy_value for EnableSILOpaqueValues.

* Test cast: SIL serializer support for opaque values.

* Add a static_assert for ParameterConvention layout.

* Test case: Mandatory SILOpt support for EnableSILOpaqueValues.

* Test case: SILOpt support for EnableSILOpaqueValues.

* SILGen opaque values: TypeLowering emitCopyValue.

* SILBuilder createLoad. Allow loading opaque values.

* SIL Verifier. Allow loading and storing opaque values.

* SILGen emitSemanticStore support for opaque values.

* Test case for SILGen emitSemanticStore.

* Test case for SIL mandatory support for inout assignment.

* Fix SILGen opaque values test case after rebasing.
This commit is contained in:
Andrew Trick
2017-01-27 18:56:53 -08:00
committed by GitHub
parent 51bdd6f09b
commit e9c559b718
22 changed files with 429 additions and 56 deletions

View File

@@ -2710,6 +2710,9 @@ enum class ParameterConvention {
/// guarantees its validity for the entirety of the call. /// guarantees its validity for the entirety of the call.
Direct_Guaranteed, Direct_Guaranteed,
}; };
// Check that the enum values fit inside SILFunctionTypeBits.
static_assert(unsigned(ParameterConvention::Direct_Guaranteed) < (1<<3),
"fits in SILFunctionTypeBits");
// Does this parameter convention require indirect storage? This reflects a // Does this parameter convention require indirect storage? This reflects a
// SILFunctionType's formal (immutable) conventions, as opposed to the transient // SILFunctionType's formal (immutable) conventions, as opposed to the transient

View File

@@ -163,6 +163,11 @@ namespace swift {
/// and methods. /// and methods.
bool InferImportAsMember = false; bool InferImportAsMember = false;
/// If set to true, compile with the SIL Opaque Values enabled.
/// This is for bootstrapping. It can't be in SILOptions because the
/// TypeChecker uses it to set resolve the ParameterConvention.
bool EnableSILOpaqueValues = false;
/// Sets the target we are building for and updates platform conditions /// Sets the target we are building for and updates platform conditions
/// to match. /// to match.
/// ///

View File

@@ -248,6 +248,9 @@ def enable_sil_ownership : Flag<["-"], "enable-sil-ownership">,
def assume_parsing_unqualified_ownership_sil : Flag<["-"], "assume-parsing-unqualified-ownership-sil">, def assume_parsing_unqualified_ownership_sil : Flag<["-"], "assume-parsing-unqualified-ownership-sil">,
HelpText<"Assume unqualified SIL ownership when parsing SIL">; HelpText<"Assume unqualified SIL ownership when parsing SIL">;
def enable_sil_opaque_values : Flag<["-"], "enable-sil-opaque-values">,
HelpText<"Enable SIL Opaque Values">;
def enable_experimental_property_behaviors : def enable_experimental_property_behaviors :
Flag<["-"], "enable-experimental-property-behaviors">, Flag<["-"], "enable-experimental-property-behaviors">,
HelpText<"Enable experimental property behaviors">; HelpText<"Enable experimental property behaviors">;

View File

@@ -469,7 +469,8 @@ public:
assert((Qualifier == LoadOwnershipQualifier::Unqualified) || assert((Qualifier == LoadOwnershipQualifier::Unqualified) ||
F.hasQualifiedOwnership() && F.hasQualifiedOwnership() &&
"Qualified inst in unqualified function"); "Qualified inst in unqualified function");
assert(LV->getType().isLoadable(F.getModule())); assert(!SILModuleConventions(F.getModule()).useLoweredAddresses()
|| LV->getType().isLoadable(F.getModule()));
return insert(new (F.getModule()) return insert(new (F.getModule())
LoadInst(getSILDebugLocation(Loc), LV, Qualifier)); LoadInst(getSILDebugLocation(Loc), LV, Qualifier));
} }

View File

@@ -576,6 +576,8 @@ public:
SILFunctionConventions getFunctionConventions(CanSILFunctionType funcTy); SILFunctionConventions getFunctionConventions(CanSILFunctionType funcTy);
bool useLoweredAddresses() const { return loweredAddresses; }
bool isSILIndirect(SILParameterInfo param) const { bool isSILIndirect(SILParameterInfo param) const {
return isIndirectSILParam(param, loweredAddresses); return isIndirectSILParam(param, loweredAddresses);
} }
@@ -612,6 +614,8 @@ public:
// SILModuleConventions API for convenience. // SILModuleConventions API for convenience.
//===--------------------------------------------------------------------===// //===--------------------------------------------------------------------===//
bool useLoweredAddresses() const { return silConv.useLoweredAddresses(); }
bool isSILIndirect(SILParameterInfo param) const { bool isSILIndirect(SILParameterInfo param) const {
return silConv.isSILIndirect(param); return silConv.isSILIndirect(param);
} }

View File

@@ -170,6 +170,11 @@ public:
return LoweredType; return LoweredType;
} }
/// Returns true if the SIL type is an address.
bool isAddress() const {
return LoweredType.isAddress();
}
/// Return the semantic type. /// Return the semantic type.
/// ///
/// The semantic type is what a type pretends to be during /// The semantic type is what a type pretends to be during

View File

@@ -944,6 +944,8 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
= A->getOption().matches(OPT_enable_objc_interop); = A->getOption().matches(OPT_enable_objc_interop);
} }
Opts.EnableSILOpaqueValues |= Args.hasArg(OPT_enable_sil_opaque_values);
// Must be processed after any other language options that could affect // Must be processed after any other language options that could affect
// platform conditions. // platform conditions.
bool UnsupportedOS, UnsupportedArch; bool UnsupportedOS, UnsupportedArch;

View File

@@ -612,7 +612,7 @@ SILResultInfo::getOwnershipKind(SILModule &M,
} }
SILModuleConventions::SILModuleConventions(const SILModule &M) SILModuleConventions::SILModuleConventions(const SILModule &M)
: loweredAddresses(true) {} : loweredAddresses(!M.getASTContext().LangOpts.EnableSILOpaqueValues) {}
bool SILModuleConventions::isReturnedIndirectlyInSIL(SILType type, bool SILModuleConventions::isReturnedIndirectlyInSIL(SILType type,
SILModule &M) { SILModule &M) {

View File

@@ -79,7 +79,15 @@ ValueOwnershipKind::ValueOwnershipKind(SILModule &M, SILType Type,
: Value() { : Value() {
switch (Convention) { switch (Convention) {
case SILArgumentConvention::Indirect_In: case SILArgumentConvention::Indirect_In:
Value = SILModuleConventions(M).useLoweredAddresses()
? ValueOwnershipKind::Trivial
: ValueOwnershipKind::Owned;
break;
case SILArgumentConvention::Indirect_In_Guaranteed: case SILArgumentConvention::Indirect_In_Guaranteed:
Value = SILModuleConventions(M).useLoweredAddresses()
? ValueOwnershipKind::Trivial
: ValueOwnershipKind::Guaranteed;
break;
case SILArgumentConvention::Indirect_Inout: case SILArgumentConvention::Indirect_Inout:
case SILArgumentConvention::Indirect_InoutAliasable: case SILArgumentConvention::Indirect_InoutAliasable:
case SILArgumentConvention::Indirect_Out: case SILArgumentConvention::Indirect_Out:

View File

@@ -103,6 +103,7 @@ namespace {
class SILVerifier : public SILVerifierBase<SILVerifier> { class SILVerifier : public SILVerifierBase<SILVerifier> {
ModuleDecl *M; ModuleDecl *M;
const SILFunction &F; const SILFunction &F;
SILFunctionConventions fnConv;
Lowering::TypeConverter &TC; Lowering::TypeConverter &TC;
SILOpenedArchetypesTracker OpenedArchetypes; SILOpenedArchetypesTracker OpenedArchetypes;
const SILInstruction *CurInstruction = nullptr; const SILInstruction *CurInstruction = nullptr;
@@ -409,8 +410,9 @@ public:
} }
SILVerifier(const SILFunction &F, bool SingleFunction = true) SILVerifier(const SILFunction &F, bool SingleFunction = true)
: M(F.getModule().getSwiftModule()), F(F), TC(F.getModule().Types), : M(F.getModule().getSwiftModule()), F(F),
OpenedArchetypes(F), Dominance(nullptr), fnConv(F.getLoweredFunctionType(), F.getModule()),
TC(F.getModule().Types), OpenedArchetypes(F), Dominance(nullptr),
SingleFunction(SingleFunction) { SingleFunction(SingleFunction) {
if (F.isExternalDeclaration()) if (F.isExternalDeclaration())
return; return;
@@ -847,13 +849,13 @@ public:
"substituted callee type does not match substitutions"); "substituted callee type does not match substitutions");
// Check that the arguments and result match. // Check that the arguments and result match.
SILFunctionConventions fnConv(substTy, F.getModule()); SILFunctionConventions substConv(substTy, F.getModule());
//require(site.getArguments().size() == substTy->getNumSILArguments(), //require(site.getArguments().size() == substTy->getNumSILArguments(),
require(site.getNumCallArguments() == fnConv.getNumSILArguments(), require(site.getNumCallArguments() == substConv.getNumSILArguments(),
"apply doesn't have right number of arguments for function"); "apply doesn't have right number of arguments for function");
for (size_t i = 0, size = site.getNumCallArguments(); i < size; ++i) { for (size_t i = 0, size = site.getNumCallArguments(); i < size; ++i) {
requireSameType(site.getArguments()[i]->getType(), requireSameType(site.getArguments()[i]->getType(),
fnConv.getSILArgumentType(i), substConv.getSILArgumentType(i),
"operand of 'apply' doesn't match function input type"); "operand of 'apply' doesn't match function input type");
} }
} }
@@ -861,14 +863,14 @@ public:
void checkApplyInst(ApplyInst *AI) { void checkApplyInst(ApplyInst *AI) {
checkFullApplySite(AI); checkFullApplySite(AI);
SILFunctionConventions fnConv(AI->getSubstCalleeType(), F.getModule()); SILFunctionConventions calleeConv(AI->getSubstCalleeType(), F.getModule());
require(AI->getType() == fnConv.getSILResultType(), require(AI->getType() == calleeConv.getSILResultType(),
"type of apply instruction doesn't match function result type"); "type of apply instruction doesn't match function result type");
if (AI->isNonThrowing()) { if (AI->isNonThrowing()) {
require(fnConv.funcTy->hasErrorResult(), require(calleeConv.funcTy->hasErrorResult(),
"nothrow flag used for callee without error result"); "nothrow flag used for callee without error result");
} else { } else {
require(!fnConv.funcTy->hasErrorResult(), require(!calleeConv.funcTy->hasErrorResult(),
"apply instruction cannot call function with error result"); "apply instruction cannot call function with error result");
} }
@@ -884,23 +886,23 @@ public:
void checkTryApplyInst(TryApplyInst *AI) { void checkTryApplyInst(TryApplyInst *AI) {
checkFullApplySite(AI); checkFullApplySite(AI);
SILFunctionConventions fnConv(AI->getSubstCalleeType(), F.getModule()); SILFunctionConventions calleeConv(AI->getSubstCalleeType(), F.getModule());
auto normalBB = AI->getNormalBB(); auto normalBB = AI->getNormalBB();
require(normalBB->args_size() == 1, require(normalBB->args_size() == 1,
"normal destination of try_apply must take one argument"); "normal destination of try_apply must take one argument");
requireSameType((*normalBB->args_begin())->getType(), requireSameType((*normalBB->args_begin())->getType(),
fnConv.getSILResultType(), calleeConv.getSILResultType(),
"normal destination of try_apply must take argument " "normal destination of try_apply must take argument "
"of normal result type"); "of normal result type");
auto errorBB = AI->getErrorBB(); auto errorBB = AI->getErrorBB();
require(fnConv.funcTy->hasErrorResult(), require(calleeConv.funcTy->hasErrorResult(),
"try_apply must call function with error result"); "try_apply must call function with error result");
require(errorBB->args_size() == 1, require(errorBB->args_size() == 1,
"error destination of try_apply must take one argument"); "error destination of try_apply must take one argument");
requireSameType((*errorBB->args_begin())->getType(), requireSameType((*errorBB->args_begin())->getType(),
fnConv.getSILErrorType(), calleeConv.getSILErrorType(),
"error destination of try_apply must take argument " "error destination of try_apply must take argument "
"of error result type"); "of error result type");
} }
@@ -1126,7 +1128,8 @@ public:
void checkLoadInst(LoadInst *LI) { void checkLoadInst(LoadInst *LI) {
require(LI->getType().isObject(), "Result of load must be an object"); require(LI->getType().isObject(), "Result of load must be an object");
require(LI->getType().isLoadable(LI->getModule()), require(!fnConv.useLoweredAddresses()
|| LI->getType().isLoadable(LI->getModule()),
"Load must have a loadable type"); "Load must have a loadable type");
require(LI->getOperand()->getType().isAddress(), require(LI->getOperand()->getType().isAddress(),
"Load operand must be an address"); "Load operand must be an address");
@@ -1188,7 +1191,8 @@ public:
void checkStoreInst(StoreInst *SI) { void checkStoreInst(StoreInst *SI) {
require(SI->getSrc()->getType().isObject(), require(SI->getSrc()->getType().isObject(),
"Can't store from an address source"); "Can't store from an address source");
require(SI->getSrc()->getType().isLoadable(SI->getModule()), require(!fnConv.useLoweredAddresses()
|| SI->getSrc()->getType().isLoadable(SI->getModule()),
"Can't store a non loadable type"); "Can't store a non loadable type");
require(SI->getDest()->getType().isAddress(), require(SI->getDest()->getType().isAddress(),
"Must store to an address dest"); "Must store to an address dest");
@@ -1322,8 +1326,7 @@ public:
MU->getSetterSubstitutions()); MU->getSetterSubstitutions());
require(SubstSetterTy->getParameters().size() == 2, require(SubstSetterTy->getParameters().size() == 2,
"mark_uninitialized setter must have a value and self param"); "mark_uninitialized setter must have a value and self param");
auto silConv = SILModuleConventions(F.getModule()); requireSameType(fnConv.getSILType(SubstSetterTy->getSelfParameter()),
requireSameType(silConv.getSILType(SubstSetterTy->getSelfParameter()),
MU->getSelf()->getType(), MU->getSelf()->getType(),
"self type must match setter's self parameter type"); "self type must match setter's self parameter type");
@@ -1366,7 +1369,7 @@ public:
void checkCopyValueInst(CopyValueInst *I) { void checkCopyValueInst(CopyValueInst *I) {
require(I->getOperand()->getType().isObject(), require(I->getOperand()->getType().isObject(),
"Source value should be an object value"); "Source value should be an object value");
require(F.hasQualifiedOwnership(), require(!fnConv.useLoweredAddresses() || F.hasQualifiedOwnership(),
"copy_value is only valid in functions with qualified " "copy_value is only valid in functions with qualified "
"ownership"); "ownership");
} }
@@ -1384,7 +1387,7 @@ public:
void checkDestroyValueInst(DestroyValueInst *I) { void checkDestroyValueInst(DestroyValueInst *I) {
require(I->getOperand()->getType().isObject(), require(I->getOperand()->getType().isObject(),
"Source value should be an object value"); "Source value should be an object value");
require(F.hasQualifiedOwnership(), require(!fnConv.useLoweredAddresses() || F.hasQualifiedOwnership(),
"destroy_value is only valid in functions with qualified " "destroy_value is only valid in functions with qualified "
"ownership"); "ownership");
} }
@@ -1912,8 +1915,7 @@ public:
} }
SILType getMethodSelfType(CanSILFunctionType ft) { SILType getMethodSelfType(CanSILFunctionType ft) {
auto silConv = SILModuleConventions(F.getModule()); return fnConv.getSILType(ft->getParameters().back());
return silConv.getSILType(ft->getParameters().back());
} }
void checkWitnessMethodInst(WitnessMethodInst *AMI) { void checkWitnessMethodInst(WitnessMethodInst *AMI) {
@@ -2798,7 +2800,7 @@ public:
DEBUG(RI->print(llvm::dbgs())); DEBUG(RI->print(llvm::dbgs()));
SILType functionResultType = SILType functionResultType =
F.mapTypeIntoContext(F.getConventions().getSILResultType()); F.mapTypeIntoContext(fnConv.getSILResultType());
SILType instResultType = RI->getOperand()->getType(); SILType instResultType = RI->getOperand()->getType();
DEBUG(llvm::dbgs() << "function return type: "; DEBUG(llvm::dbgs() << "function return type: ";
functionResultType.dump(); functionResultType.dump();
@@ -2815,8 +2817,7 @@ public:
require(fnType->hasErrorResult(), require(fnType->hasErrorResult(),
"throw in function that doesn't have an error result"); "throw in function that doesn't have an error result");
SILType functionResultType = SILType functionResultType = F.mapTypeIntoContext(fnConv.getSILErrorType());
F.mapTypeIntoContext(F.getConventions().getSILErrorType());
SILType instResultType = TI->getOperand()->getType(); SILType instResultType = TI->getOperand()->getType();
DEBUG(llvm::dbgs() << "function error result type: "; DEBUG(llvm::dbgs() << "function error result type: ";
functionResultType.dump(); functionResultType.dump();
@@ -3248,7 +3249,6 @@ public:
} }
void verifyEntryPointArguments(SILBasicBlock *entry) { void verifyEntryPointArguments(SILBasicBlock *entry) {
auto fnConv = F.getConventions();
DEBUG(llvm::dbgs() << "Argument types for entry point BB:\n"; DEBUG(llvm::dbgs() << "Argument types for entry point BB:\n";
for (auto *arg for (auto *arg
: make_range(entry->args_begin(), entry->args_end())) : make_range(entry->args_begin(), entry->args_end()))
@@ -3267,14 +3267,13 @@ public:
bool matched = true; bool matched = true;
auto argI = entry->args_begin(); auto argI = entry->args_begin();
SILModule &M = F.getModule(); SILModule &M = F.getModule();
auto funcConv = F.getConventions();
auto check = [&](const char *what, SILType ty) { auto check = [&](const char *what, SILType ty) {
auto mappedTy = F.mapTypeIntoContext(ty); auto mappedTy = F.mapTypeIntoContext(ty);
SILArgument *bbarg = *argI; SILArgument *bbarg = *argI;
++argI; ++argI;
auto ownershipkind = ValueOwnershipKind( auto ownershipkind = ValueOwnershipKind(
M, mappedTy, funcConv.getSILArgumentConvention(bbarg->getIndex())); M, mappedTy, fnConv.getSILArgumentConvention(bbarg->getIndex()));
if (bbarg->getType() != mappedTy) { if (bbarg->getType() != mappedTy) {
llvm::errs() << what << " type mismatch!\n"; llvm::errs() << what << " type mismatch!\n";
llvm::errs() << " argument: "; bbarg->dump(); llvm::errs() << " argument: "; bbarg->dump();

View File

@@ -487,12 +487,15 @@ bool SILType::isAddressOnly(CanType type, SILModule &M,
} }
namespace { namespace {
/// A class for loadable types. /// A class for types that can be loaded and stored in SIL.
/// This always include loadable types, but can include address-only types if
/// opaque values are passed by value.
class LoadableTypeLowering : public TypeLowering { class LoadableTypeLowering : public TypeLowering {
protected: protected:
LoadableTypeLowering(SILType type, IsTrivial_t isTrivial, LoadableTypeLowering(SILType type, IsTrivial_t isTrivial,
IsAddressOnly_t isAddressOnly,
IsReferenceCounted_t isRefCounted) IsReferenceCounted_t isRefCounted)
: TypeLowering(type, isTrivial, IsNotAddressOnly, isRefCounted) {} : TypeLowering(type, isTrivial, isAddressOnly, isRefCounted) {}
public: public:
void emitDestroyAddress(SILBuilder &B, SILLocation loc, void emitDestroyAddress(SILBuilder &B, SILLocation loc,
@@ -518,7 +521,8 @@ namespace {
class TrivialTypeLowering final : public LoadableTypeLowering { class TrivialTypeLowering final : public LoadableTypeLowering {
public: public:
TrivialTypeLowering(SILType type) TrivialTypeLowering(SILType type)
: LoadableTypeLowering(type, IsTrivial, IsNotReferenceCounted) {} : LoadableTypeLowering(type, IsTrivial, IsNotAddressOnly,
IsNotReferenceCounted) {}
SILValue emitLoadOfCopy(SILBuilder &B, SILLocation loc, SILValue addr, SILValue emitLoadOfCopy(SILBuilder &B, SILLocation loc, SILValue addr,
IsTake_t isTake) const override { IsTake_t isTake) const override {
@@ -579,8 +583,9 @@ namespace {
class NonTrivialLoadableTypeLowering : public LoadableTypeLowering { class NonTrivialLoadableTypeLowering : public LoadableTypeLowering {
public: public:
NonTrivialLoadableTypeLowering(SILType type, NonTrivialLoadableTypeLowering(SILType type,
IsAddressOnly_t isAddressOnly,
IsReferenceCounted_t isRefCounted) IsReferenceCounted_t isRefCounted)
: LoadableTypeLowering(type, IsNotTrivial, isRefCounted) {} : LoadableTypeLowering(type, IsNotTrivial, isAddressOnly, isRefCounted) {}
SILValue emitLoadOfCopy(SILBuilder &B, SILLocation loc, SILValue emitLoadOfCopy(SILBuilder &B, SILLocation loc,
SILValue addr, IsTake_t isTake) const override { SILValue addr, IsTake_t isTake) const override {
@@ -671,6 +676,7 @@ namespace {
public: public:
LoadableAggTypeLowering(CanType type) LoadableAggTypeLowering(CanType type)
: NonTrivialLoadableTypeLowering(SILType::getPrimitiveObjectType(type), : NonTrivialLoadableTypeLowering(SILType::getPrimitiveObjectType(type),
IsNotAddressOnly,
IsNotReferenceCounted) { IsNotReferenceCounted) {
} }
@@ -849,6 +855,7 @@ namespace {
public: public:
LoadableEnumTypeLowering(CanType type) LoadableEnumTypeLowering(CanType type)
: NonTrivialLoadableTypeLowering(SILType::getPrimitiveObjectType(type), : NonTrivialLoadableTypeLowering(SILType::getPrimitiveObjectType(type),
IsNotAddressOnly,
IsNotReferenceCounted) {} IsNotReferenceCounted) {}
SILValue emitCopyValue(SILBuilder &B, SILLocation loc, SILValue emitCopyValue(SILBuilder &B, SILLocation loc,
@@ -892,8 +899,10 @@ namespace {
class LeafLoadableTypeLowering : public NonTrivialLoadableTypeLowering { class LeafLoadableTypeLowering : public NonTrivialLoadableTypeLowering {
public: public:
LeafLoadableTypeLowering(SILType type, IsReferenceCounted_t isRefCounted) LeafLoadableTypeLowering(SILType type,
: NonTrivialLoadableTypeLowering(type, isRefCounted) {} IsAddressOnly_t isAddressOnly,
IsReferenceCounted_t isRefCounted)
: NonTrivialLoadableTypeLowering(type, isAddressOnly, isRefCounted) {}
SILValue emitLoweredCopyValue(SILBuilder &B, SILLocation loc, SILValue emitLoweredCopyValue(SILBuilder &B, SILLocation loc,
SILValue value, SILValue value,
@@ -912,7 +921,7 @@ namespace {
class ReferenceTypeLowering : public LeafLoadableTypeLowering { class ReferenceTypeLowering : public LeafLoadableTypeLowering {
public: public:
ReferenceTypeLowering(SILType type) ReferenceTypeLowering(SILType type)
: LeafLoadableTypeLowering(type, IsReferenceCounted) {} : LeafLoadableTypeLowering(type, IsNotAddressOnly, IsReferenceCounted) {}
SILValue emitCopyValue(SILBuilder &B, SILLocation loc, SILValue emitCopyValue(SILBuilder &B, SILLocation loc,
SILValue value) const override { SILValue value) const override {
@@ -940,7 +949,7 @@ namespace {
class LoadableUnownedTypeLowering final : public LeafLoadableTypeLowering { class LoadableUnownedTypeLowering final : public LeafLoadableTypeLowering {
public: public:
LoadableUnownedTypeLowering(SILType type) LoadableUnownedTypeLowering(SILType type)
: LeafLoadableTypeLowering(type, IsReferenceCounted) {} : LeafLoadableTypeLowering(type, IsNotAddressOnly, IsReferenceCounted) {}
SILValue emitCopyValue(SILBuilder &B, SILLocation loc, SILValue emitCopyValue(SILBuilder &B, SILLocation loc,
SILValue value) const override { SILValue value) const override {
@@ -1051,6 +1060,80 @@ namespace {
} }
}; };
/// Lower address only types as opaque values.
///
/// Opaque values behave like loadable leaf types in SIL.
///
/// FIXME: When you remove an unreachable, just delete the method.
class OpaqueValueTypeLowering : public LeafLoadableTypeLowering {
public:
OpaqueValueTypeLowering(SILType type)
: LeafLoadableTypeLowering(type, IsAddressOnly, IsReferenceCounted) {}
// --- Same as LoadableTypeLowering.
void emitDestroyAddress(SILBuilder &B, SILLocation loc,
SILValue addr) const override {
llvm_unreachable("destroy address");
}
void emitDestroyRValue(SILBuilder &B, SILLocation loc,
SILValue value) const override {
llvm_unreachable("destroy value");
}
void emitCopyInto(SILBuilder &B, SILLocation loc,
SILValue src, SILValue dest, IsTake_t isTake,
IsInitialization_t isInit) const override {
llvm_unreachable("copy into");
}
// --- Same as NonTrivialLoadableTypeLowering
SILValue emitLoadOfCopy(SILBuilder &B, SILLocation loc,
SILValue addr, IsTake_t isTake) const override {
llvm_unreachable("load copy");
}
void emitStoreOfCopy(SILBuilder &B, SILLocation loc,
SILValue newValue, SILValue addr,
IsInitialization_t isInit) const override {
llvm_unreachable("store copy");
}
void emitStore(SILBuilder &B, SILLocation loc, SILValue value,
SILValue addr, StoreOwnershipQualifier qual) const override {
llvm_unreachable("store");
}
SILValue emitLoad(SILBuilder &B, SILLocation loc, SILValue addr,
LoadOwnershipQualifier qual) const override {
llvm_unreachable("store");
}
// --- Same as LeafLoadableTypeLowering.
SILValue emitLoweredCopyValue(SILBuilder &B, SILLocation loc,
SILValue value,
LoweringStyle style) const override {
llvm_unreachable("lowered copy");
}
void emitLoweredDestroyValue(SILBuilder &B, SILLocation loc, SILValue value,
LoweringStyle style) const override {
llvm_unreachable("destroy value");
}
SILValue emitCopyValue(SILBuilder &B, SILLocation loc,
SILValue value) const override {
return B.createCopyValue(loc, value);
}
void emitDestroyValue(SILBuilder &B, SILLocation loc,
SILValue value) const override {
B.createDestroyValue(loc, value);
}
};
/// Build the appropriate TypeLowering subclass for the given type, /// Build the appropriate TypeLowering subclass for the given type,
/// which is assumed to already have been lowered. /// which is assumed to already have been lowered.
class LowerType class LowerType
@@ -1075,8 +1158,12 @@ namespace {
} }
const TypeLowering *handleAddressOnly(CanType type) { const TypeLowering *handleAddressOnly(CanType type) {
auto silType = SILType::getPrimitiveAddressType(type); if (SILModuleConventions(M).useLoweredAddresses()) {
return new (TC, Dependent) AddressOnlyTypeLowering(silType); auto silType = SILType::getPrimitiveAddressType(type);
return new (TC, Dependent) AddressOnlyTypeLowering(silType);
}
auto silType = SILType::getPrimitiveObjectType(type);
return new (TC, Dependent) OpaqueValueTypeLowering(silType);
} }
const TypeLowering * const TypeLowering *

View File

@@ -64,7 +64,7 @@ ManagedValue ManagedValue::copyUnmanaged(SILGenFunction &gen, SILLocation loc) {
return *this; return *this;
SILValue result; SILValue result;
if (!lowering.isAddressOnly()) { if (!lowering.isAddress()) {
result = lowering.emitCopyValue(gen.B, loc, getValue()); result = lowering.emitCopyValue(gen.B, loc, getValue());
} else { } else {
result = gen.emitTemporaryAllocation(loc, getType()); result = gen.emitTemporaryAllocation(loc, getType());

View File

@@ -694,9 +694,10 @@ static Callee prepareArchetypeCallee(SILGenFunction &gen, SILLocation loc,
!cast<ArchetypeType>(selfValue.getSubstRValueType())->requiresClass()) !cast<ArchetypeType>(selfValue.getSubstRValueType())->requiresClass())
return; return;
auto selfParameter = getSelfParameter(); assert(gen.silConv.useLoweredAddresses()
assert(gen.silConv.isSILIndirect(selfParameter)); == gen.silConv.isSILIndirect(getSelfParameter()));
(void)selfParameter; if (!gen.silConv.useLoweredAddresses())
return;
SILLocation selfLoc = selfValue.getLocation(); SILLocation selfLoc = selfValue.getLocation();

View File

@@ -2343,7 +2343,8 @@ void SILGenFunction::emitSemanticStore(SILLocation loc,
// Easy case: the types match. // Easy case: the types match.
if (rvalue->getType() == destTL.getLoweredType()) { if (rvalue->getType() == destTL.getLoweredType()) {
assert(destTL.isAddressOnly() == rvalue->getType().isAddress()); assert(!silConv.useLoweredAddresses()
|| (destTL.isAddressOnly() == rvalue->getType().isAddress()));
if (rvalue->getType().isAddress()) { if (rvalue->getType().isAddress()) {
B.createCopyAddr(loc, rvalue, dest, IsTake, isInit); B.createCopyAddr(loc, rvalue, dest, IsTake, isInit);
} else { } else {

View File

@@ -730,6 +730,19 @@ static ManagedValue manageParam(SILGenFunction &gen,
SILParameterInfo info, SILParameterInfo info,
bool allowPlusZero) { bool allowPlusZero) {
switch (info.getConvention()) { switch (info.getConvention()) {
case ParameterConvention::Indirect_In_Guaranteed:
if (gen.silConv.useLoweredAddresses()) {
// FIXME: Avoid a behavior change while guaranteed self is disabled by
// default.
if (allowPlusZero) {
return ManagedValue::forUnmanaged(paramValue);
} else {
auto copy = gen.emitTemporaryAllocation(loc, paramValue->getType());
gen.B.createCopyAddr(loc, paramValue, copy, IsNotTake, IsInitialization);
return gen.emitManagedBufferWithCleanup(copy);
}
}
SWIFT_FALLTHROUGH;
case ParameterConvention::Direct_Guaranteed: case ParameterConvention::Direct_Guaranteed:
if (allowPlusZero) if (allowPlusZero)
return ManagedValue::forUnmanaged(paramValue); return ManagedValue::forUnmanaged(paramValue);
@@ -743,21 +756,14 @@ static ManagedValue manageParam(SILGenFunction &gen,
case ParameterConvention::Direct_Owned: case ParameterConvention::Direct_Owned:
return gen.emitManagedRValueWithCleanup(paramValue); return gen.emitManagedRValueWithCleanup(paramValue);
case ParameterConvention::Indirect_In_Guaranteed: case ParameterConvention::Indirect_In:
// FIXME: Avoid a behavior change while guaranteed self is disabled by if (gen.silConv.useLoweredAddresses())
// default. return gen.emitManagedBufferWithCleanup(paramValue);
if (allowPlusZero) { return gen.emitManagedRValueWithCleanup(paramValue);
return ManagedValue::forUnmanaged(paramValue);
} else {
auto copy = gen.emitTemporaryAllocation(loc, paramValue->getType());
gen.B.createCopyAddr(loc, paramValue, copy, IsNotTake, IsInitialization);
return gen.emitManagedBufferWithCleanup(copy);
}
case ParameterConvention::Indirect_Inout: case ParameterConvention::Indirect_Inout:
case ParameterConvention::Indirect_InoutAliasable: case ParameterConvention::Indirect_InoutAliasable:
return ManagedValue::forLValue(paramValue); return ManagedValue::forLValue(paramValue);
case ParameterConvention::Indirect_In:
return gen.emitManagedBufferWithCleanup(paramValue);
} }
llvm_unreachable("bad parameter convention"); llvm_unreachable("bad parameter convention");
} }

View File

@@ -0,0 +1,62 @@
// RUN: %target-sil-opt -enable-sil-opaque-values -enable-sil-verify-all -emit-sorted-sil %s | %FileCheck %s
import Builtin
sil_stage canonical
// Test @callee_guaranteed parsing.
// ----
sil @dummy : $@convention(thin) (Builtin.Int64) -> ()
// SILFunctionType.getCalleeConvention requires all ParameterConventions to fit
// inside SILFunctionTypeBits. The only way to test this is with @callee_guaranteed.
// CHECK-LABEL: sil hidden @parse_callee_guaranteed : $@convention(thin) () -> @callee_guaranteed () -> () {
sil hidden @parse_callee_guaranteed : $@convention(thin) () -> @callee_guaranteed () -> () {
entry:
%f = function_ref @dummy : $@convention(thin) (Builtin.Int64) -> ()
%z = integer_literal $Builtin.Int64, 0
// CHECK: [[PA:%.*]] = partial_apply [callee_guaranteed] {{.*}} $@convention(thin) (Builtin.Int64) -> ()
%g = partial_apply [callee_guaranteed] %f(%z) : $@convention(thin) (Builtin.Int64) -> ()
// CHECK: return [[PA]] : $@callee_guaranteed () -> ()
return %g : $@callee_guaranteed () -> ()
}
// CHECK-LABEL: } // end sil function 'parse_callee_guaranteed'
// Test @in/@out parsing.
// ----
//
// CHECK-LABEL: sil hidden @parse_identity : $@convention(thin) <T> (@in T) -> @out T {
// CHECK: bb0(%0 : $T):
// CHECK: return %0 : $T
// CHECK-LABEL: } // end sil function 'parse_identity'
sil hidden @parse_identity : $@convention(thin) <T> (@in T) -> @out T {
bb0(%0 : $T):
return %0 : $T
}
// Test @in_guaranteed parsing.
// ----
protocol Foo {
func foo()
}
struct S : Foo {
func foo()
init()
}
sil @doWithS : $@convention(method) (S) -> ()
// CHECK-LABEL: sil hidden [transparent] [thunk] @parse_mutating : $@convention(witness_method) (@in_guaranteed S) -> () {
sil hidden [transparent] [thunk] @parse_mutating : $@convention(witness_method) (@in_guaranteed S) -> () {
// CHECK: bb0(%0 : $S):
bb0(%0 : $S):
%f = function_ref @doWithS : $@convention(method) (S) -> ()
// CHECK: apply %{{.*}}(%0) : $@convention(method) (S) -> ()
%a = apply %f(%0) : $@convention(method) (S) -> ()
%t = tuple ()
return %t : $()
}
// CHECK-LABEL: } // end sil function 'parse_mutating'

View File

@@ -0,0 +1,49 @@
// First parse this and then emit a *.sib. Then read in the *.sib, then recreate
// RUN: rm -rfv %t
// RUN: mkdir %t
// FIXME: <rdar://problem/29281364> sil-opt -verify is broken
// RUN: %target-sil-opt %s -enable-sil-opaque-values -emit-sib -o %t/tmp.sib -module-name opaqueval
// RUN: %target-sil-opt %t/tmp.sib -enable-sil-opaque-values -verify -o %t/tmp.2.sib -module-name opaqueval
// RUN: %target-sil-opt %t/tmp.2.sib -enable-sil-opaque-values -emit-sorted-sil -verify -module-name opaqueval | %FileCheck %s
sil_stage canonical
import Builtin
// Test @in/@out serialization.
// ----
// CHECK-LABEL: sil hidden @serialize_identity : $@convention(thin) <T> (@in T) -> @out T {
sil hidden @serialize_identity : $@convention(thin) <T> (@in T) -> @out T {
// CHECK: bb0(%0 : $T):
bb0(%0 : $T):
// CHECK: return %0 : $T
return %0 : $T
}
// CHECK-LABEL: } // end sil function 'serialize_identity'
// Test @in_guaranteed serialization.
// ----
protocol Foo {
func foo()
}
struct S : Foo {
func foo()
init()
}
sil @doWithS : $@convention(method) (S) -> ()
// CHECK-LABEL: sil hidden [transparent] [thunk] @serialize_mutating : $@convention(witness_method) (@in_guaranteed S) -> () {
sil hidden [transparent] [thunk] @serialize_mutating : $@convention(witness_method) (@in_guaranteed S) -> () {
// CHECK: bb0(%0 : $S):
bb0(%0 : $S):
%f = function_ref @doWithS : $@convention(method) (S) -> ()
// CHECK: apply %{{.*}}(%0) : $@convention(method) (S) -> ()
%a = apply %f(%0) : $@convention(method) (S) -> ()
%t = tuple ()
return %t : $()
}
// CHECK-LABEL: } // end sil function 'serialize_mutating'

View File

@@ -0,0 +1,69 @@
// RUN: %target-swift-frontend -enable-sil-opaque-values -emit-sorted-sil -Xllvm -sil-full-demangle -emit-silgen %s | %FileCheck %s
protocol Foo {
func foo()
}
// Test emitSemanticStore.
// ---
// CHECK-LABEL: sil hidden @_TF20opaque_values_silgen11assigninouturFTRxx_T_ : $@convention(thin) <T> (@inout T, @in T) -> () {
// CHECK: bb0(%0 : $*T, %1 : $T):
// CHECK: %[[CPY:.*]] = copy_value %1 : $T
// CHECK: assign %[[CPY]] to %0 : $*T
// CHECK: destroy_value %1 : $T
// CHECK: return %{{.*}} : $()
// CHECK-LABEL: } // end sil function '_TF20opaque_values_silgen11assigninouturFTRxx_T_'
func assigninout<T>(_ a: inout T, _ b: T) {
a = b
}
// SILGen, prepareArchetypeCallee. Materialize a
// non-class-constrainted self from a class-constrained archetype.
// ---
// CHECK-LABEL: sil hidden @_TF20opaque_values_silgen15materializeSelfuRxs9AnyObjectxS_3FoorFT1tx_T_ : $@convention(thin) <T where T : AnyObject, T : Foo> (@owned T) -> () {
// CHECK: bb0(%0 : $T):
// CHECK: witness_method $T, #Foo.foo!1 : <Self where Self : Foo> (Self) -> () -> () : $@convention(witness_method) <τ_0_0 where τ_0_0 : Foo> (@in_guaranteed τ_0_0) -> ()
// CHECK: apply %{{[0-9]+}}<T>(%0) : $@convention(witness_method) <τ_0_0 where τ_0_0 : Foo> (@in_guaranteed τ_0_0) -> ()
// CHECK: destroy_value %0 : $T
// CHECK: return %{{[0-9]+}} : $()
// CHECK: } // end sil function '_TF20opaque_values_silgen15materializeSelfuRxs9AnyObjectxS_3FoorFT1tx_T_'
func materializeSelf<T: Foo>(t: T) where T: AnyObject {
t.foo()
}
// Test OpaqueTypeLowering copyValue and destroyValue.
// ---
// CHECK-LABEL: sil hidden @_TF20opaque_values_silgen6callerurFxx : $@convention(thin) <T> (@in T) -> @out T {
// CHECK: bb0(%0 : $T):
// CHECK: %[[COPY:[0-9]+]] = copy_value %0 : $T
// CHECK: %{{.*}} = apply %{{.*}}<T>(%[[COPY]]) : $@convention(thin) <τ_0_0> (@in τ_0_0) -> @out τ_0_0
// CHECK: destroy_value %0 : $T
// CHECK: return %{{.*}} : $T
// CHECK-LABEL: } // end sil function '_TF20opaque_values_silgen6callerurFxx'
func caller<T>(_ t: T) -> T {
return caller(t)
}
// Test a simple opaque parameter and return value.
// ---
// CHECK-LABEL: sil hidden @_TF20opaque_values_silgen8identityurFT1tx_x : $@convention(thin) <T> (@in T) -> @out T {
// CHECK: bb0(%0 : $T):
// CHECK: %[[CPY:.*]] = copy_value %0 : $T
// CHECK: destroy_value %0 : $T
// CHECK: return %[[CPY]] : $T
// CHECK-LABEL: } // end sil function '_TF20opaque_values_silgen8identityurFT1tx_x'
func identity<T>(t: T) -> T {
return t
}
// Test a guaranteed opaque parameter.
// ---
// CHECK-LABEL: sil hidden [transparent] [thunk] @_TTWV20opaque_values_silgen17HasGuaranteedSelfS_3FooS_FS1_3foofT_T_ : $@convention(witness_method) (@in_guaranteed HasGuaranteedSelf) -> () {
// CHECK: bb0(%0 : $HasGuaranteedSelf):
// CHECK: %[[F:.*]] = function_ref @_TFV20opaque_values_silgen17HasGuaranteedSelf3foofT_T_ : $@convention(method) (HasGuaranteedSelf) -> ()
// CHECK: apply %[[F]](%0) : $@convention(method) (HasGuaranteedSelf) -> ()
// CHECK: return
// CHECK-LABEL: } // end sil function '_TTWV20opaque_values_silgen17HasGuaranteedSelfS_3FooS_FS1_3foofT_T_'
struct HasGuaranteedSelf : Foo {
func foo() {}
}

View File

@@ -0,0 +1,18 @@
// RUN: %target-swift-frontend -enable-sil-opaque-values -emit-sorted-sil -Xllvm -sil-full-demangle -emit-silgen %s | %FileCheck %s
// REQUIRES: EnableSILOpaqueValues
func hasVarArg(_ args: Any...) {}
// ArgEmitter: fix SpecialDest args.
public func callVarArg() {
hasVarArg(3)
}
public protocol P {
var x : Int { get }
}
// SILGenFunction::emitOpenExistential: add an open_existential instruction.
public func foo(p: P) -> Int {
return p.x
}

View File

@@ -0,0 +1,38 @@
// RUN: %target-sil-opt -diagnostics -enable-sil-opaque-values -emit-sorted-sil %s | %FileCheck %s
import Builtin
sil_stage raw
// Test lowerAssignment, createLoad, checkLoadInst, checkStoreInst.
// ---
// CHECK-LABEL: sil hidden @assign_inout : $@convention(thin) <T> (@inout T, @in T) -> () {
// CHECK: bb0(%0 : $*T, %1 : $T):
// CHECK: %[[CPY:.*]] = copy_value %1 : $T
// CHECK: %[[LD:.*]] = load %0 : $*T
// CHECK: store %1 to %0 : $*T
// CHECK: destroy_value %[[LD]] : $T
// CHECK: destroy_value %1 : $T
// CHECK: return %{{.*}} : $()
// CHECK-LABEL: } // end sil function 'assign_inout'
sil hidden @assign_inout : $@convention(thin) <T> (@inout T, @in T) -> () {
bb0(%0 : $*T, %1 : $T):
%2 = copy_value %1 : $T
assign %2 to %0 : $*T
destroy_value %1 : $T
%5 = tuple ()
return %5 : $()
}
// Test trivial, guaranteed copyforwarding.
// ----
// CHECK-LABEL: sil hidden @diagnose_identity : $@convention(thin) <T> (@in T) -> @out T {
// CHECK: bb0(%0 : $T):
// CHECK: return %0 : $T
// CHECK-LABEL: } // end sil function 'diagnose_identity'
sil hidden @diagnose_identity : $@convention(thin) <T> (@in T) -> @out T {
bb0(%0 : $T):
%c = copy_value %0 : $T
destroy_value %0 : $T
return %c : $T
}

View File

@@ -0,0 +1,7 @@
// RUN: %target-sil-opt -O -enable-sil-opaque-values -emit-sorted-sil %s | %FileCheck %s
import Builtin
sil_stage raw
// CHECK-LABEL: sil_stage

View File

@@ -79,6 +79,10 @@ static llvm::cl::opt<bool>
EnableSILOwnershipOpt("enable-sil-ownership", EnableSILOwnershipOpt("enable-sil-ownership",
llvm::cl::desc("Compile the module with sil-ownership initially enabled for all functions")); llvm::cl::desc("Compile the module with sil-ownership initially enabled for all functions"));
static llvm::cl::opt<bool>
EnableSILOpaqueValues("enable-sil-opaque-values",
llvm::cl::desc("Compile the module with sil-opaque-values enabled."));
static llvm::cl::opt<std::string> static llvm::cl::opt<std::string>
ResourceDir("resource-dir", ResourceDir("resource-dir",
llvm::cl::desc("The directory that holds the compiler resource files")); llvm::cl::desc("The directory that holds the compiler resource files"));
@@ -238,6 +242,7 @@ int main(int argc, char **argv) {
ASTVerifierProcessCount; ASTVerifierProcessCount;
Invocation.getLangOptions().ASTVerifierProcessId = Invocation.getLangOptions().ASTVerifierProcessId =
ASTVerifierProcessId; ASTVerifierProcessId;
Invocation.getLangOptions().EnableSILOpaqueValues = EnableSILOpaqueValues;
// Setup the SIL Options. // Setup the SIL Options.
SILOptions &SILOpts = Invocation.getSILOptions(); SILOptions &SILOpts = Invocation.getSILOptions();