mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[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:
@@ -2710,6 +2710,9 @@ enum class ParameterConvention {
|
||||
/// guarantees its validity for the entirety of the call.
|
||||
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
|
||||
// SILFunctionType's formal (immutable) conventions, as opposed to the transient
|
||||
|
||||
@@ -163,6 +163,11 @@ namespace swift {
|
||||
/// and methods.
|
||||
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
|
||||
/// to match.
|
||||
///
|
||||
|
||||
@@ -248,6 +248,9 @@ def enable_sil_ownership : Flag<["-"], "enable-sil-ownership">,
|
||||
def assume_parsing_unqualified_ownership_sil : Flag<["-"], "assume-parsing-unqualified-ownership-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 :
|
||||
Flag<["-"], "enable-experimental-property-behaviors">,
|
||||
HelpText<"Enable experimental property behaviors">;
|
||||
|
||||
@@ -469,7 +469,8 @@ public:
|
||||
assert((Qualifier == LoadOwnershipQualifier::Unqualified) ||
|
||||
F.hasQualifiedOwnership() &&
|
||||
"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())
|
||||
LoadInst(getSILDebugLocation(Loc), LV, Qualifier));
|
||||
}
|
||||
|
||||
@@ -576,6 +576,8 @@ public:
|
||||
|
||||
SILFunctionConventions getFunctionConventions(CanSILFunctionType funcTy);
|
||||
|
||||
bool useLoweredAddresses() const { return loweredAddresses; }
|
||||
|
||||
bool isSILIndirect(SILParameterInfo param) const {
|
||||
return isIndirectSILParam(param, loweredAddresses);
|
||||
}
|
||||
@@ -612,6 +614,8 @@ public:
|
||||
// SILModuleConventions API for convenience.
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
||||
bool useLoweredAddresses() const { return silConv.useLoweredAddresses(); }
|
||||
|
||||
bool isSILIndirect(SILParameterInfo param) const {
|
||||
return silConv.isSILIndirect(param);
|
||||
}
|
||||
|
||||
@@ -170,6 +170,11 @@ public:
|
||||
return LoweredType;
|
||||
}
|
||||
|
||||
/// Returns true if the SIL type is an address.
|
||||
bool isAddress() const {
|
||||
return LoweredType.isAddress();
|
||||
}
|
||||
|
||||
/// Return the semantic type.
|
||||
///
|
||||
/// The semantic type is what a type pretends to be during
|
||||
|
||||
@@ -944,6 +944,8 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
|
||||
= 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
|
||||
// platform conditions.
|
||||
bool UnsupportedOS, UnsupportedArch;
|
||||
|
||||
@@ -612,7 +612,7 @@ SILResultInfo::getOwnershipKind(SILModule &M,
|
||||
}
|
||||
|
||||
SILModuleConventions::SILModuleConventions(const SILModule &M)
|
||||
: loweredAddresses(true) {}
|
||||
: loweredAddresses(!M.getASTContext().LangOpts.EnableSILOpaqueValues) {}
|
||||
|
||||
bool SILModuleConventions::isReturnedIndirectlyInSIL(SILType type,
|
||||
SILModule &M) {
|
||||
|
||||
@@ -79,7 +79,15 @@ ValueOwnershipKind::ValueOwnershipKind(SILModule &M, SILType Type,
|
||||
: Value() {
|
||||
switch (Convention) {
|
||||
case SILArgumentConvention::Indirect_In:
|
||||
Value = SILModuleConventions(M).useLoweredAddresses()
|
||||
? ValueOwnershipKind::Trivial
|
||||
: ValueOwnershipKind::Owned;
|
||||
break;
|
||||
case SILArgumentConvention::Indirect_In_Guaranteed:
|
||||
Value = SILModuleConventions(M).useLoweredAddresses()
|
||||
? ValueOwnershipKind::Trivial
|
||||
: ValueOwnershipKind::Guaranteed;
|
||||
break;
|
||||
case SILArgumentConvention::Indirect_Inout:
|
||||
case SILArgumentConvention::Indirect_InoutAliasable:
|
||||
case SILArgumentConvention::Indirect_Out:
|
||||
|
||||
@@ -103,6 +103,7 @@ namespace {
|
||||
class SILVerifier : public SILVerifierBase<SILVerifier> {
|
||||
ModuleDecl *M;
|
||||
const SILFunction &F;
|
||||
SILFunctionConventions fnConv;
|
||||
Lowering::TypeConverter &TC;
|
||||
SILOpenedArchetypesTracker OpenedArchetypes;
|
||||
const SILInstruction *CurInstruction = nullptr;
|
||||
@@ -409,8 +410,9 @@ public:
|
||||
}
|
||||
|
||||
SILVerifier(const SILFunction &F, bool SingleFunction = true)
|
||||
: M(F.getModule().getSwiftModule()), F(F), TC(F.getModule().Types),
|
||||
OpenedArchetypes(F), Dominance(nullptr),
|
||||
: M(F.getModule().getSwiftModule()), F(F),
|
||||
fnConv(F.getLoweredFunctionType(), F.getModule()),
|
||||
TC(F.getModule().Types), OpenedArchetypes(F), Dominance(nullptr),
|
||||
SingleFunction(SingleFunction) {
|
||||
if (F.isExternalDeclaration())
|
||||
return;
|
||||
@@ -847,13 +849,13 @@ public:
|
||||
"substituted callee type does not match substitutions");
|
||||
|
||||
// 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.getNumCallArguments() == fnConv.getNumSILArguments(),
|
||||
require(site.getNumCallArguments() == substConv.getNumSILArguments(),
|
||||
"apply doesn't have right number of arguments for function");
|
||||
for (size_t i = 0, size = site.getNumCallArguments(); i < size; ++i) {
|
||||
requireSameType(site.getArguments()[i]->getType(),
|
||||
fnConv.getSILArgumentType(i),
|
||||
substConv.getSILArgumentType(i),
|
||||
"operand of 'apply' doesn't match function input type");
|
||||
}
|
||||
}
|
||||
@@ -861,14 +863,14 @@ public:
|
||||
void checkApplyInst(ApplyInst *AI) {
|
||||
checkFullApplySite(AI);
|
||||
|
||||
SILFunctionConventions fnConv(AI->getSubstCalleeType(), F.getModule());
|
||||
require(AI->getType() == fnConv.getSILResultType(),
|
||||
SILFunctionConventions calleeConv(AI->getSubstCalleeType(), F.getModule());
|
||||
require(AI->getType() == calleeConv.getSILResultType(),
|
||||
"type of apply instruction doesn't match function result type");
|
||||
if (AI->isNonThrowing()) {
|
||||
require(fnConv.funcTy->hasErrorResult(),
|
||||
require(calleeConv.funcTy->hasErrorResult(),
|
||||
"nothrow flag used for callee without error result");
|
||||
} else {
|
||||
require(!fnConv.funcTy->hasErrorResult(),
|
||||
require(!calleeConv.funcTy->hasErrorResult(),
|
||||
"apply instruction cannot call function with error result");
|
||||
}
|
||||
|
||||
@@ -884,23 +886,23 @@ public:
|
||||
void checkTryApplyInst(TryApplyInst *AI) {
|
||||
checkFullApplySite(AI);
|
||||
|
||||
SILFunctionConventions fnConv(AI->getSubstCalleeType(), F.getModule());
|
||||
SILFunctionConventions calleeConv(AI->getSubstCalleeType(), F.getModule());
|
||||
|
||||
auto normalBB = AI->getNormalBB();
|
||||
require(normalBB->args_size() == 1,
|
||||
"normal destination of try_apply must take one argument");
|
||||
requireSameType((*normalBB->args_begin())->getType(),
|
||||
fnConv.getSILResultType(),
|
||||
calleeConv.getSILResultType(),
|
||||
"normal destination of try_apply must take argument "
|
||||
"of normal result type");
|
||||
|
||||
auto errorBB = AI->getErrorBB();
|
||||
require(fnConv.funcTy->hasErrorResult(),
|
||||
require(calleeConv.funcTy->hasErrorResult(),
|
||||
"try_apply must call function with error result");
|
||||
require(errorBB->args_size() == 1,
|
||||
"error destination of try_apply must take one argument");
|
||||
requireSameType((*errorBB->args_begin())->getType(),
|
||||
fnConv.getSILErrorType(),
|
||||
calleeConv.getSILErrorType(),
|
||||
"error destination of try_apply must take argument "
|
||||
"of error result type");
|
||||
}
|
||||
@@ -1126,7 +1128,8 @@ public:
|
||||
|
||||
void checkLoadInst(LoadInst *LI) {
|
||||
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");
|
||||
require(LI->getOperand()->getType().isAddress(),
|
||||
"Load operand must be an address");
|
||||
@@ -1188,7 +1191,8 @@ public:
|
||||
void checkStoreInst(StoreInst *SI) {
|
||||
require(SI->getSrc()->getType().isObject(),
|
||||
"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");
|
||||
require(SI->getDest()->getType().isAddress(),
|
||||
"Must store to an address dest");
|
||||
@@ -1322,8 +1326,7 @@ public:
|
||||
MU->getSetterSubstitutions());
|
||||
require(SubstSetterTy->getParameters().size() == 2,
|
||||
"mark_uninitialized setter must have a value and self param");
|
||||
auto silConv = SILModuleConventions(F.getModule());
|
||||
requireSameType(silConv.getSILType(SubstSetterTy->getSelfParameter()),
|
||||
requireSameType(fnConv.getSILType(SubstSetterTy->getSelfParameter()),
|
||||
MU->getSelf()->getType(),
|
||||
"self type must match setter's self parameter type");
|
||||
|
||||
@@ -1366,7 +1369,7 @@ public:
|
||||
void checkCopyValueInst(CopyValueInst *I) {
|
||||
require(I->getOperand()->getType().isObject(),
|
||||
"Source value should be an object value");
|
||||
require(F.hasQualifiedOwnership(),
|
||||
require(!fnConv.useLoweredAddresses() || F.hasQualifiedOwnership(),
|
||||
"copy_value is only valid in functions with qualified "
|
||||
"ownership");
|
||||
}
|
||||
@@ -1384,7 +1387,7 @@ public:
|
||||
void checkDestroyValueInst(DestroyValueInst *I) {
|
||||
require(I->getOperand()->getType().isObject(),
|
||||
"Source value should be an object value");
|
||||
require(F.hasQualifiedOwnership(),
|
||||
require(!fnConv.useLoweredAddresses() || F.hasQualifiedOwnership(),
|
||||
"destroy_value is only valid in functions with qualified "
|
||||
"ownership");
|
||||
}
|
||||
@@ -1912,8 +1915,7 @@ public:
|
||||
}
|
||||
|
||||
SILType getMethodSelfType(CanSILFunctionType ft) {
|
||||
auto silConv = SILModuleConventions(F.getModule());
|
||||
return silConv.getSILType(ft->getParameters().back());
|
||||
return fnConv.getSILType(ft->getParameters().back());
|
||||
}
|
||||
|
||||
void checkWitnessMethodInst(WitnessMethodInst *AMI) {
|
||||
@@ -2798,7 +2800,7 @@ public:
|
||||
DEBUG(RI->print(llvm::dbgs()));
|
||||
|
||||
SILType functionResultType =
|
||||
F.mapTypeIntoContext(F.getConventions().getSILResultType());
|
||||
F.mapTypeIntoContext(fnConv.getSILResultType());
|
||||
SILType instResultType = RI->getOperand()->getType();
|
||||
DEBUG(llvm::dbgs() << "function return type: ";
|
||||
functionResultType.dump();
|
||||
@@ -2815,8 +2817,7 @@ public:
|
||||
require(fnType->hasErrorResult(),
|
||||
"throw in function that doesn't have an error result");
|
||||
|
||||
SILType functionResultType =
|
||||
F.mapTypeIntoContext(F.getConventions().getSILErrorType());
|
||||
SILType functionResultType = F.mapTypeIntoContext(fnConv.getSILErrorType());
|
||||
SILType instResultType = TI->getOperand()->getType();
|
||||
DEBUG(llvm::dbgs() << "function error result type: ";
|
||||
functionResultType.dump();
|
||||
@@ -3248,7 +3249,6 @@ public:
|
||||
}
|
||||
|
||||
void verifyEntryPointArguments(SILBasicBlock *entry) {
|
||||
auto fnConv = F.getConventions();
|
||||
DEBUG(llvm::dbgs() << "Argument types for entry point BB:\n";
|
||||
for (auto *arg
|
||||
: make_range(entry->args_begin(), entry->args_end()))
|
||||
@@ -3267,14 +3267,13 @@ public:
|
||||
bool matched = true;
|
||||
auto argI = entry->args_begin();
|
||||
SILModule &M = F.getModule();
|
||||
auto funcConv = F.getConventions();
|
||||
|
||||
auto check = [&](const char *what, SILType ty) {
|
||||
auto mappedTy = F.mapTypeIntoContext(ty);
|
||||
SILArgument *bbarg = *argI;
|
||||
++argI;
|
||||
auto ownershipkind = ValueOwnershipKind(
|
||||
M, mappedTy, funcConv.getSILArgumentConvention(bbarg->getIndex()));
|
||||
M, mappedTy, fnConv.getSILArgumentConvention(bbarg->getIndex()));
|
||||
if (bbarg->getType() != mappedTy) {
|
||||
llvm::errs() << what << " type mismatch!\n";
|
||||
llvm::errs() << " argument: "; bbarg->dump();
|
||||
|
||||
@@ -487,12 +487,15 @@ bool SILType::isAddressOnly(CanType type, SILModule &M,
|
||||
}
|
||||
|
||||
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 {
|
||||
protected:
|
||||
LoadableTypeLowering(SILType type, IsTrivial_t isTrivial,
|
||||
IsAddressOnly_t isAddressOnly,
|
||||
IsReferenceCounted_t isRefCounted)
|
||||
: TypeLowering(type, isTrivial, IsNotAddressOnly, isRefCounted) {}
|
||||
: TypeLowering(type, isTrivial, isAddressOnly, isRefCounted) {}
|
||||
|
||||
public:
|
||||
void emitDestroyAddress(SILBuilder &B, SILLocation loc,
|
||||
@@ -518,7 +521,8 @@ namespace {
|
||||
class TrivialTypeLowering final : public LoadableTypeLowering {
|
||||
public:
|
||||
TrivialTypeLowering(SILType type)
|
||||
: LoadableTypeLowering(type, IsTrivial, IsNotReferenceCounted) {}
|
||||
: LoadableTypeLowering(type, IsTrivial, IsNotAddressOnly,
|
||||
IsNotReferenceCounted) {}
|
||||
|
||||
SILValue emitLoadOfCopy(SILBuilder &B, SILLocation loc, SILValue addr,
|
||||
IsTake_t isTake) const override {
|
||||
@@ -579,8 +583,9 @@ namespace {
|
||||
class NonTrivialLoadableTypeLowering : public LoadableTypeLowering {
|
||||
public:
|
||||
NonTrivialLoadableTypeLowering(SILType type,
|
||||
IsAddressOnly_t isAddressOnly,
|
||||
IsReferenceCounted_t isRefCounted)
|
||||
: LoadableTypeLowering(type, IsNotTrivial, isRefCounted) {}
|
||||
: LoadableTypeLowering(type, IsNotTrivial, isAddressOnly, isRefCounted) {}
|
||||
|
||||
SILValue emitLoadOfCopy(SILBuilder &B, SILLocation loc,
|
||||
SILValue addr, IsTake_t isTake) const override {
|
||||
@@ -671,6 +676,7 @@ namespace {
|
||||
public:
|
||||
LoadableAggTypeLowering(CanType type)
|
||||
: NonTrivialLoadableTypeLowering(SILType::getPrimitiveObjectType(type),
|
||||
IsNotAddressOnly,
|
||||
IsNotReferenceCounted) {
|
||||
}
|
||||
|
||||
@@ -849,6 +855,7 @@ namespace {
|
||||
public:
|
||||
LoadableEnumTypeLowering(CanType type)
|
||||
: NonTrivialLoadableTypeLowering(SILType::getPrimitiveObjectType(type),
|
||||
IsNotAddressOnly,
|
||||
IsNotReferenceCounted) {}
|
||||
|
||||
SILValue emitCopyValue(SILBuilder &B, SILLocation loc,
|
||||
@@ -892,8 +899,10 @@ namespace {
|
||||
|
||||
class LeafLoadableTypeLowering : public NonTrivialLoadableTypeLowering {
|
||||
public:
|
||||
LeafLoadableTypeLowering(SILType type, IsReferenceCounted_t isRefCounted)
|
||||
: NonTrivialLoadableTypeLowering(type, isRefCounted) {}
|
||||
LeafLoadableTypeLowering(SILType type,
|
||||
IsAddressOnly_t isAddressOnly,
|
||||
IsReferenceCounted_t isRefCounted)
|
||||
: NonTrivialLoadableTypeLowering(type, isAddressOnly, isRefCounted) {}
|
||||
|
||||
SILValue emitLoweredCopyValue(SILBuilder &B, SILLocation loc,
|
||||
SILValue value,
|
||||
@@ -912,7 +921,7 @@ namespace {
|
||||
class ReferenceTypeLowering : public LeafLoadableTypeLowering {
|
||||
public:
|
||||
ReferenceTypeLowering(SILType type)
|
||||
: LeafLoadableTypeLowering(type, IsReferenceCounted) {}
|
||||
: LeafLoadableTypeLowering(type, IsNotAddressOnly, IsReferenceCounted) {}
|
||||
|
||||
SILValue emitCopyValue(SILBuilder &B, SILLocation loc,
|
||||
SILValue value) const override {
|
||||
@@ -940,7 +949,7 @@ namespace {
|
||||
class LoadableUnownedTypeLowering final : public LeafLoadableTypeLowering {
|
||||
public:
|
||||
LoadableUnownedTypeLowering(SILType type)
|
||||
: LeafLoadableTypeLowering(type, IsReferenceCounted) {}
|
||||
: LeafLoadableTypeLowering(type, IsNotAddressOnly, IsReferenceCounted) {}
|
||||
|
||||
SILValue emitCopyValue(SILBuilder &B, SILLocation loc,
|
||||
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,
|
||||
/// which is assumed to already have been lowered.
|
||||
class LowerType
|
||||
@@ -1075,8 +1158,12 @@ namespace {
|
||||
}
|
||||
|
||||
const TypeLowering *handleAddressOnly(CanType type) {
|
||||
auto silType = SILType::getPrimitiveAddressType(type);
|
||||
return new (TC, Dependent) AddressOnlyTypeLowering(silType);
|
||||
if (SILModuleConventions(M).useLoweredAddresses()) {
|
||||
auto silType = SILType::getPrimitiveAddressType(type);
|
||||
return new (TC, Dependent) AddressOnlyTypeLowering(silType);
|
||||
}
|
||||
auto silType = SILType::getPrimitiveObjectType(type);
|
||||
return new (TC, Dependent) OpaqueValueTypeLowering(silType);
|
||||
}
|
||||
|
||||
const TypeLowering *
|
||||
|
||||
@@ -64,7 +64,7 @@ ManagedValue ManagedValue::copyUnmanaged(SILGenFunction &gen, SILLocation loc) {
|
||||
return *this;
|
||||
|
||||
SILValue result;
|
||||
if (!lowering.isAddressOnly()) {
|
||||
if (!lowering.isAddress()) {
|
||||
result = lowering.emitCopyValue(gen.B, loc, getValue());
|
||||
} else {
|
||||
result = gen.emitTemporaryAllocation(loc, getType());
|
||||
|
||||
@@ -694,9 +694,10 @@ static Callee prepareArchetypeCallee(SILGenFunction &gen, SILLocation loc,
|
||||
!cast<ArchetypeType>(selfValue.getSubstRValueType())->requiresClass())
|
||||
return;
|
||||
|
||||
auto selfParameter = getSelfParameter();
|
||||
assert(gen.silConv.isSILIndirect(selfParameter));
|
||||
(void)selfParameter;
|
||||
assert(gen.silConv.useLoweredAddresses()
|
||||
== gen.silConv.isSILIndirect(getSelfParameter()));
|
||||
if (!gen.silConv.useLoweredAddresses())
|
||||
return;
|
||||
|
||||
SILLocation selfLoc = selfValue.getLocation();
|
||||
|
||||
|
||||
@@ -2343,7 +2343,8 @@ void SILGenFunction::emitSemanticStore(SILLocation loc,
|
||||
|
||||
// Easy case: the types match.
|
||||
if (rvalue->getType() == destTL.getLoweredType()) {
|
||||
assert(destTL.isAddressOnly() == rvalue->getType().isAddress());
|
||||
assert(!silConv.useLoweredAddresses()
|
||||
|| (destTL.isAddressOnly() == rvalue->getType().isAddress()));
|
||||
if (rvalue->getType().isAddress()) {
|
||||
B.createCopyAddr(loc, rvalue, dest, IsTake, isInit);
|
||||
} else {
|
||||
|
||||
@@ -730,6 +730,19 @@ static ManagedValue manageParam(SILGenFunction &gen,
|
||||
SILParameterInfo info,
|
||||
bool allowPlusZero) {
|
||||
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:
|
||||
if (allowPlusZero)
|
||||
return ManagedValue::forUnmanaged(paramValue);
|
||||
@@ -743,21 +756,14 @@ static ManagedValue manageParam(SILGenFunction &gen,
|
||||
case ParameterConvention::Direct_Owned:
|
||||
return gen.emitManagedRValueWithCleanup(paramValue);
|
||||
|
||||
case ParameterConvention::Indirect_In_Guaranteed:
|
||||
// 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);
|
||||
}
|
||||
case ParameterConvention::Indirect_In:
|
||||
if (gen.silConv.useLoweredAddresses())
|
||||
return gen.emitManagedBufferWithCleanup(paramValue);
|
||||
return gen.emitManagedRValueWithCleanup(paramValue);
|
||||
|
||||
case ParameterConvention::Indirect_Inout:
|
||||
case ParameterConvention::Indirect_InoutAliasable:
|
||||
return ManagedValue::forLValue(paramValue);
|
||||
case ParameterConvention::Indirect_In:
|
||||
return gen.emitManagedBufferWithCleanup(paramValue);
|
||||
}
|
||||
llvm_unreachable("bad parameter convention");
|
||||
}
|
||||
|
||||
62
test/SIL/Parser/opaque_values_parse.sil
Normal file
62
test/SIL/Parser/opaque_values_parse.sil
Normal 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'
|
||||
49
test/SIL/Serialization/opaque_values_serialize.sil
Normal file
49
test/SIL/Serialization/opaque_values_serialize.sil
Normal 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'
|
||||
69
test/SILGen/opaque_values_silgen.swift
Normal file
69
test/SILGen/opaque_values_silgen.swift
Normal 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() {}
|
||||
}
|
||||
18
test/SILGen/opaque_values_silgen_todo.swift
Normal file
18
test/SILGen/opaque_values_silgen_todo.swift
Normal 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
|
||||
}
|
||||
38
test/SILOptimizer/opaque_values_mandatory.sil
Normal file
38
test/SILOptimizer/opaque_values_mandatory.sil
Normal 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
|
||||
}
|
||||
7
test/SILOptimizer/opaque_values_opt.sil
Normal file
7
test/SILOptimizer/opaque_values_opt.sil
Normal 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
|
||||
@@ -79,6 +79,10 @@ static llvm::cl::opt<bool>
|
||||
EnableSILOwnershipOpt("enable-sil-ownership",
|
||||
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>
|
||||
ResourceDir("resource-dir",
|
||||
llvm::cl::desc("The directory that holds the compiler resource files"));
|
||||
@@ -238,6 +242,7 @@ int main(int argc, char **argv) {
|
||||
ASTVerifierProcessCount;
|
||||
Invocation.getLangOptions().ASTVerifierProcessId =
|
||||
ASTVerifierProcessId;
|
||||
Invocation.getLangOptions().EnableSILOpaqueValues = EnableSILOpaqueValues;
|
||||
|
||||
// Setup the SIL Options.
|
||||
SILOptions &SILOpts = Invocation.getSILOptions();
|
||||
|
||||
Reference in New Issue
Block a user