blob: b551a5b102c52aff7ef8f5b242f283ea13dbbe04 [file] [log] [blame]
//===--- SILGenThunk.cpp - SILGen for thunks ------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file contains code for emitting various types of thunks that can be
// referenced from code, such as dynamic thunks, curry thunks, native to foreign
// thunks and foreign to native thunks.
//
// VTable thunks and witness thunks can be found in SILGenType.cpp, and the
// meat of the bridging thunk implementation is in SILGenBridging.cpp, and
// re-abstraction thunks are in SILGenPoly.cpp.
//
//===----------------------------------------------------------------------===//
#include "SILGenFunction.h"
#include "Scope.h"
#include "ManagedValue.h"
#include "swift/AST/ASTMangler.h"
#include "swift/AST/DiagnosticsSIL.h"
#include "swift/AST/GenericEnvironment.h"
#include "swift/SIL/PrettyStackTrace.h"
#include "swift/SIL/SILArgument.h"
#include "swift/SIL/TypeLowering.h"
using namespace swift;
using namespace Lowering;
SILFunction *SILGenModule::getDispatchThunk(SILDeclRef constant,
ForDefinition_t forDefinition) {
// Mangle the constant with a Tv suffix.
auto name = constant.mangle(SILDeclRef::ManglingKind::SwiftDispatchThunk);
auto constantTy = M.Types.getConstantOverrideType(constant);
auto linkage = (forDefinition
? SILLinkage::Public
: SILLinkage::PublicExternal);
// N.B.: Right now, we don't serialize vtables in resilient modules,
// so there's nothing to gain from marking these as serialized.
return M.getOrCreateFunction(constant.getDecl(), name, linkage,
constantTy, IsBare, IsNotTransparent,
IsNotSerialized, ProfileCounter(), IsThunk);
}
SILFunction *SILGenModule::emitDispatchThunk(SILDeclRef constant) {
auto *F = getDispatchThunk(constant, ForDefinition);
if (F->empty()) {
auto *afd = cast<AbstractFunctionDecl>(constant.getDecl());
preEmitFunction(constant, afd, F, afd);
SILGenFunction SGF(*this, *F);
SGF.emitDispatchThunk(constant);
postEmitFunction(constant, F);
}
return F;
}
void SILGenFunction::emitDispatchThunk(SILDeclRef constant) {
auto *afd = cast<AbstractFunctionDecl>(constant.getDecl());
SILLocation loc(afd);
loc.markAutoGenerated();
auto subs = F.getForwardingSubstitutions();
SmallVector<SILValue, 8> args;
// Add the indirect results.
for (auto resultTy : F.getConventions().getIndirectSILResultTypes()) {
auto paramTy = F.mapTypeIntoContext(resultTy);
args.push_back(F.begin()->createFunctionArgument(paramTy));
}
// Add the parameters.
auto paramTypes = F.getLoweredFunctionType()->getParameters();
for (auto param : paramTypes) {
auto paramTy = F.mapTypeIntoContext(F.getConventions().getSILType(param));
args.push_back(F.begin()->createFunctionArgument(paramTy));
}
auto constantTy = F.getLoweredFunctionType();
auto selfPtr = args.back();
auto fnValue =
B.createClassMethod(loc, selfPtr, constant,
SILType::getPrimitiveObjectType(constantTy));
auto substFnType = fnValue->getType().substGenericArgs(SGM.M, subs);
auto result =
emitApplyWithRethrow(loc, fnValue, substFnType, subs, args);
B.createReturn(loc, result);
}
static bool shouldUseDispatchThunk(SILDeclRef constant,
SILFunction *F,
SILModule &M) {
auto *afd = cast<AbstractFunctionDecl>(constant.getDecl());
auto *classDecl = cast<ClassDecl>(afd->getDeclContext());
// Resilient classes in other resilience domains use dispatch thunks.
return !classDecl->hasFixedLayout(M.getSwiftModule(),
F->getResilienceExpansion());
}
SILValue SILGenFunction::emitClassMethodRef(SILLocation loc,
SILValue selfPtr,
SILDeclRef constant,
CanSILFunctionType constantTy) {
assert(!constant.isForeign);
// Not every override gets a dispatch thunk; find the least derived one
// that does.
auto base = SGM.M.Types.getOverriddenVTableEntry(constant);
if (shouldUseDispatchThunk(base, &F, SGM.M)) {
auto *thunkFn = SGM.getDispatchThunk(base, NotForDefinition);
SILValue ref = B.createFunctionRef(loc, thunkFn);
if (thunkFn->getLoweredFunctionType() != constantTy) {
auto resultTy = SILType::getPrimitiveObjectType(constantTy);
ref = B.createConvertFunction(loc, ref, resultTy);
}
return ref;
}
return B.createClassMethod(loc, selfPtr, constant,
SILType::getPrimitiveObjectType(constantTy));
}
SILFunction *SILGenModule::getDynamicThunk(SILDeclRef constant,
CanSILFunctionType constantTy) {
assert(constant.kind != SILDeclRef::Kind::Allocator &&
"allocating entry point for constructor is never dynamic");
// Mangle the constant with a TD suffix.
auto name = constant.mangle(SILDeclRef::ManglingKind::DynamicThunk);
auto F = M.getOrCreateFunction(constant.getDecl(), name, SILLinkage::Shared,
constantTy, IsBare, IsTransparent,
IsSerializable, ProfileCounter(), IsThunk);
if (F->empty()) {
// Emit the thunk if we haven't yet.
// Currently a dynamic thunk looks just like a foreign-to-native thunk around
// an ObjC method. This would change if we introduced a native
// runtime-hookable mechanism.
SILGenFunction SGF(*this, *F);
SGF.emitForeignToNativeThunk(constant);
}
return F;
}
SILValue SILGenFunction::emitDynamicMethodRef(SILLocation loc,
SILDeclRef constant,
CanSILFunctionType constantTy) {
// If the method is foreign, its foreign thunk will handle the dynamic
// dispatch for us.
if (constant.isForeignToNativeThunk()) {
if (!SGM.hasFunction(constant))
SGM.emitForeignToNativeThunk(constant);
return B.createFunctionRef(loc, SGM.getFunction(constant, NotForDefinition));
}
// Otherwise, we need a dynamic dispatch thunk.
SILFunction *F = SGM.getDynamicThunk(constant, constantTy);
return B.createFunctionRef(loc, F);
}
static SILValue getNextUncurryLevelRef(SILGenFunction &SGF,
SILLocation loc,
SILDeclRef thunk,
SILValue selfArg,
SubstitutionList curriedSubs) {
auto *vd = thunk.getDecl();
// Reference the next uncurrying level of the function.
SILDeclRef next = SILDeclRef(vd, thunk.kind);
assert(!next.isCurried);
// If the function is natively foreign, reference its foreign entry point.
if (requiresForeignToNativeThunk(vd))
return SGF.emitGlobalFunctionRef(loc, next);
// If the thunk is a curry thunk for a direct method reference, we are
// doing a direct dispatch (eg, a fragile 'super.foo()' call).
if (thunk.isDirectReference)
return SGF.emitGlobalFunctionRef(loc, next);
auto constantInfo = SGF.SGM.Types.getConstantInfo(next);
if (auto *func = dyn_cast<AbstractFunctionDecl>(vd)) {
if (getMethodDispatch(func) == MethodDispatch::Class) {
// Use the dynamic thunk if dynamic.
if (vd->isDynamic())
return SGF.emitDynamicMethodRef(loc, next, constantInfo.SILFnType);
auto methodTy = SGF.SGM.Types.getConstantOverrideType(next);
return SGF.emitClassMethodRef(loc, selfArg, next, methodTy);
}
// If the fully-uncurried reference is to a generic method, look up the
// witness.
if (constantInfo.SILFnType->getRepresentation()
== SILFunctionTypeRepresentation::WitnessMethod) {
auto protocol =
func->getDeclContext()->getAsProtocolOrProtocolExtensionContext();
auto subMap = func->getGenericSignature()
->getSubstitutionMap(curriedSubs);
auto origSelfType = protocol->getSelfInterfaceType()->getCanonicalType();
auto substSelfType = origSelfType.subst(subMap)->getCanonicalType();
auto conformance = subMap.lookupConformance(origSelfType, protocol);
return SGF.B.createWitnessMethod(loc, substSelfType, *conformance, next,
constantInfo.getSILType());
}
}
// Otherwise, emit a direct call.
return SGF.emitGlobalFunctionRef(loc, next);
}
void SILGenFunction::emitCurryThunk(SILDeclRef thunk) {
assert(thunk.isCurried);
auto *vd = thunk.getDecl();
if (auto *fd = dyn_cast<AbstractFunctionDecl>(vd)) {
assert(!SGM.M.Types.hasLoweredLocalCaptures(fd) &&
"methods cannot have captures");
(void) fd;
}
auto selfTy = vd->getInterfaceType()->castTo<AnyFunctionType>()
->getInput();
selfTy = vd->getInnermostDeclContext()->mapTypeIntoContext(selfTy);
auto selfArg = F.begin()->createFunctionArgument(getLoweredType(selfTy));
// Forward substitutions.
auto subs = F.getForwardingSubstitutions();
SILValue toFn = getNextUncurryLevelRef(*this, vd, thunk,
selfArg, subs);
// FIXME: Using the type from the ConstantInfo instead of looking at
// getConstantOverrideInfo() for methods looks suspect in the presence
// of covariant overrides and multiple vtable entries.
SILFunctionConventions fromConv(
SGM.Types.getConstantInfo(thunk).SILFnType, SGM.M);
SILType resultTy = fromConv.getSingleSILResultType();
resultTy = F.mapTypeIntoContext(resultTy);
auto substTy = toFn->getType().substGenericArgs(SGM.M, subs);
auto calleeConvention = ParameterConvention::Direct_Guaranteed;
// Partially apply the next uncurry level and return the result closure.
auto closureTy = SILGenBuilder::getPartialApplyResultType(
toFn->getType(), /*appliedParams=*/1, SGM.M, subs, calleeConvention);
SILValue toClosure =
B.createPartialApply(vd, toFn, substTy, subs, {selfArg}, closureTy);
if (resultTy != closureTy)
toClosure = B.createConvertFunction(vd, toClosure, resultTy);
B.createReturn(ImplicitReturnLocation::getImplicitReturnLoc(vd), toClosure);
}
void SILGenModule::emitCurryThunk(SILDeclRef constant) {
assert(constant.isCurried);
// Thunks are always emitted by need, so don't need delayed emission.
SILFunction *f = getFunction(constant, ForDefinition);
f->setThunk(IsThunk);
f->setBare(IsBare);
auto *fd = constant.getDecl();
preEmitFunction(constant, fd, f, fd);
PrettyStackTraceSILFunction X("silgen emitCurryThunk", f);
SILGenFunction(*this, *f).emitCurryThunk(constant);
postEmitFunction(constant, f);
}
void SILGenModule::emitForeignToNativeThunk(SILDeclRef thunk) {
// Thunks are always emitted by need, so don't need delayed emission.
assert(!thunk.isForeign && "foreign-to-native thunks only");
SILFunction *f = getFunction(thunk, ForDefinition);
f->setThunk(IsThunk);
if (thunk.asForeign().isClangGenerated())
f->setSerialized(IsSerialized);
preEmitFunction(thunk, thunk.getDecl(), f, thunk.getDecl());
PrettyStackTraceSILFunction X("silgen emitForeignToNativeThunk", f);
SILGenFunction(*this, *f).emitForeignToNativeThunk(thunk);
postEmitFunction(thunk, f);
}
void SILGenModule::emitNativeToForeignThunk(SILDeclRef thunk) {
// Thunks are always emitted by need, so don't need delayed emission.
assert(thunk.isForeign && "native-to-foreign thunks only");
SILFunction *f = getFunction(thunk, ForDefinition);
if (thunk.hasDecl())
preEmitFunction(thunk, thunk.getDecl(), f, thunk.getDecl());
else
preEmitFunction(thunk, thunk.getAbstractClosureExpr(), f,
thunk.getAbstractClosureExpr());
PrettyStackTraceSILFunction X("silgen emitNativeToForeignThunk", f);
f->setBare(IsBare);
f->setThunk(IsThunk);
SILGenFunction(*this, *f).emitNativeToForeignThunk(thunk);
postEmitFunction(thunk, f);
}
SILValue SILGenFunction::emitGlobalFunctionRef(SILLocation loc,
SILDeclRef constant,
SILConstantInfo constantInfo) {
assert(constantInfo == getConstantInfo(constant));
// Builtins must be fully applied at the point of reference.
if (constant.hasDecl() &&
isa<BuiltinUnit>(constant.getDecl()->getDeclContext())) {
SGM.diagnose(loc.getSourceLoc(), diag::not_implemented,
"delayed application of builtin");
return SILUndef::get(constantInfo.getSILType(), SGM.M);
}
// If the constant is a thunk we haven't emitted yet, emit it.
if (!SGM.hasFunction(constant)) {
if (constant.isCurried) {
SGM.emitCurryThunk(constant);
} else if (constant.isForeignToNativeThunk()) {
SGM.emitForeignToNativeThunk(constant);
} else if (constant.isNativeToForeignThunk()) {
SGM.emitNativeToForeignThunk(constant);
} else if (constant.kind == SILDeclRef::Kind::EnumElement) {
SGM.emitEnumConstructor(cast<EnumElementDecl>(constant.getDecl()));
}
}
auto f = SGM.getFunction(constant, NotForDefinition);
assert(f->getLoweredFunctionType() == constantInfo.SILFnType);
return B.createFunctionRef(loc, f);
}
SILFunction *SILGenModule::
getOrCreateReabstractionThunk(CanSILFunctionType thunkType,
CanSILFunctionType fromType,
CanSILFunctionType toType,
IsSerialized_t Serialized) {
// The reference to the thunk is likely @noescape, but declarations are always
// escaping.
auto thunkDeclType =
adjustFunctionType(thunkType, thunkType->getExtInfo().withNoEscape(false),
thunkType->getWitnessMethodConformanceOrNone());
// Mangle the reabstraction thunk.
// Substitute context parameters out of the "from" and "to" types.
auto fromInterfaceType = fromType->mapTypeOutOfContext()
->getCanonicalType();
auto toInterfaceType = toType->mapTypeOutOfContext()
->getCanonicalType();
Mangle::ASTMangler NewMangler;
std::string name = NewMangler.mangleReabstractionThunkHelper(thunkType,
fromInterfaceType, toInterfaceType, M.getSwiftModule());
auto loc = RegularLocation::getAutoGeneratedLocation();
return M.getOrCreateSharedFunction(loc, name, thunkDeclType, IsBare,
IsTransparent, IsSerializable,
ProfileCounter(), IsReabstractionThunk);
}