| //===--- 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; |
| } |