mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
SILGen: Emit literal closures at the abstraction level of their context.
Literal closures are only ever directly referenced in the context of the expression they're written in, so it's wasteful to emit them at their fully-substituted calling convention and then reabstract them if they're passed directly to a generic function. Avoid this by saving the abstraction pattern of the context before emitting the closure, and then lowering its main entry point's calling convention at that level of abstraction. Generalize some of the prolog/epilog code to handle converting arguments and returns to the correct representation for a different abstraction level.
This commit is contained in:
@@ -40,7 +40,8 @@ SILValue SILGenFunction::emitSelfDecl(VarDecl *selfDecl) {
|
||||
|
||||
namespace {
|
||||
class EmitBBArguments : public CanTypeVisitor<EmitBBArguments,
|
||||
/*RetTy*/ ManagedValue>
|
||||
/*RetTy*/ ManagedValue,
|
||||
/*ArgTys...*/ AbstractionPattern>
|
||||
{
|
||||
public:
|
||||
SILGenFunction &SGF;
|
||||
@@ -54,11 +55,11 @@ public:
|
||||
ArrayRef<SILParameterInfo> ¶meters)
|
||||
: SGF(sgf), parent(parent), loc(l), fnTy(fnTy), parameters(parameters) {}
|
||||
|
||||
ManagedValue visitType(CanType t) {
|
||||
return visitType(t, /*isInOut=*/false);
|
||||
ManagedValue visitType(CanType t, AbstractionPattern orig) {
|
||||
return visitType(t, orig, /*isInOut=*/false);
|
||||
}
|
||||
|
||||
ManagedValue visitType(CanType t, bool isInOut) {
|
||||
ManagedValue visitType(CanType t, AbstractionPattern orig, bool isInOut) {
|
||||
// The calling convention always uses minimal resilience expansion but
|
||||
// inside the function we lower/expand types in context of the current
|
||||
// function.
|
||||
@@ -67,8 +68,9 @@ public:
|
||||
SGF.SGM.Types.getLoweredType(t, TypeExpansionContext::minimal());
|
||||
argType = argType.getCategoryType(argTypeConv.getCategory());
|
||||
|
||||
if (isInOut)
|
||||
argType = SILType::getPrimitiveAddressType(argType.getASTType());
|
||||
if (isInOut
|
||||
|| orig.getParameterConvention(SGF.SGM.Types) == AbstractionPattern::Indirect)
|
||||
argType = argType.getCategoryType(SILValueCategory::Address);
|
||||
|
||||
// Pop the next parameter info.
|
||||
auto parameterInfo = parameters.front();
|
||||
@@ -79,18 +81,18 @@ public:
|
||||
ManagedValue mv = SGF.B.createInputFunctionArgument(
|
||||
paramType, loc.getAsASTNode<ValueDecl>());
|
||||
|
||||
// This is a hack to deal with the fact that Self.Type comes in as a static
|
||||
// metatype, but we have to downcast it to a dynamic Self metatype to get
|
||||
// the right semantics.
|
||||
if (argType != paramType) {
|
||||
// This is a hack to deal with the fact that Self.Type comes in as a
|
||||
// static metatype, but we have to downcast it to a dynamic Self
|
||||
// metatype to get the right semantics.
|
||||
assert(
|
||||
cast<DynamicSelfType>(
|
||||
argType.castTo<MetatypeType>().getInstanceType())
|
||||
.getSelfType()
|
||||
== paramType.castTo<MetatypeType>().getInstanceType());
|
||||
mv = SGF.B.createUncheckedBitCast(loc, mv, argType);
|
||||
if (auto argMetaTy = argType.getAs<MetatypeType>()) {
|
||||
if (auto argSelfTy = dyn_cast<DynamicSelfType>(argMetaTy.getInstanceType())) {
|
||||
assert(argSelfTy.getSelfType()
|
||||
== paramType.castTo<MetatypeType>().getInstanceType());
|
||||
mv = SGF.B.createUncheckedBitCast(loc, mv, argType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isInOut)
|
||||
return mv;
|
||||
|
||||
@@ -101,6 +103,12 @@ public:
|
||||
mv = SGF.B.createLoadTake(loc, mv);
|
||||
else
|
||||
mv = SGF.B.createLoadBorrow(loc, mv);
|
||||
argType = argType.getObjectType();
|
||||
}
|
||||
|
||||
if (argType != paramType) {
|
||||
// Reabstract the value if necessary.
|
||||
mv = SGF.emitOrigToSubstValue(loc, mv, orig, t);
|
||||
}
|
||||
|
||||
// If the value is a (possibly optional) ObjC block passed into the entry
|
||||
@@ -119,15 +127,20 @@ public:
|
||||
return mv;
|
||||
}
|
||||
|
||||
ManagedValue visitTupleType(CanTupleType t) {
|
||||
ManagedValue visitTupleType(CanTupleType t, AbstractionPattern orig) {
|
||||
// Only destructure if the abstraction pattern is also a tuple.
|
||||
if (!orig.isTuple())
|
||||
return visitType(t, orig);
|
||||
|
||||
SmallVector<ManagedValue, 4> elements;
|
||||
|
||||
auto &tl = SGF.SGM.Types.getTypeLowering(t, SGF.getTypeExpansionContext());
|
||||
bool canBeGuaranteed = tl.isLoadable();
|
||||
|
||||
// Collect the exploded elements.
|
||||
for (auto fieldType : t.getElementTypes()) {
|
||||
auto elt = visit(fieldType);
|
||||
for (unsigned i = 0, e = orig.getNumTupleElements(); i < e; ++i) {
|
||||
auto elt = visit(t.getElementType(i),
|
||||
orig.getTupleElementType(i));
|
||||
// If we can't borrow one of the elements as a guaranteed parameter, then
|
||||
// we have to +1 the tuple.
|
||||
if (elt.hasCleanup())
|
||||
@@ -191,11 +204,16 @@ struct ArgumentInitHelper {
|
||||
ArrayRef<SILParameterInfo> parameters;
|
||||
uint16_t ArgNo = 0;
|
||||
|
||||
ArgumentInitHelper(SILGenFunction &SGF, SILFunction &f)
|
||||
Optional<AbstractionPattern> OrigFnType;
|
||||
|
||||
ArgumentInitHelper(SILGenFunction &SGF, SILFunction &f,
|
||||
Optional<AbstractionPattern> origFnType)
|
||||
: SGF(SGF), f(f), initB(SGF.B),
|
||||
parameters(
|
||||
f.getLoweredFunctionTypeInContext(SGF.B.getTypeExpansionContext())
|
||||
->getParameters()) {}
|
||||
->getParameters()),
|
||||
OrigFnType(origFnType)
|
||||
{}
|
||||
|
||||
unsigned getNumArgs() const { return ArgNo; }
|
||||
|
||||
@@ -209,9 +227,12 @@ struct ArgumentInitHelper {
|
||||
f.getLoweredFunctionType(), parameters);
|
||||
|
||||
// Note: inouts of tuples are not exploded, so we bypass visit().
|
||||
AbstractionPattern origTy = OrigFnType
|
||||
? OrigFnType->getFunctionParamType(ArgNo - 1)
|
||||
: AbstractionPattern(canTy);
|
||||
if (isInOut)
|
||||
return argEmitter.visitType(canTy, /*isInOut=*/true);
|
||||
return argEmitter.visit(canTy);
|
||||
return argEmitter.visitType(canTy, origTy, /*isInOut=*/true);
|
||||
return argEmitter.visit(canTy, origTy);
|
||||
}
|
||||
|
||||
/// Create a SILArgument and store its value into the given Initialization,
|
||||
@@ -413,9 +434,10 @@ void SILGenFunction::emitProlog(CaptureInfo captureInfo,
|
||||
DeclContext *DC,
|
||||
Type resultType,
|
||||
bool throws,
|
||||
SourceLoc throwsLoc) {
|
||||
SourceLoc throwsLoc,
|
||||
Optional<AbstractionPattern> origClosureType) {
|
||||
uint16_t ArgNo = emitBasicProlog(paramList, selfParam, resultType,
|
||||
DC, throws, throwsLoc);
|
||||
DC, throws, throwsLoc, origClosureType);
|
||||
|
||||
// Emit the capture argument variables. These are placed last because they
|
||||
// become the first curry level of the SIL function.
|
||||
@@ -760,28 +782,40 @@ SILValue SILGenFunction::emitGetCurrentExecutor(SILLocation loc) {
|
||||
SubstitutionMap(), { });
|
||||
}
|
||||
|
||||
static void emitIndirectResultParameters(SILGenFunction &SGF, Type resultType,
|
||||
static void emitIndirectResultParameters(SILGenFunction &SGF,
|
||||
Type resultType,
|
||||
AbstractionPattern origResultType,
|
||||
DeclContext *DC) {
|
||||
// Expand tuples.
|
||||
if (auto tupleType = resultType->getAs<TupleType>()) {
|
||||
for (auto eltType : tupleType->getElementTypes()) {
|
||||
emitIndirectResultParameters(SGF, eltType, DC);
|
||||
if (origResultType.isTuple()) {
|
||||
auto tupleType = resultType->castTo<TupleType>();
|
||||
for (unsigned i = 0, e = origResultType.getNumTupleElements(); i < e; ++i) {
|
||||
emitIndirectResultParameters(SGF, tupleType->getElementType(i),
|
||||
origResultType.getTupleElementType(i),
|
||||
DC);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// If the return type is address-only, emit the indirect return argument.
|
||||
|
||||
// The calling convention always uses minimal resilience expansion.
|
||||
auto &resultTI =
|
||||
SGF.SGM.Types.getTypeLowering(DC->mapTypeIntoContext(resultType),
|
||||
SGF.SGM.Types.getTypeLowering(origResultType,
|
||||
DC->mapTypeIntoContext(resultType),
|
||||
SGF.getTypeExpansionContext());
|
||||
|
||||
// The calling convention always uses minimal resilience expansion.
|
||||
auto &resultTIConv = SGF.SGM.Types.getTypeLowering(
|
||||
DC->mapTypeIntoContext(resultType), TypeExpansionContext::minimal());
|
||||
auto resultConvType = resultTIConv.getLoweredType();
|
||||
|
||||
if (!SILModuleConventions::isReturnedIndirectlyInSIL(
|
||||
resultTIConv.getLoweredType(), SGF.SGM.M)) {
|
||||
return;
|
||||
// And the abstraction pattern may force an indirect return even if the
|
||||
// concrete type wouldn't normally be returned indirectly.
|
||||
if (!SILModuleConventions::isReturnedIndirectlyInSIL(resultConvType,
|
||||
SGF.SGM.M))
|
||||
|
||||
if (!SILModuleConventions(SGF.SGM.M).useLoweredAddresses()
|
||||
|| origResultType.getResultConvention(SGF.SGM.Types) != AbstractionPattern::Indirect) {
|
||||
return;
|
||||
}
|
||||
auto &ctx = SGF.getASTContext();
|
||||
auto var = new (ctx) ParamDecl(SourceLoc(), SourceLoc(),
|
||||
@@ -796,19 +830,25 @@ static void emitIndirectResultParameters(SILGenFunction &SGF, Type resultType,
|
||||
}
|
||||
|
||||
uint16_t SILGenFunction::emitBasicProlog(ParameterList *paramList,
|
||||
ParamDecl *selfParam,
|
||||
Type resultType,
|
||||
DeclContext *DC,
|
||||
bool throws,
|
||||
SourceLoc throwsLoc) {
|
||||
ParamDecl *selfParam,
|
||||
Type resultType,
|
||||
DeclContext *DC,
|
||||
bool throws,
|
||||
SourceLoc throwsLoc,
|
||||
Optional<AbstractionPattern> origClosureType) {
|
||||
// Create the indirect result parameters.
|
||||
auto genericSig = DC->getGenericSignatureOfContext();
|
||||
resultType = resultType->getCanonicalType(genericSig);
|
||||
|
||||
emitIndirectResultParameters(*this, resultType, DC);
|
||||
AbstractionPattern origResultType = origClosureType
|
||||
? origClosureType->getFunctionResultType()
|
||||
: AbstractionPattern(genericSig.getCanonicalSignature(),
|
||||
CanType(resultType));
|
||||
|
||||
emitIndirectResultParameters(*this, resultType, origResultType, DC);
|
||||
|
||||
// Emit the argument variables in calling convention order.
|
||||
ArgumentInitHelper emitter(*this, F);
|
||||
ArgumentInitHelper emitter(*this, F, origClosureType);
|
||||
|
||||
// Add the SILArguments and use them to initialize the local argument
|
||||
// values.
|
||||
|
||||
Reference in New Issue
Block a user