blob: 1691387a6c8a9e8c45b821abdd5885a0041dc6c2 [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 "ManagedValue.h"
#include "SILGenFunction.h"
#include "SILGenFunctionBuilder.h"
#include "Scope.h"
#include "swift/AST/ASTMangler.h"
#include "swift/AST/DiagnosticsSIL.h"
#include "swift/AST/FileUnit.h"
#include "swift/AST/ForeignAsyncConvention.h"
#include "swift/AST/GenericEnvironment.h"
#include "swift/SIL/PrettyStackTrace.h"
#include "swift/SIL/SILArgument.h"
#include "swift/SIL/TypeLowering.h"
#include "clang/AST/ASTContext.h"
using namespace swift;
using namespace Lowering;
SILValue SILGenFunction::emitClassMethodRef(SILLocation loc,
SILValue selfPtr,
SILDeclRef constant,
CanSILFunctionType constantTy) {
assert(!constant.isForeign);
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 nameTmp = constant.mangle(SILDeclRef::ManglingKind::DynamicThunk);
auto name = M.allocateCopy(nameTmp);
SILGenFunctionBuilder builder(*this);
auto F = builder.getOrCreateFunction(
constant.getDecl(), name, SILLinkage::Shared, constantTy, IsBare,
IsTransparent, IsSerializable, IsNotDynamic, 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, SwiftModule);
SGF.emitForeignToNativeThunk(constant);
emitLazyConformancesForFunction(F);
}
return F;
}
ManagedValue
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 ManagedValue::forUnmanaged(B.createFunctionRefFor(
loc, SGM.getFunction(constant, NotForDefinition)));
}
// Otherwise, we need a dynamic dispatch thunk.
SILFunction *F = SGM.getDynamicThunk(constant, constantTy);
return ManagedValue::forUnmanaged(B.createFunctionRefFor(loc, F));
}
void SILGenModule::emitForeignToNativeThunk(SILDeclRef thunk) {
// Thunks are always emitted by need, so don't need delayed emission.
assert(thunk.isForeignToNativeThunk() && "foreign-to-native thunks only");
emitFunctionDefinition(thunk, getFunction(thunk, ForDefinition));
}
void SILGenModule::emitNativeToForeignThunk(SILDeclRef thunk) {
// Thunks are always emitted by need, so don't need delayed emission.
assert(thunk.isNativeToForeignThunk() && "native-to-foreign thunks only");
emitFunctionDefinition(thunk, getFunction(thunk, ForDefinition));
}
SILValue
SILGenFunction::emitGlobalFunctionRef(SILLocation loc, SILDeclRef constant,
SILConstantInfo constantInfo,
bool callPreviousDynamicReplaceableImpl) {
assert(constantInfo == getConstantInfo(getTypeExpansionContext(), 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(), F);
}
// If the constant is a thunk we haven't emitted yet, emit it.
if (!SGM.hasFunction(constant)) {
if (constant.isForeignToNativeThunk()) {
SGM.emitForeignToNativeThunk(constant);
} else if (constant.isNativeToForeignThunk()) {
SGM.emitNativeToForeignThunk(constant);
}
}
auto f = SGM.getFunction(constant, NotForDefinition);
#ifndef NDEBUG
auto constantFnTypeInContext =
SGM.Types.getLoweredType(constantInfo.SILFnType,
B.getTypeExpansionContext())
.castTo<SILFunctionType>();
assert(f->getLoweredFunctionTypeInContext(B.getTypeExpansionContext())
== constantFnTypeInContext);
#endif
if (callPreviousDynamicReplaceableImpl)
return B.createPreviousDynamicFunctionRef(loc, f);
else
return B.createFunctionRefFor(loc, f);
}
static const clang::Type *prependParameterType(
ASTContext &ctx,
const clang::Type *oldBlockPtrTy,
const clang::Type *newParameterTy) {
if (!oldBlockPtrTy)
return nullptr;
SmallVector<clang::QualType, 4> newParamTypes;
newParamTypes.push_back(clang::QualType(newParameterTy, 0));
clang::QualType returnType;
clang::FunctionProtoType::ExtProtoInfo newExtProtoInfo{};
using ExtParameterInfo = clang::FunctionProtoType::ExtParameterInfo;
SmallVector<ExtParameterInfo, 4> newExtParamInfos;
auto blockPtrTy = cast<clang::BlockPointerType>(oldBlockPtrTy);
auto blockPointeeTy = blockPtrTy->getPointeeType().getTypePtr();
if (auto fnNoProtoTy = dyn_cast<clang::FunctionNoProtoType>(blockPointeeTy)) {
returnType = fnNoProtoTy->getReturnType();
newExtProtoInfo.ExtInfo = fnNoProtoTy->getExtInfo();
} else {
auto fnProtoTy = cast<clang::FunctionProtoType>(blockPointeeTy);
llvm::copy(fnProtoTy->getParamTypes(), std::back_inserter(newParamTypes));
returnType = fnProtoTy->getReturnType();
newExtProtoInfo = fnProtoTy->getExtProtoInfo();
auto extParamInfos = fnProtoTy->getExtParameterInfosOrNull();
if (extParamInfos) {
auto oldExtParamInfos =
ArrayRef<ExtParameterInfo>(extParamInfos, fnProtoTy->getNumParams());
newExtParamInfos.push_back(clang::FunctionProtoType::ExtParameterInfo());
llvm::copy(oldExtParamInfos, std::back_inserter(newExtParamInfos));
newExtProtoInfo.ExtParameterInfos = newExtParamInfos.data();
}
}
auto &clangCtx = ctx.getClangModuleLoader()->getClangASTContext();
auto newFnTy =
clangCtx.getFunctionType(returnType, newParamTypes, newExtProtoInfo);
return clangCtx.getPointerType(newFnTy).getTypePtr();
}
SILFunction *
SILGenModule::getOrCreateForeignAsyncCompletionHandlerImplFunction(
CanSILFunctionType blockType,
CanType continuationTy,
ForeignAsyncConvention convention) {
// Extract the result type from the continuation type.
auto resumeType = cast<BoundGenericType>(continuationTy).getGenericArgs()[0];
// Build up the implementation function type, which matches the
// block signature with an added block storage argument that points at the
// block buffer. The block storage holds the continuation we feed the
// result values into.
SmallVector<SILParameterInfo, 4> implArgs;
auto blockStorageTy = SILBlockStorageType::get(continuationTy);
implArgs.push_back(SILParameterInfo(blockStorageTy,
ParameterConvention::Indirect_InoutAliasable));
std::copy(blockType->getParameters().begin(),
blockType->getParameters().end(),
std::back_inserter(implArgs));
auto newClangTy = prependParameterType(
getASTContext(),
blockType->getClangTypeInfo().getType(),
getASTContext().getClangTypeForIRGen(blockStorageTy));
auto implTy = SILFunctionType::get(GenericSignature(),
blockType->getExtInfo().intoBuilder()
.withRepresentation(SILFunctionTypeRepresentation::CFunctionPointer)
.withClangFunctionType(newClangTy)
.build(),
SILCoroutineKind::None,
ParameterConvention::Direct_Unowned,
implArgs, {}, blockType->getResults(),
None,
SubstitutionMap(), SubstitutionMap(), getASTContext());
auto loc = RegularLocation::getAutoGeneratedLocation();
Mangle::ASTMangler Mangler;
auto name = Mangler.mangleObjCAsyncCompletionHandlerImpl(blockType,
resumeType,
/*predefined*/ false);
SILGenFunctionBuilder builder(*this);
auto F = builder.getOrCreateSharedFunction(loc, name, implTy,
IsBare, IsTransparent, IsSerializable,
ProfileCounter(),
IsThunk,
IsNotDynamic);
if (F->empty()) {
// TODO: Emit the implementation.
SILGenFunction SGF(*this, *F, SwiftModule);
{
Scope scope(SGF, loc);
SmallVector<ManagedValue, 4> params;
SGF.collectThunkParams(loc, params);
// Get the continuation out of the block object.
auto blockStorage = params[0].getValue();
auto continuationAddr = SGF.B.createProjectBlockStorage(loc, blockStorage);
auto continuationVal = SGF.B.createLoad(loc, continuationAddr,
LoadOwnershipQualifier::Trivial);
auto continuation = ManagedValue::forUnmanaged(continuationVal);
// Check for an error if the convention includes one.
auto errorIndex = convention.completionHandlerErrorParamIndex();
FuncDecl *resumeIntrinsic, *errorIntrinsic;
SILBasicBlock *returnBB = nullptr;
if (errorIndex) {
resumeIntrinsic = getResumeUnsafeThrowingContinuation();
errorIntrinsic = getResumeUnsafeThrowingContinuationWithError();
auto errorArgument = params[*errorIndex + 1];
auto someErrorBB = SGF.createBasicBlock(FunctionSection::Postmatter);
auto noneErrorBB = SGF.createBasicBlock();
returnBB = SGF.createBasicBlockAfter(noneErrorBB);
auto &C = SGF.getASTContext();
std::pair<EnumElementDecl *, SILBasicBlock *> switchErrorBBs[] = {
{C.getOptionalSomeDecl(), someErrorBB},
{C.getOptionalNoneDecl(), noneErrorBB}
};
SGF.B.createSwitchEnum(loc, errorArgument.borrow(SGF, loc).getValue(),
/*default*/ nullptr,
switchErrorBBs);
SGF.B.emitBlock(someErrorBB);
auto matchedErrorTy = errorArgument.getType().getOptionalObjectType();
auto matchedError = SGF.B
.createGuaranteedTransformingTerminatorArgument(matchedErrorTy);
// Resume the continuation as throwing the given error, bridged to a
// native Swift error.
auto nativeError = SGF.emitBridgedToNativeError(loc, matchedError);
Type replacementTypes[] = {resumeType};
auto subs = SubstitutionMap::get(errorIntrinsic->getGenericSignature(),
replacementTypes,
ArrayRef<ProtocolConformanceRef>{});
SGF.emitApplyOfLibraryIntrinsic(loc, errorIntrinsic, subs,
{continuation, nativeError},
SGFContext());
SGF.B.createBranch(loc, returnBB);
SGF.B.emitBlock(noneErrorBB);
} else {
resumeIntrinsic = getResumeUnsafeContinuation();
}
auto loweredResumeTy = SGF.getLoweredType(AbstractionPattern::getOpaque(),
resumeType);
// Prepare the argument for the resume intrinsic, using the non-error
// arguments to the callback.
{
Scope resumeScope(SGF, loc);
unsigned errorIndexBoundary = errorIndex ? *errorIndex : ~0u;
auto resumeArgBuf = SGF.emitTemporaryAllocation(loc,
loweredResumeTy.getAddressType());
auto prepareArgument = [&](SILValue destBuf, ManagedValue arg) {
// Convert the ObjC argument to the bridged Swift representation we
// want.
ManagedValue bridgedArg = SGF.emitBridgedToNativeValue(loc,
arg,
arg.getType().getASTType(),
// FIXME: pass down formal type
destBuf->getType().getASTType(),
destBuf->getType().getObjectType());
bridgedArg.forwardInto(SGF, loc, destBuf);
};
if (auto resumeTuple = dyn_cast<TupleType>(resumeType)) {
assert(params.size() == resumeTuple->getNumElements()
+ 1 + (bool)errorIndex);
for (auto i : indices(resumeTuple.getElementTypes())) {
auto resumeEltBuf = SGF.B.createTupleElementAddr(loc,
resumeArgBuf, i);
auto arg = params[1 + i + (i >= errorIndexBoundary)];
prepareArgument(resumeEltBuf, arg);
}
} else {
assert(params.size() == 2 + (bool)errorIndex);
prepareArgument(resumeArgBuf, params[1 + (errorIndexBoundary == 0)]);
}
// Resume the continuation with the composed bridged result.
ManagedValue resumeArg = SGF.emitManagedBufferWithCleanup(resumeArgBuf);
Type replacementTypes[] = {resumeType};
auto subs = SubstitutionMap::get(resumeIntrinsic->getGenericSignature(),
replacementTypes,
ArrayRef<ProtocolConformanceRef>{});
SGF.emitApplyOfLibraryIntrinsic(loc, resumeIntrinsic, subs,
{continuation, resumeArg},
SGFContext());
}
// Now we've resumed the continuation one way or another. Return from the
// completion callback.
if (returnBB) {
SGF.B.createBranch(loc, returnBB);
SGF.B.emitBlock(returnBB);
}
}
SGF.B.createReturn(loc,
SILUndef::get(SGF.SGM.Types.getEmptyTupleType(), SGF.F));
}
return F;
}
SILFunction *SILGenModule::
getOrCreateReabstractionThunk(CanSILFunctionType thunkType,
CanSILFunctionType fromType,
CanSILFunctionType toType,
CanType dynamicSelfType) {
// The reference to the thunk is likely @noescape, but declarations are always
// escaping.
auto thunkDeclType =
thunkType->getWithExtInfo(thunkType->getExtInfo().withNoEscape(false));
// Mangle the reabstraction thunk.
// Substitute context parameters out of the "from" and "to" types.
auto fromInterfaceType = fromType->mapTypeOutOfContext()
->getCanonicalType();
auto toInterfaceType = toType->mapTypeOutOfContext()
->getCanonicalType();
CanType dynamicSelfInterfaceType;
if (dynamicSelfType)
dynamicSelfInterfaceType = dynamicSelfType->mapTypeOutOfContext()
->getCanonicalType();
Mangle::ASTMangler NewMangler;
std::string name = NewMangler.mangleReabstractionThunkHelper(thunkType,
fromInterfaceType, toInterfaceType,
dynamicSelfInterfaceType,
M.getSwiftModule());
auto loc = RegularLocation::getAutoGeneratedLocation();
SILGenFunctionBuilder builder(*this);
return builder.getOrCreateSharedFunction(
loc, name, thunkDeclType, IsBare, IsTransparent, IsSerializable,
ProfileCounter(), IsReabstractionThunk, IsNotDynamic);
}
SILFunction *SILGenModule::getOrCreateAutoDiffClassMethodThunk(
SILDeclRef derivativeFnDeclRef, CanSILFunctionType constantTy) {
auto *derivativeId = derivativeFnDeclRef.getDerivativeFunctionIdentifier();
assert(derivativeId);
auto *derivativeFnDecl = derivativeFnDeclRef.getDecl();
SILGenFunctionBuilder builder(*this);
auto originalFnDeclRef = derivativeFnDeclRef.asAutoDiffOriginalFunction();
// TODO(TF-685): Use principled thunk mangling.
// Do not simply reuse reabstraction thunk mangling.
auto name = derivativeFnDeclRef.mangle() + "_vtable_entry_thunk";
auto *thunk = builder.getOrCreateFunction(
derivativeFnDecl, name, originalFnDeclRef.getLinkage(ForDefinition),
constantTy, IsBare, IsTransparent, derivativeFnDeclRef.isSerialized(),
IsNotDynamic, ProfileCounter(), IsThunk);
if (!thunk->empty())
return thunk;
if (auto genSig = constantTy->getSubstGenericSignature())
thunk->setGenericEnvironment(genSig->getGenericEnvironment());
SILGenFunction SGF(*this, *thunk, SwiftModule);
SmallVector<ManagedValue, 4> params;
auto loc = derivativeFnDeclRef.getAsRegularLocation();
SGF.collectThunkParams(loc, params);
auto originalFn = SGF.emitGlobalFunctionRef(loc, originalFnDeclRef);
auto *loweredParamIndices = autodiff::getLoweredParameterIndices(
derivativeId->getParameterIndices(),
derivativeFnDecl->getInterfaceType()->castTo<AnyFunctionType>());
auto *loweredResultIndices = IndexSubset::get(getASTContext(), 1, {0});
auto diffFn = SGF.B.createDifferentiableFunction(
loc, loweredParamIndices, loweredResultIndices, originalFn);
auto derivativeFn = SGF.B.createDifferentiableFunctionExtract(
loc, NormalDifferentiableFunctionTypeComponent(derivativeId->getKind()),
diffFn);
auto derivativeFnSILTy = SILType::getPrimitiveObjectType(constantTy);
SmallVector<SILValue, 4> args(thunk->getArguments().begin(),
thunk->getArguments().end());
auto apply =
SGF.emitApplyWithRethrow(loc, derivativeFn, derivativeFnSILTy,
SGF.getForwardingSubstitutionMap(), args);
SGF.B.createReturn(loc, apply);
return thunk;
}