mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
525 lines
19 KiB
C++
525 lines
19 KiB
C++
//===--- SILGenProlog.cpp - Function prologue emission --------------------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "SILGenFunction.h"
|
|
#include "Initialization.h"
|
|
#include "ManagedValue.h"
|
|
#include "Scope.h"
|
|
#include "swift/SIL/SILArgument.h"
|
|
#include "swift/AST/GenericEnvironment.h"
|
|
#include "swift/AST/ParameterList.h"
|
|
|
|
using namespace swift;
|
|
using namespace Lowering;
|
|
|
|
SILValue SILGenFunction::emitSelfDecl(VarDecl *selfDecl) {
|
|
// Emit the implicit 'self' argument.
|
|
SILType selfType = getLoweredLoadableType(selfDecl->getType());
|
|
SILValue selfValue = F.begin()->createFunctionArgument(selfType, selfDecl);
|
|
VarLocs[selfDecl] = VarLoc::get(selfValue);
|
|
SILLocation PrologueLoc(selfDecl);
|
|
PrologueLoc.markAsPrologue();
|
|
uint16_t ArgNo = 1; // Hardcoded for destructors.
|
|
B.createDebugValue(PrologueLoc, selfValue,
|
|
SILDebugVariable(selfDecl->isLet(), ArgNo));
|
|
return selfValue;
|
|
}
|
|
|
|
namespace {
|
|
class EmitBBArguments : public CanTypeVisitor<EmitBBArguments,
|
|
/*RetTy*/ ManagedValue>
|
|
{
|
|
public:
|
|
SILGenFunction &SGF;
|
|
SILBasicBlock *parent;
|
|
SILLocation loc;
|
|
CanSILFunctionType fnTy;
|
|
ArrayRef<SILParameterInfo> ¶meters;
|
|
|
|
EmitBBArguments(SILGenFunction &sgf, SILBasicBlock *parent, SILLocation l,
|
|
CanSILFunctionType fnTy,
|
|
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, bool isInOut) {
|
|
// The calling convention always uses minimal resilience expansion but
|
|
// inside the function we lower/expand types in context of the current
|
|
// function.
|
|
auto argType = SGF.SGM.Types.getLoweredType(t, SGF.getTypeExpansionContext());
|
|
auto argTypeConv =
|
|
SGF.SGM.Types.getLoweredType(t, TypeExpansionContext::minimal());
|
|
argType = argType.getCategoryType(argTypeConv.getCategory());
|
|
|
|
if (isInOut)
|
|
argType = SILType::getPrimitiveAddressType(argType.getASTType());
|
|
|
|
// Pop the next parameter info.
|
|
auto parameterInfo = parameters.front();
|
|
parameters = parameters.slice(1);
|
|
|
|
auto paramType =
|
|
SGF.F.mapTypeIntoContext(SGF.getSILType(parameterInfo, fnTy));
|
|
ManagedValue mv = SGF.B.createInputFunctionArgument(
|
|
paramType, loc.getAsASTNode<ValueDecl>());
|
|
|
|
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 (isInOut)
|
|
return mv;
|
|
|
|
// This can happen if the value is resilient in the calling convention
|
|
// but not resilient locally.
|
|
if (argType.isLoadable(SGF.F) && argType.isAddress()) {
|
|
if (mv.isPlusOne(SGF))
|
|
mv = SGF.B.createLoadTake(loc, mv);
|
|
else
|
|
mv = SGF.B.createLoadBorrow(loc, mv);
|
|
}
|
|
|
|
// If the value is a (possibly optional) ObjC block passed into the entry
|
|
// point of the function, then copy it so we can treat the value reliably
|
|
// as a heap object. Escape analysis can eliminate this copy if it's
|
|
// unneeded during optimization.
|
|
CanType objectType = t;
|
|
if (auto theObjTy = t.getOptionalObjectType())
|
|
objectType = theObjTy;
|
|
if (isa<FunctionType>(objectType) &&
|
|
cast<FunctionType>(objectType)->getRepresentation()
|
|
== FunctionType::Representation::Block) {
|
|
SILValue blockCopy = SGF.B.createCopyBlock(loc, mv.getValue());
|
|
mv = SGF.emitManagedRValueWithCleanup(blockCopy);
|
|
}
|
|
return mv;
|
|
}
|
|
|
|
ManagedValue visitTupleType(CanTupleType t) {
|
|
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);
|
|
// If we can't borrow one of the elements as a guaranteed parameter, then
|
|
// we have to +1 the tuple.
|
|
if (elt.hasCleanup())
|
|
canBeGuaranteed = false;
|
|
elements.push_back(elt);
|
|
}
|
|
|
|
if (tl.isLoadable() || !SGF.silConv.useLoweredAddresses()) {
|
|
SmallVector<SILValue, 4> elementValues;
|
|
if (canBeGuaranteed) {
|
|
// If all of the elements were guaranteed, we can form a guaranteed tuple.
|
|
for (auto element : elements)
|
|
elementValues.push_back(element.getUnmanagedValue());
|
|
} else {
|
|
// Otherwise, we need to move or copy values into a +1 tuple.
|
|
for (auto element : elements) {
|
|
SILValue value = element.hasCleanup()
|
|
? element.forward(SGF)
|
|
: element.copyUnmanaged(SGF, loc).forward(SGF);
|
|
elementValues.push_back(value);
|
|
}
|
|
}
|
|
auto tupleValue = SGF.B.createTuple(loc, tl.getLoweredType(),
|
|
elementValues);
|
|
return canBeGuaranteed
|
|
? ManagedValue::forUnmanaged(tupleValue)
|
|
: SGF.emitManagedRValueWithCleanup(tupleValue);
|
|
} else {
|
|
// If the type is address-only, we need to move or copy the elements into
|
|
// a tuple in memory.
|
|
// TODO: It would be a bit more efficient to use a preallocated buffer
|
|
// in this case.
|
|
auto buffer = SGF.emitTemporaryAllocation(loc, tl.getLoweredType());
|
|
for (auto i : indices(elements)) {
|
|
auto element = elements[i];
|
|
auto elementBuffer = SGF.B.createTupleElementAddr(loc, buffer,
|
|
i, element.getType().getAddressType());
|
|
if (element.hasCleanup())
|
|
element.forwardInto(SGF, loc, elementBuffer);
|
|
else
|
|
element.copyInto(SGF, loc, elementBuffer);
|
|
}
|
|
return SGF.emitManagedRValueWithCleanup(buffer);
|
|
}
|
|
}
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
|
|
namespace {
|
|
|
|
/// A helper for creating SILArguments and binding variables to the argument
|
|
/// names.
|
|
struct ArgumentInitHelper {
|
|
SILGenFunction &SGF;
|
|
SILFunction &f;
|
|
SILGenBuilder &initB;
|
|
|
|
/// An ArrayRef that we use in our SILParameterList queue. Parameters are
|
|
/// sliced off of the front as they're emitted.
|
|
ArrayRef<SILParameterInfo> parameters;
|
|
uint16_t ArgNo = 0;
|
|
|
|
ArgumentInitHelper(SILGenFunction &SGF, SILFunction &f)
|
|
: SGF(SGF), f(f), initB(SGF.B),
|
|
parameters(
|
|
f.getLoweredFunctionTypeInContext(SGF.B.getTypeExpansionContext())
|
|
->getParameters()) {}
|
|
|
|
unsigned getNumArgs() const { return ArgNo; }
|
|
|
|
ManagedValue makeArgument(Type ty, bool isInOut, SILBasicBlock *parent,
|
|
SILLocation l) {
|
|
assert(ty && "no type?!");
|
|
|
|
// Create an RValue by emitting destructured arguments into a basic block.
|
|
CanType canTy = ty->getCanonicalType();
|
|
EmitBBArguments argEmitter(SGF, parent, l,
|
|
f.getLoweredFunctionType(), parameters);
|
|
|
|
// Note: inouts of tuples are not exploded, so we bypass visit().
|
|
if (isInOut)
|
|
return argEmitter.visitType(canTy, /*isInOut=*/true);
|
|
return argEmitter.visit(canTy);
|
|
}
|
|
|
|
/// Create a SILArgument and store its value into the given Initialization,
|
|
/// if not null.
|
|
void makeArgumentIntoBinding(Type ty, SILBasicBlock *parent, ParamDecl *pd) {
|
|
SILLocation loc(pd);
|
|
loc.markAsPrologue();
|
|
|
|
ManagedValue argrv = makeArgument(ty, pd->isInOut(), parent, loc);
|
|
|
|
if (pd->isInOut()) {
|
|
assert(argrv.getType().isAddress() && "expected inout to be address");
|
|
} else {
|
|
assert(pd->isImmutable() && "expected parameter to be immutable!");
|
|
// If the variable is immutable, we can bind the value as is.
|
|
// Leave the cleanup on the argument, if any, in place to consume the
|
|
// argument if we're responsible for it.
|
|
}
|
|
SGF.VarLocs[pd] = SILGenFunction::VarLoc::get(argrv.getValue());
|
|
SILValue value = argrv.getValue();
|
|
SILDebugVariable varinfo(pd->isImmutable(), ArgNo);
|
|
if (!argrv.getType().isAddress()) {
|
|
SGF.B.createDebugValue(loc, value, varinfo);
|
|
} else {
|
|
if (auto AllocStack = dyn_cast<AllocStackInst>(value))
|
|
AllocStack->setArgNo(ArgNo);
|
|
else
|
|
SGF.B.createDebugValueAddr(loc, value, varinfo);
|
|
}
|
|
}
|
|
|
|
void emitParam(ParamDecl *PD) {
|
|
auto type = PD->getType();
|
|
|
|
assert(type->isMaterializable());
|
|
|
|
++ArgNo;
|
|
if (PD->hasName()) {
|
|
makeArgumentIntoBinding(type, &*f.begin(), PD);
|
|
return;
|
|
}
|
|
|
|
emitAnonymousParam(type, PD, PD);
|
|
}
|
|
|
|
void emitAnonymousParam(Type type, SILLocation paramLoc, ParamDecl *PD) {
|
|
// A value bound to _ is unused and can be immediately released.
|
|
Scope discardScope(SGF.Cleanups, CleanupLocation(PD));
|
|
|
|
// Manage the parameter.
|
|
auto argrv = makeArgument(type, PD->isInOut(), &*f.begin(), paramLoc);
|
|
|
|
// Emit debug information for the argument.
|
|
SILLocation loc(PD);
|
|
loc.markAsPrologue();
|
|
if (argrv.getType().isAddress())
|
|
SGF.B.createDebugValueAddr(loc, argrv.getValue(),
|
|
SILDebugVariable(PD->isLet(), ArgNo));
|
|
else
|
|
SGF.B.createDebugValue(loc, argrv.getValue(),
|
|
SILDebugVariable(PD->isLet(), ArgNo));
|
|
}
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
|
|
static void makeArgument(Type ty, ParamDecl *decl,
|
|
SmallVectorImpl<SILValue> &args, SILGenFunction &SGF) {
|
|
assert(ty && "no type?!");
|
|
|
|
// Destructure tuple value arguments.
|
|
if (TupleType *tupleTy = decl->isInOut() ? nullptr : ty->getAs<TupleType>()) {
|
|
for (auto fieldType : tupleTy->getElementTypes())
|
|
makeArgument(fieldType, decl, args, SGF);
|
|
} else {
|
|
auto loweredTy = SGF.getLoweredTypeForFunctionArgument(ty);
|
|
if (decl->isInOut())
|
|
loweredTy = SILType::getPrimitiveAddressType(loweredTy.getASTType());
|
|
auto arg = SGF.F.begin()->createFunctionArgument(loweredTy, decl);
|
|
args.push_back(arg);
|
|
}
|
|
}
|
|
|
|
|
|
void SILGenFunction::bindParameterForForwarding(ParamDecl *param,
|
|
SmallVectorImpl<SILValue> ¶meters) {
|
|
makeArgument(param->getType(), param, parameters, *this);
|
|
}
|
|
|
|
void SILGenFunction::bindParametersForForwarding(const ParameterList *params,
|
|
SmallVectorImpl<SILValue> ¶meters) {
|
|
for (auto param : *params)
|
|
bindParameterForForwarding(param, parameters);
|
|
}
|
|
|
|
static void emitCaptureArguments(SILGenFunction &SGF,
|
|
GenericSignature origGenericSig,
|
|
CapturedValue capture,
|
|
uint16_t ArgNo) {
|
|
|
|
auto *VD = cast<VarDecl>(capture.getDecl());
|
|
SILLocation Loc(VD);
|
|
Loc.markAsPrologue();
|
|
|
|
// Local function to get the captured variable type within the capturing
|
|
// context.
|
|
auto getVarTypeInCaptureContext = [&]() -> Type {
|
|
auto interfaceType = VD->getInterfaceType()->getCanonicalType(
|
|
origGenericSig);
|
|
return SGF.F.mapTypeIntoContext(interfaceType);
|
|
};
|
|
|
|
auto expansion = SGF.getTypeExpansionContext();
|
|
switch (SGF.SGM.Types.getDeclCaptureKind(capture, expansion)) {
|
|
case CaptureKind::Constant: {
|
|
auto type = getVarTypeInCaptureContext();
|
|
auto &lowering = SGF.getTypeLowering(type);
|
|
// Constant decls are captured by value.
|
|
SILType ty = lowering.getLoweredType();
|
|
SILValue val = SGF.F.begin()->createFunctionArgument(ty, VD);
|
|
|
|
bool NeedToDestroyValueAtExit = false;
|
|
|
|
// If the original variable was settable, then Sema will have treated the
|
|
// VarDecl as an lvalue, even in the closure's use. As such, we need to
|
|
// allow formation of the address for this captured value. Create a
|
|
// temporary within the closure to provide this address.
|
|
if (VD->isSettable(VD->getDeclContext())) {
|
|
auto addr = SGF.emitTemporaryAllocation(VD, ty);
|
|
// We have created a copy that needs to be destroyed.
|
|
val = SGF.B.emitCopyValueOperation(Loc, val);
|
|
NeedToDestroyValueAtExit = true;
|
|
lowering.emitStore(SGF.B, VD, val, addr, StoreOwnershipQualifier::Init);
|
|
val = addr;
|
|
}
|
|
|
|
SGF.VarLocs[VD] = SILGenFunction::VarLoc::get(val);
|
|
if (auto *AllocStack = dyn_cast<AllocStackInst>(val))
|
|
AllocStack->setArgNo(ArgNo);
|
|
else {
|
|
SILDebugVariable DbgVar(/*Constant*/ true, ArgNo);
|
|
SGF.B.createDebugValue(Loc, val, DbgVar);
|
|
}
|
|
|
|
// TODO: Closure contexts should always be guaranteed.
|
|
if (NeedToDestroyValueAtExit && !lowering.isTrivial())
|
|
SGF.enterDestroyCleanup(val);
|
|
break;
|
|
}
|
|
|
|
case CaptureKind::Box: {
|
|
// LValues are captured as a retained @box that owns
|
|
// the captured value.
|
|
auto type = getVarTypeInCaptureContext();
|
|
// Get the content for the box in the minimal resilience domain because we
|
|
// are declaring a type.
|
|
auto boxTy = SGF.SGM.Types.getContextBoxTypeForCapture(
|
|
VD,
|
|
SGF.SGM.Types.getLoweredRValueType(TypeExpansionContext::minimal(),
|
|
type),
|
|
SGF.F.getGenericEnvironment(), /*mutable*/ true);
|
|
SILValue box = SGF.F.begin()->createFunctionArgument(
|
|
SILType::getPrimitiveObjectType(boxTy), VD);
|
|
SILValue addr = SGF.B.createProjectBox(VD, box, 0);
|
|
SGF.VarLocs[VD] = SILGenFunction::VarLoc::get(addr, box);
|
|
SILDebugVariable DbgVar(/*Constant*/ false, ArgNo);
|
|
SGF.B.createDebugValueAddr(Loc, addr, DbgVar);
|
|
break;
|
|
}
|
|
case CaptureKind::Immutable:
|
|
case CaptureKind::StorageAddress: {
|
|
// Non-escaping stored decls are captured as the address of the value.
|
|
auto type = getVarTypeInCaptureContext();
|
|
SILType ty = SGF.getLoweredType(type).getAddressType();
|
|
SILValue addr = SGF.F.begin()->createFunctionArgument(ty, VD);
|
|
SGF.VarLocs[VD] = SILGenFunction::VarLoc::get(addr);
|
|
SILDebugVariable DbgVar(/*Constant*/ true, ArgNo);
|
|
SGF.B.createDebugValueAddr(Loc, addr, DbgVar);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void SILGenFunction::emitProlog(CaptureInfo captureInfo,
|
|
ParameterList *paramList,
|
|
ParamDecl *selfParam,
|
|
DeclContext *DC,
|
|
Type resultType,
|
|
bool throws,
|
|
SourceLoc throwsLoc) {
|
|
uint16_t ArgNo = emitProlog(paramList, selfParam, resultType,
|
|
DC, throws, throwsLoc);
|
|
|
|
// Emit the capture argument variables. These are placed last because they
|
|
// become the first curry level of the SIL function.
|
|
assert(captureInfo.hasBeenComputed() &&
|
|
"can't emit prolog of function with uncomputed captures");
|
|
for (auto capture : captureInfo.getCaptures()) {
|
|
if (capture.isDynamicSelfMetadata()) {
|
|
auto selfMetatype = MetatypeType::get(
|
|
captureInfo.getDynamicSelfType());
|
|
SILType ty = getLoweredType(selfMetatype);
|
|
SILValue val = F.begin()->createFunctionArgument(ty);
|
|
(void) val;
|
|
|
|
continue;
|
|
}
|
|
|
|
if (capture.isOpaqueValue()) {
|
|
OpaqueValueExpr *opaqueValue = capture.getOpaqueValue();
|
|
Type type = opaqueValue->getType()->mapTypeOutOfContext();
|
|
type = F.mapTypeIntoContext(type);
|
|
auto &lowering = getTypeLowering(type);
|
|
SILType ty = lowering.getLoweredType();
|
|
SILValue val = F.begin()->createFunctionArgument(ty);
|
|
OpaqueValues[opaqueValue] = ManagedValue::forUnmanaged(val);
|
|
|
|
// Opaque values are always passed 'owned', so add a clean up if needed.
|
|
if (!lowering.isTrivial())
|
|
enterDestroyCleanup(val);
|
|
|
|
continue;
|
|
}
|
|
|
|
emitCaptureArguments(*this, DC->getGenericSignatureOfContext(),
|
|
capture, ++ArgNo);
|
|
}
|
|
|
|
// Emit an unreachable instruction if a parameter type is
|
|
// uninhabited
|
|
if (paramList) {
|
|
for (auto *param : *paramList) {
|
|
if (param->getType()->isStructurallyUninhabited()) {
|
|
SILLocation unreachableLoc(param);
|
|
unreachableLoc.markAsPrologue();
|
|
B.createUnreachable(unreachableLoc);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void emitIndirectResultParameters(SILGenFunction &SGF, Type resultType,
|
|
DeclContext *DC) {
|
|
// Expand tuples.
|
|
if (auto tupleType = resultType->getAs<TupleType>()) {
|
|
for (auto eltType : tupleType->getElementTypes()) {
|
|
emitIndirectResultParameters(SGF, eltType, 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.getTypeExpansionContext());
|
|
auto &resultTIConv = SGF.SGM.Types.getTypeLowering(
|
|
DC->mapTypeIntoContext(resultType), TypeExpansionContext::minimal());
|
|
|
|
if (!SILModuleConventions::isReturnedIndirectlyInSIL(
|
|
resultTIConv.getLoweredType(), SGF.SGM.M)) {
|
|
return;
|
|
}
|
|
auto &ctx = SGF.getASTContext();
|
|
auto var = new (ctx) ParamDecl(SourceLoc(), SourceLoc(),
|
|
ctx.getIdentifier("$return_value"), SourceLoc(),
|
|
ctx.getIdentifier("$return_value"),
|
|
DC);
|
|
var->setSpecifier(ParamSpecifier::InOut);
|
|
var->setInterfaceType(resultType);
|
|
auto *arg = SGF.F.begin()->createFunctionArgument(
|
|
resultTI.getLoweredType().getAddressType(), var);
|
|
(void)arg;
|
|
}
|
|
|
|
uint16_t SILGenFunction::emitProlog(ParameterList *paramList,
|
|
ParamDecl *selfParam,
|
|
Type resultType,
|
|
DeclContext *DC,
|
|
bool throws,
|
|
SourceLoc throwsLoc) {
|
|
// Create the indirect result parameters.
|
|
auto genericSig = DC->getGenericSignatureOfContext();
|
|
resultType = resultType->getCanonicalType(genericSig);
|
|
|
|
emitIndirectResultParameters(*this, resultType, DC);
|
|
|
|
// Emit the argument variables in calling convention order.
|
|
ArgumentInitHelper emitter(*this, F);
|
|
|
|
// Add the SILArguments and use them to initialize the local argument
|
|
// values.
|
|
if (paramList)
|
|
for (auto *param : *paramList)
|
|
emitter.emitParam(param);
|
|
if (selfParam)
|
|
emitter.emitParam(selfParam);
|
|
|
|
// Record the ArgNo of the artificial $error inout argument.
|
|
unsigned ArgNo = emitter.getNumArgs();
|
|
if (throws) {
|
|
auto NativeErrorTy = SILType::getExceptionType(getASTContext());
|
|
ManagedValue Undef = emitUndef(NativeErrorTy);
|
|
SILDebugVariable DbgVar("$error", /*Constant*/ false, ++ArgNo);
|
|
RegularLocation loc = RegularLocation::getAutoGeneratedLocation();
|
|
if (throwsLoc.isValid())
|
|
loc = throwsLoc;
|
|
B.createDebugValue(loc, Undef.getValue(), DbgVar);
|
|
}
|
|
|
|
return ArgNo;
|
|
}
|