| //===--- 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 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::getDynamicThunk(SILDeclRef constant, |
| SILConstantInfo constantInfo) { |
| // Mangle the constant with a _TTD header. |
| auto name = constant.mangle(SILDeclRef::ManglingKind::DynamicThunk); |
| |
| auto F = M.getOrCreateFunction(constant.getDecl(), name, SILLinkage::Shared, |
| constantInfo.SILFnType, IsBare, IsTransparent, |
| IsSerializable, 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, |
| SILConstantInfo constantInfo) { |
| // 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, constantInfo); |
| |
| return B.createFunctionRef(loc, F); |
| } |
| |
| static SILValue getNextUncurryLevelRef(SILGenFunction &gen, |
| SILLocation loc, |
| SILDeclRef next, |
| bool direct, |
| SILValue selfArg, |
| SubstitutionList curriedSubs) { |
| if (next.isForeign || next.isCurried || !next.hasDecl() || direct) |
| return gen.emitGlobalFunctionRef(loc, next.asForeign(false)); |
| |
| auto constantInfo = gen.SGM.Types.getConstantInfo(next); |
| |
| if (isa<AbstractFunctionDecl>(next.getDecl()) && |
| getMethodDispatch(cast<AbstractFunctionDecl>(next.getDecl())) |
| == MethodDispatch::Class) { |
| // Use the dynamic thunk if dynamic. |
| if (next.getDecl()->isDynamic()) { |
| auto dynamicThunk = gen.SGM.getDynamicThunk(next, constantInfo); |
| return gen.B.createFunctionRef(loc, dynamicThunk); |
| } |
| |
| return gen.B.createClassMethod(loc, selfArg, next); |
| } |
| |
| // If the fully-uncurried reference is to a generic method, look up the |
| // witness. |
| if (constantInfo.SILFnType->getRepresentation() |
| == SILFunctionTypeRepresentation::WitnessMethod) { |
| // FIXME: This is wrong if the requirement makes Self non-canonical, |
| // eg <Self, T where Self : P, T : Q, T.X == Self> |
| auto selfType = curriedSubs[0].getReplacement()->getCanonicalType(); |
| assert(isa<ArchetypeType>(selfType) && "no archetype for witness?!"); |
| SILValue OpenedExistential; |
| if (!cast<ArchetypeType>(selfType)->getOpenedExistentialType().isNull()) |
| OpenedExistential = selfArg; |
| auto protocol = |
| next.getDecl()->getDeclContext()->getAsProtocolOrProtocolExtensionContext(); |
| auto conformance = ProtocolConformanceRef(protocol); |
| return gen.B.createWitnessMethod(loc, selfType, conformance, next, |
| constantInfo.getSILType(), |
| OpenedExistential); |
| } |
| |
| // Otherwise, emit a direct call. |
| return gen.emitGlobalFunctionRef(loc, next); |
| } |
| |
| void SILGenFunction::emitCurryThunk(ValueDecl *vd, |
| SILDeclRef from, SILDeclRef to) { |
| #ifndef NDEBUG |
| assert(from.uncurryLevel == 0 && to.uncurryLevel == 1 |
| && "currying function at level other than one?!"); |
| |
| if (auto *fd = dyn_cast<AbstractFunctionDecl>(vd)) { |
| assert(!SGM.M.Types.hasLoweredLocalCaptures(fd) && |
| "methods cannot have captures"); |
| } |
| #endif |
| |
| 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, to, from.isDirectReference, |
| 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(from).SILFnType, SGM.M); |
| SILType resultTy = fromConv.getSingleSILResultType(); |
| resultTy = F.mapTypeIntoContext(resultTy); |
| auto substTy = toFn->getType().substGenericArgs(SGM.M, subs); |
| |
| // Partially apply the next uncurry level and return the result closure. |
| auto closureTy = |
| SILGenBuilder::getPartialApplyResultType(toFn->getType(), /*appliedParams=*/1, |
| SGM.M, subs, |
| ParameterConvention::Direct_Owned); |
| SILInstruction *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(ValueDecl *fd, |
| SILDeclRef entryPoint, |
| SILDeclRef nextEntryPoint) { |
| // Thunks are always emitted by need, so don't need delayed emission. |
| SILFunction *f = getFunction(entryPoint, ForDefinition); |
| f->setThunk(IsThunk); |
| f->setBare(IsBare); |
| |
| preEmitFunction(entryPoint, fd, f, fd); |
| PrettyStackTraceSILFunction X("silgen emitCurryThunk", f); |
| |
| SILGenFunction(*this, *f) |
| .emitCurryThunk(fd, entryPoint, nextEntryPoint); |
| postEmitFunction(entryPoint, 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) { |
| auto vd = constant.getDecl(); |
| // Reference the next uncurrying level of the function. |
| SILDeclRef next = SILDeclRef(vd, constant.kind, |
| SILDeclRef::ConstructAtBestResilienceExpansion, |
| constant.uncurryLevel + 1); |
| // If the function is fully uncurried and natively foreign, reference its |
| // foreign entry point. |
| if (!next.isCurried) { |
| if (requiresForeignToNativeThunk(vd)) |
| next = next.asForeign(); |
| } |
| |
| // Preserve whether the curry thunks lead to a direct reference to the |
| // method implementation. |
| next = next.asDirectReference(constant.isDirectReference); |
| |
| SGM.emitCurryThunk(vd, constant, next); |
| } |
| // Otherwise, if this is a calling convention thunk we haven't emitted yet, |
| // emit it. |
| 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(GenericEnvironment *genericEnv, |
| CanSILFunctionType thunkType, |
| CanSILFunctionType fromType, |
| CanSILFunctionType toType, |
| IsSerialized_t Serialized) { |
| // Mangle the reabstraction thunk. |
| // Substitute context parameters out of the "from" and "to" types. |
| auto fromInterfaceType |
| = GenericEnvironment::mapTypeOutOfContext(genericEnv, fromType) |
| ->getCanonicalType(); |
| auto toInterfaceType |
| = GenericEnvironment::mapTypeOutOfContext(genericEnv, toType) |
| ->getCanonicalType(); |
| |
| Mangle::ASTMangler NewMangler; |
| std::string name = NewMangler.mangleReabstractionThunkHelper(thunkType, |
| fromInterfaceType, toInterfaceType, M.getSwiftModule()); |
| |
| auto loc = RegularLocation::getAutoGeneratedLocation(); |
| return M.getOrCreateSharedFunction(loc, name, thunkType, IsBare, |
| IsTransparent, IsSerializable, |
| IsReabstractionThunk); |
| } |