diff --git a/docs/ABI/Mangling.rst b/docs/ABI/Mangling.rst index 6873ce5b321d3..25f964624d276 100644 --- a/docs/ABI/Mangling.rst +++ b/docs/ABI/Mangling.rst @@ -223,6 +223,8 @@ types where the metadata itself has unknown layout.) global ::= global 'TI' // implementation of a dynamic_replaceable function global ::= global 'Tu' // async function pointer of a function global ::= global 'TX' // function pointer of a dynamic_replaceable function + global ::= global 'Twb' // back deployment thunk + global ::= global 'TwB' // back deployment fallback function global ::= entity entity 'TV' // vtable override thunk, derived followed by base global ::= type label-list? 'D' // type mangling for the debugger with label list for function types. global ::= type 'TC' // continuation prototype (not actually used for real symbols) diff --git a/include/swift/AST/ASTMangler.h b/include/swift/AST/ASTMangler.h index 31fca8e1fad20..f0fe872729d68 100644 --- a/include/swift/AST/ASTMangler.h +++ b/include/swift/AST/ASTMangler.h @@ -105,7 +105,9 @@ class ASTMangler : public Mangler { ObjCAsSwiftThunk, DistributedThunk, DistributedAccessor, - AccessibleFunctionRecord + AccessibleFunctionRecord, + BackDeploymentThunk, + BackDeploymentFallback, }; ASTMangler(bool DWARFMangling = false) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index efcd68dfefe42..f796652e0b1ad 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -6268,6 +6268,9 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl { /// Returns 'true' if the function is distributed. bool isDistributed() const; + /// Returns 'true' if the function has the @c @_backDeploy attribute. + bool isBackDeployed() const; + PolymorphicEffectKind getPolymorphicEffectKind(EffectKind kind) const; // FIXME: Hack that provides names with keyword arguments for accessors. diff --git a/include/swift/Demangling/DemangleNodes.def b/include/swift/Demangling/DemangleNodes.def index aedcf44c65c25..1a2d812a57951 100644 --- a/include/swift/Demangling/DemangleNodes.def +++ b/include/swift/Demangling/DemangleNodes.def @@ -332,6 +332,8 @@ NODE(CompileTimeConst) // Added in Swift 5.7 NODE(OpaqueReturnTypeIndexed) +NODE(BackDeploymentThunk) +NODE(BackDeploymentFallback) #undef CONTEXT_NODE #undef NODE diff --git a/include/swift/SIL/SILDeclRef.h b/include/swift/SIL/SILDeclRef.h index 980ea7bd50cf7..4113b70998c6e 100644 --- a/include/swift/SIL/SILDeclRef.h +++ b/include/swift/SIL/SILDeclRef.h @@ -162,6 +162,21 @@ struct SILDeclRef { AsyncEntryPoint, }; + /// Represents the variants of a back deployable function. + enum class BackDeploymentKind : unsigned { + /// Default value. If a SILDecRef references a function that has been back + /// deployed and has this back deployment kind, then it references the + /// original ABI stable function. + None, + /// The thunk variant of a function that calls either the original function + /// or the fallback variant if the original is unavailable. This thunk will + /// be emitted with PublicNonABI linkage. + Thunk, + /// The fallback variant of the function. This function will be emitted with + /// PublicNonABI linkage. + Fallback, + }; + /// The AST node represented by this SILDeclRef. Loc loc; /// The Kind of this SILDeclRef. @@ -170,6 +185,8 @@ struct SILDeclRef { unsigned isForeign : 1; /// True if this references a distributed function. unsigned isDistributed : 1; + /// The BackDeploymentKind of this SILDeclRef. + BackDeploymentKind backDeploymentKind : 2; /// The default argument index for a default argument getter. unsigned defaultArgIndex : 10; @@ -204,13 +221,15 @@ struct SILDeclRef { /// Produces a null SILDeclRef. SILDeclRef() - : loc(), kind(Kind::Func), isForeign(0), isDistributed(0), defaultArgIndex(0) {} + : loc(), kind(Kind::Func), isForeign(0), isDistributed(0), + backDeploymentKind(BackDeploymentKind::None), defaultArgIndex(0) {} /// Produces a SILDeclRef of the given kind for the given decl. explicit SILDeclRef( ValueDecl *decl, Kind kind, bool isForeign = false, bool isDistributed = false, + BackDeploymentKind backDeploymentKind = BackDeploymentKind::None, AutoDiffDerivativeFunctionIdentifier *derivativeId = nullptr); /// Produces a SILDeclRef for the given ValueDecl or @@ -224,7 +243,10 @@ struct SILDeclRef { /// for the containing ClassDecl. /// - If 'loc' is a global VarDecl, this returns its GlobalAccessor /// SILDeclRef. - explicit SILDeclRef(Loc loc, bool isForeign = false, bool isDistributed = false); + explicit SILDeclRef( + Loc loc, + bool isForeign = false, + bool isDistributed = false); /// See above put produces a prespecialization according to the signature. explicit SILDeclRef(Loc loc, GenericSignature prespecializationSig); @@ -360,6 +382,7 @@ struct SILDeclRef { return loc.getOpaqueValue() == rhs.loc.getOpaqueValue() && kind == rhs.kind && isForeign == rhs.isForeign && isDistributed == rhs.isDistributed && + backDeploymentKind == rhs.backDeploymentKind && defaultArgIndex == rhs.defaultArgIndex && pointer == rhs.pointer; } @@ -378,6 +401,7 @@ struct SILDeclRef { return SILDeclRef(loc.getOpaqueValue(), kind, /*foreign=*/foreign, /*distributed=*/false, + backDeploymentKind, defaultArgIndex, pointer.get()); } @@ -387,6 +411,16 @@ struct SILDeclRef { return SILDeclRef(loc.getOpaqueValue(), kind, /*foreign=*/false, /*distributed=*/distributed, + backDeploymentKind, + defaultArgIndex, + pointer.get()); + } + /// Returns a copy of the decl with the given back deployment kind. + SILDeclRef asBackDeploymentKind(BackDeploymentKind backDeploymentKind) const { + return SILDeclRef(loc.getOpaqueValue(), kind, + isForeign, + isDistributed, + backDeploymentKind, defaultArgIndex, pointer.get()); } @@ -431,6 +465,14 @@ struct SILDeclRef { /// True if the decl ref references a thunk handling potentially distributed actor functions bool isDistributedThunk() const; + /// True if the decl ref references a thunk handling a call to a function that + /// supports back deployment. + bool isBackDeploymentThunk() const; + + /// True if the decl ref references a function that is the back deployment + /// fallback for an original function which may be unavailable at runtime. + bool isBackDeploymentFallback() const; + /// True if the decl ref references a method which introduces a new vtable /// entry. bool requiresNewVTableEntry() const; @@ -508,10 +550,12 @@ struct SILDeclRef { explicit SILDeclRef(void *opaqueLoc, Kind kind, bool isForeign, bool isDistributed, + BackDeploymentKind backDeploymentKind, unsigned defaultArgIndex, AutoDiffDerivativeFunctionIdentifier *derivativeId) : loc(Loc::getFromOpaqueValue(opaqueLoc)), kind(kind), isForeign(isForeign), isDistributed(isDistributed), + backDeploymentKind(backDeploymentKind), defaultArgIndex(defaultArgIndex), pointer(derivativeId) {} }; @@ -529,17 +573,18 @@ namespace llvm { template<> struct DenseMapInfo { using SILDeclRef = swift::SILDeclRef; using Kind = SILDeclRef::Kind; + using BackDeploymentKind = SILDeclRef::BackDeploymentKind; using Loc = SILDeclRef::Loc; using PointerInfo = DenseMapInfo; using UnsignedInfo = DenseMapInfo; static SILDeclRef getEmptyKey() { - return SILDeclRef(PointerInfo::getEmptyKey(), Kind::Func, false, false, 0, - nullptr); + return SILDeclRef(PointerInfo::getEmptyKey(), Kind::Func, false, false, + BackDeploymentKind::None, 0, nullptr); } static SILDeclRef getTombstoneKey() { return SILDeclRef(PointerInfo::getTombstoneKey(), Kind::Func, false, false, - 0, nullptr); + BackDeploymentKind::None, 0, nullptr); } static unsigned getHashValue(swift::SILDeclRef Val) { unsigned h1 = PointerInfo::getHashValue(Val.loc.getOpaqueValue()); @@ -550,7 +595,9 @@ template<> struct DenseMapInfo { unsigned h4 = UnsignedInfo::getHashValue(Val.isForeign); unsigned h5 = PointerInfo::getHashValue(Val.pointer.getOpaqueValue()); unsigned h6 = UnsignedInfo::getHashValue(Val.isDistributed); - return h1 ^ (h2 << 4) ^ (h3 << 9) ^ (h4 << 7) ^ (h5 << 11) ^ (h6 << 8); + unsigned h7 = UnsignedInfo::getHashValue(unsigned(Val.backDeploymentKind)); + return h1 ^ (h2 << 4) ^ (h3 << 9) ^ (h4 << 7) ^ (h5 << 11) ^ (h6 << 8) ^ + (h7 << 10); } static bool isEqual(swift::SILDeclRef const &LHS, swift::SILDeclRef const &RHS) { diff --git a/lib/AST/ASTMangler.cpp b/lib/AST/ASTMangler.cpp index ed73355a46261..78db761c4be1d 100644 --- a/lib/AST/ASTMangler.cpp +++ b/lib/AST/ASTMangler.cpp @@ -857,6 +857,8 @@ void ASTMangler::appendSymbolKind(SymbolKind SKind) { case SymbolKind::DistributedThunk: return appendOperator("TE"); case SymbolKind::DistributedAccessor: return appendOperator("TF"); case SymbolKind::AccessibleFunctionRecord: return appendOperator("HF"); + case SymbolKind::BackDeploymentThunk: return appendOperator("Twb"); + case SymbolKind::BackDeploymentFallback: return appendOperator("TwB"); } } diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index fbb26895c6dad..82709d0e3023b 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -7531,6 +7531,10 @@ bool AbstractFunctionDecl::isSendable() const { return getAttrs().hasAttribute(); } +bool AbstractFunctionDecl::isBackDeployed() const { + return getAttrs().hasAttribute(); +} + BraceStmt *AbstractFunctionDecl::getBody(bool canSynthesize) const { if ((getBodyKind() == BodyKind::Synthesize || getBodyKind() == BodyKind::Unparsed) && diff --git a/lib/Demangling/Demangler.cpp b/lib/Demangling/Demangler.cpp index 0afd4c1a3f531..9c8b21ea89f82 100644 --- a/lib/Demangling/Demangler.cpp +++ b/lib/Demangling/Demangler.cpp @@ -139,6 +139,8 @@ bool swift::Demangle::isFunctionAttr(Node::Kind kind) { case Node::Kind::AsyncAwaitResumePartialFunction: case Node::Kind::AsyncSuspendResumePartialFunction: case Node::Kind::AccessibleFunctionRecord: + case Node::Kind::BackDeploymentThunk: + case Node::Kind::BackDeploymentFallback: return true; default: return false; @@ -2641,6 +2643,13 @@ NodePointer Demangler::demangleThunkOrSpecialization() { return demangleAutoDiffFunctionOrSimpleThunk( Node::Kind::AutoDiffFunction); } + case 'w': + switch (nextChar()) { + case 'b': return createNode(Node::Kind::BackDeploymentThunk); + case 'B': return createNode(Node::Kind::BackDeploymentFallback); + default: + return nullptr; + } default: return nullptr; } diff --git a/lib/Demangling/NodePrinter.cpp b/lib/Demangling/NodePrinter.cpp index a6875826bf810..7a3dc8961a806 100644 --- a/lib/Demangling/NodePrinter.cpp +++ b/lib/Demangling/NodePrinter.cpp @@ -591,6 +591,8 @@ class NodePrinter { case Node::Kind::AsyncAwaitResumePartialFunction: case Node::Kind::AsyncSuspendResumePartialFunction: case Node::Kind::AccessibleFunctionRecord: + case Node::Kind::BackDeploymentThunk: + case Node::Kind::BackDeploymentFallback: return false; } printer_unreachable("bad node kind"); @@ -2050,6 +2052,14 @@ NodePointer NodePrinter::print(NodePointer Node, unsigned depth, Printer << "dynamically replaceable variable for "; } return nullptr; + case Node::Kind::BackDeploymentThunk: + if (!Options.ShortenThunk) { + Printer << "back deployment thunk for "; + } + return nullptr; + case Node::Kind::BackDeploymentFallback: + Printer << "back deployment fallback for "; + return nullptr; case Node::Kind::ProtocolSymbolicReference: Printer << "protocol symbolic reference 0x"; Printer.writeHex(Node->getIndex()); diff --git a/lib/Demangling/OldRemangler.cpp b/lib/Demangling/OldRemangler.cpp index 18464f608e286..a667e220882fd 100644 --- a/lib/Demangling/OldRemangler.cpp +++ b/lib/Demangling/OldRemangler.cpp @@ -2800,3 +2800,14 @@ ManglingError Remangler::mangleAccessibleFunctionRecord(Node *node, Buffer << "HF"; return ManglingError::Success; } + +ManglingError Remangler::mangleBackDeploymentThunk(Node *node, unsigned depth) { + Buffer << "Twb"; + return ManglingError::Success; +} + +ManglingError Remangler::mangleBackDeploymentFallback(Node *node, + unsigned depth) { + Buffer << "TwB"; + return ManglingError::Success; +} diff --git a/lib/Demangling/Remangler.cpp b/lib/Demangling/Remangler.cpp index 89dec2bee5ef8..5b3bce92dda07 100644 --- a/lib/Demangling/Remangler.cpp +++ b/lib/Demangling/Remangler.cpp @@ -1627,6 +1627,8 @@ ManglingError Remangler::mangleGlobal(Node *node, unsigned depth) { case Node::Kind::AsyncAwaitResumePartialFunction: case Node::Kind::AsyncSuspendResumePartialFunction: case Node::Kind::AccessibleFunctionRecord: + case Node::Kind::BackDeploymentThunk: + case Node::Kind::BackDeploymentFallback: mangleInReverseOrder = true; break; default: @@ -3407,6 +3409,18 @@ ManglingError Remangler::mangleAccessibleFunctionRecord(Node *node, return ManglingError::Success; } +ManglingError Remangler::mangleBackDeploymentThunk(Node *node, + unsigned depth) { + Buffer << "Twb"; + return ManglingError::Success; +} + +ManglingError Remangler::mangleBackDeploymentFallback(Node *node, + unsigned depth) { + Buffer << "TwB"; + return ManglingError::Success; +} + } // anonymous namespace /// The top-level interface to the remangler. diff --git a/lib/SIL/IR/SILDeclRef.cpp b/lib/SIL/IR/SILDeclRef.cpp index bf187c57a9888..5c224b072d39c 100644 --- a/lib/SIL/IR/SILDeclRef.cpp +++ b/lib/SIL/IR/SILDeclRef.cpp @@ -121,14 +121,18 @@ bool swift::requiresForeignEntryPoint(ValueDecl *vd) { return false; } -SILDeclRef::SILDeclRef(ValueDecl *vd, SILDeclRef::Kind kind, - bool isForeign, bool isDistributed, +SILDeclRef::SILDeclRef(ValueDecl *vd, SILDeclRef::Kind kind, bool isForeign, + bool isDistributed, + SILDeclRef::BackDeploymentKind backDeploymentKind, AutoDiffDerivativeFunctionIdentifier *derivativeId) : loc(vd), kind(kind), isForeign(isForeign), isDistributed(isDistributed), - defaultArgIndex(0), pointer(derivativeId) {} + backDeploymentKind(backDeploymentKind), defaultArgIndex(0), + pointer(derivativeId) {} -SILDeclRef::SILDeclRef(SILDeclRef::Loc baseLoc, bool asForeign, bool asDistributed) - : defaultArgIndex(0), +SILDeclRef::SILDeclRef(SILDeclRef::Loc baseLoc, bool asForeign, + bool asDistributed) + : backDeploymentKind(SILDeclRef::BackDeploymentKind::None), + defaultArgIndex(0), pointer((AutoDiffDerivativeFunctionIdentifier *)nullptr) { if (auto *vd = baseLoc.dyn_cast()) { if (auto *fd = dyn_cast(vd)) { @@ -202,7 +206,8 @@ ASTContext &SILDeclRef::getASTContext() const { } bool SILDeclRef::isThunk() const { - return isForeignToNativeThunk() || isNativeToForeignThunk() || isDistributedThunk(); + return isForeignToNativeThunk() || isNativeToForeignThunk() || + isDistributedThunk() || isBackDeploymentThunk(); } bool SILDeclRef::isClangImported() const { @@ -314,6 +319,11 @@ SILLinkage SILDeclRef::getLinkage(ForDefinition_t forDefinition) const { return maybeAddExternal(SILLinkage::PublicNonABI); } + // Back deployment thunks and fallbacks are emitted into the client and + // therefore have PublicNonABI linkage. + if (backDeploymentKind != SILDeclRef::BackDeploymentKind::None) + return maybeAddExternal(SILLinkage::PublicNonABI); + enum class Limit { /// No limit. None, @@ -728,9 +738,8 @@ bool SILDeclRef::isAlwaysInline() const { } bool SILDeclRef::isAnyThunk() const { - return isForeignToNativeThunk() || - isNativeToForeignThunk() || - isDistributedThunk(); + return isForeignToNativeThunk() || isNativeToForeignThunk() || + isDistributedThunk() || isBackDeploymentThunk(); } bool SILDeclRef::isForeignToNativeThunk() const { @@ -784,6 +793,18 @@ bool SILDeclRef::isDistributedThunk() const { return kind == Kind::Func; } +bool SILDeclRef::isBackDeploymentFallback() const { + if (backDeploymentKind != BackDeploymentKind::Fallback) + return false; + return kind == Kind::Func; +} + +bool SILDeclRef::isBackDeploymentThunk() const { + if (backDeploymentKind != BackDeploymentKind::Thunk) + return false; + return kind == Kind::Func; +} + /// Use the Clang importer to mangle a Clang declaration. static void mangleClangDecl(raw_ostream &buffer, const clang::NamedDecl *clangDecl, @@ -864,6 +885,10 @@ std::string SILDeclRef::mangle(ManglingKind MKind) const { SKind = ASTMangler::SymbolKind::ObjCAsSwiftThunk; } else if (isDistributedThunk()) { SKind = ASTMangler::SymbolKind::DistributedThunk; + } else if (isBackDeploymentThunk()) { + SKind = ASTMangler::SymbolKind::BackDeploymentThunk; + } else if (isBackDeploymentFallback()) { + SKind = ASTMangler::SymbolKind::BackDeploymentFallback; } break; case SILDeclRef::ManglingKind::DynamicThunk: @@ -995,6 +1020,8 @@ bool SILDeclRef::requiresNewVTableEntry() const { return false; if (isDistributedThunk()) return false; + if (isBackDeploymentThunk()) + return false; auto fnDecl = dyn_cast(getDecl()); if (!fnDecl) return false; @@ -1022,6 +1049,9 @@ SILDeclRef SILDeclRef::getOverridden() const { SILDeclRef SILDeclRef::getNextOverriddenVTableEntry() const { if (auto overridden = getOverridden()) { + // Back deployed methods should not be overridden. + assert(backDeploymentKind == SILDeclRef::BackDeploymentKind::None); + // If we overrode a foreign decl or dynamic method, if this is an // accessor for a property that overrides an ObjC decl, or if it is an // @NSManaged property, then it won't be in the vtable. @@ -1031,7 +1061,7 @@ SILDeclRef SILDeclRef::getNextOverriddenVTableEntry() const { // Distributed thunks are not in the vtable. if (isDistributedThunk()) return SILDeclRef(); - + // An @objc convenience initializer can be "overridden" in the sense that // its selector is reclaimed by a subclass's convenience init with the // same name. The AST models this as an override for the purposes of @@ -1313,6 +1343,8 @@ bool SILDeclRef::canBeDynamicReplacement() const { return false; if (isDistributedThunk()) return false; + if (backDeploymentKind != SILDeclRef::BackDeploymentKind::None) + return false; if (kind == SILDeclRef::Kind::Destroyer || kind == SILDeclRef::Kind::DefaultArgGenerator) return false; @@ -1332,6 +1364,9 @@ bool SILDeclRef::isDynamicallyReplaceable() const { if (isDistributedThunk()) return false; + if (backDeploymentKind != SILDeclRef::BackDeploymentKind::None) + return false; + if (kind == SILDeclRef::Kind::DefaultArgGenerator) return false; if (isStoredPropertyInitializer() || isPropertyWrapperBackingInitializer()) diff --git a/lib/SIL/Parser/ParseSIL.cpp b/lib/SIL/Parser/ParseSIL.cpp index da25949a81b50..f936b73b7066b 100644 --- a/lib/SIL/Parser/ParseSIL.cpp +++ b/lib/SIL/Parser/ParseSIL.cpp @@ -1518,7 +1518,8 @@ bool SILParser::parseSILDeclRef(SILDeclRef &Result, if (!P.consumeIf(tok::sil_exclamation)) { // Construct SILDeclRef. - Result = SILDeclRef(VD, Kind, IsObjC, /*distributed=*/false, DerivativeId); + Result = SILDeclRef(VD, Kind, IsObjC, /*distributed=*/false, + SILDeclRef::BackDeploymentKind::None, DerivativeId); return false; } @@ -1638,7 +1639,8 @@ bool SILParser::parseSILDeclRef(SILDeclRef &Result, } while (P.consumeIf(tok::period)); // Construct SILDeclRef. - Result = SILDeclRef(VD, Kind, IsObjC, /*distributed=*/false, DerivativeId); + Result = SILDeclRef(VD, Kind, IsObjC, /*distributed=*/false, + SILDeclRef::BackDeploymentKind::None, DerivativeId); return false; } diff --git a/lib/SILGen/CMakeLists.txt b/lib/SILGen/CMakeLists.txt index 94090fa995aff..8edd5e6b82682 100644 --- a/lib/SILGen/CMakeLists.txt +++ b/lib/SILGen/CMakeLists.txt @@ -11,6 +11,7 @@ add_swift_host_library(swiftSILGen STATIC SwitchEnumBuilder.cpp SILGen.cpp SILGenApply.cpp + SILGenBackDeploy.cpp SILGenBridging.cpp SILGenBuilder.cpp SILGenBuiltin.cpp diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index f6efff04ee4dc..b1a5b594f9d2f 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -853,6 +853,23 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) { return; } + if (constant.isBackDeploymentThunk()) { + auto loc = constant.getAsRegularLocation(); + loc.markAutoGenerated(); + auto *dc = loc.getAsDeclContext(); + assert(dc); + + preEmitFunction(constant, f, loc); + PrettyStackTraceSILFunction X("silgen emitBackDeploymentThunk", f); + f->setBare(IsBare); + f->setThunk(IsThunk); + + SILGenFunction(*this, *f, dc).emitBackDeploymentThunk(constant); + + postEmitFunction(constant, f); + return; + } + switch (constant.kind) { case SILDeclRef::Kind::Func: { if (auto *ce = constant.getAbstractClosureExpr()) { @@ -1383,6 +1400,20 @@ void SILGenModule::emitAbstractFuncDecl(AbstractFunctionDecl *AFD) { auto thunk = SILDeclRef(AFD).asDistributed(); emitDistributedThunk(thunk); } + + if (AFD->isBackDeployed()) { + // Emit the fallback function that will be used when the original function + // is unavailable at runtime. + auto fallback = SILDeclRef(AFD).asBackDeploymentKind( + SILDeclRef::BackDeploymentKind::Fallback); + emitFunctionDefinition(fallback, getFunction(fallback, ForDefinition)); + + // Emit the thunk that either invokes the original function or the fallback + // function depending on the availability of the original. + auto thunk = SILDeclRef(AFD).asBackDeploymentKind( + SILDeclRef::BackDeploymentKind::Thunk); + emitBackDeploymentThunk(thunk); + } } void SILGenModule::emitFunction(FuncDecl *fd) { diff --git a/lib/SILGen/SILGen.h b/lib/SILGen/SILGen.h index 6593c8381075c..147c7e9810875 100644 --- a/lib/SILGen/SILGen.h +++ b/lib/SILGen/SILGen.h @@ -335,7 +335,12 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor { /// Emits a thunk from an actor function to a potentially distributed call. void emitDistributedThunk(SILDeclRef thunk); - + + /// Emits a thunk that calls either the original function if it is available + /// or otherwise calls a fallback variant of the function that was emitted + /// into the client module. + void emitBackDeploymentThunk(SILDeclRef thunk); + void preEmitFunction(SILDeclRef constant, SILFunction *F, SILLocation L); void postEmitFunction(SILDeclRef constant, SILFunction *F); diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index 551eaaec4fdae..ef487058983f2 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -1030,6 +1030,11 @@ class SILGenApply : public Lowering::ExprVisitor { } void processClassMethod(DeclRefExpr *e, AbstractFunctionDecl *afd) { + // FIXME(backDeploy): Investigate support for back deployed class method + // calls + assert(!afd->isBackDeployed() && + "back deployed method calls are unsupported"); + ArgumentSource selfArgSource(selfApply->getBase()); setSelfParam(std::move(selfArgSource)); @@ -1119,6 +1124,12 @@ class SILGenApply : public Lowering::ExprVisitor { auto constant = SILDeclRef(e->getDecl()); if (callSite && callSite->shouldApplyDistributedThunk()) { constant = constant.asDistributed(true); + } else if (afd->isBackDeployed()) { + // If we're calling a back deployed function then we need to call a + // thunk instead that will handle the fallback when the original + // function is unavailable at runtime. + constant = + constant.asBackDeploymentKind(SILDeclRef::BackDeploymentKind::Thunk); } else { constant = constant.asForeign( !isConstructorWithGeneratedAllocatorThunk(e->getDecl()) @@ -5137,6 +5148,9 @@ RValue SILGenFunction::emitApplyMethod(SILLocation loc, ConcreteDeclRef declRef, if (call->isDistributed()) { callRef = callRef.asDistributed(true); + } else if (call->isBackDeployed()) { + callRef = + callRef.asBackDeploymentKind(SILDeclRef::BackDeploymentKind::Thunk); } auto declRefConstant = getConstantInfo(getTypeExpansionContext(), callRef); diff --git a/lib/SILGen/SILGenBackDeploy.cpp b/lib/SILGen/SILGenBackDeploy.cpp new file mode 100644 index 0000000000000..e79e94641f6e7 --- /dev/null +++ b/lib/SILGen/SILGenBackDeploy.cpp @@ -0,0 +1,194 @@ +//===--- SILGenBackDeploy.cpp - SILGen for back deployment ----------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 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 "SILGenFunctionBuilder.h" +#include "Scope.h" +#include "swift/SIL/SILDeclRef.h" + +using namespace swift; +using namespace Lowering; + +/// Given a value, extracts all elements to `result` from this value if it's a +/// tuple. Otherwise, add this value directly to `result`. +static void extractAllElements(SILValue val, SILLocation loc, + SILBuilder &builder, + SmallVectorImpl &result) { + auto &fn = builder.getFunction(); + auto tupleType = val->getType().getAs(); + if (!tupleType) { + result.push_back(val); + return; + } + if (!fn.hasOwnership()) { + for (auto i : range(tupleType->getNumElements())) + result.push_back(builder.createTupleExtract(loc, val, i)); + return; + } + if (tupleType->getNumElements() == 0) + return; + builder.emitDestructureValueOperation(loc, val, result); +} + +/// Emit the following branch SIL instruction: +/// \verbatim +/// if #available(OSVersion) { +/// +/// } else { +/// +/// } +/// \endverbatim +static void emitBackDeployIfAvailableCondition(SILGenFunction &SGF, + AbstractFunctionDecl *AFD, + SILLocation loc, + SILBasicBlock *availableBB, + SILBasicBlock *unavailableBB) { + PlatformKind platform = targetPlatform(SGF.SGM.getASTContext().LangOpts); + auto introduced = AFD->getIntroducedOSVersion(platform); + VersionRange OSVersion = VersionRange::empty(); + if (introduced.hasValue()) { + OSVersion = VersionRange::allGTE(*introduced); + } + + SILValue booleanTestValue; + if (OSVersion.isEmpty() || OSVersion.isAll()) { + // If there's no check for the current platform, this condition is + // trivially true. + SILType i1 = SILType::getBuiltinIntegerType(1, SGF.getASTContext()); + booleanTestValue = SGF.B.createIntegerLiteral(loc, i1, 1); + } else { + booleanTestValue = SGF.emitOSVersionRangeCheck(loc, OSVersion); + } + + SGF.B.createCondBranch(loc, booleanTestValue, availableBB, unavailableBB); +} + +/// Emits a function or method application, forwarding parameters. +static void emitBackDeployForwardApplyAndReturnOrThrow( + SILGenFunction &SGF, AbstractFunctionDecl *AFD, SILLocation loc, + SILDeclRef function, SmallVector ¶ms) { + // Only statically dispatched class methods are supported. + if (auto classDecl = dyn_cast(AFD->getDeclContext())) { + assert(classDecl->isFinal() || AFD->isFinal() || + AFD->hasForcedStaticDispatch()); + } + + TypeExpansionContext TEC = SGF.getTypeExpansionContext(); + auto fnType = SGF.SGM.Types.getConstantOverrideType(TEC, function); + auto silFnType = + SILType::getPrimitiveObjectType(fnType).castTo(); + SILFunctionConventions fnConv(silFnType, SGF.SGM.M); + + SILValue functionRef = SGF.emitGlobalFunctionRef(loc, function); + auto subs = SGF.F.getForwardingSubstitutionMap(); + SmallVector directResults; + + if (silFnType->hasErrorResult()) { + // Apply a throwing function and forward the results and the error to the + // return/throw blocks via intermediate basic blocks. The intermediates + // are needed to avoid forming critical edges. + SILBasicBlock *normalBB = SGF.createBasicBlock(); + SILBasicBlock *errorBB = SGF.createBasicBlock(); + + SGF.B.createTryApply(loc, functionRef, subs, params, normalBB, errorBB); + + // Emit error block. + SGF.B.emitBlock(errorBB); + SILValue error = errorBB->createPhiArgument(fnConv.getSILErrorType(TEC), + OwnershipKind::Owned); + SGF.B.createBranch(loc, SGF.ThrowDest.getBlock(), {error}); + + // Emit normal block. + SGF.B.emitBlock(normalBB); + SILValue result = normalBB->createPhiArgument(fnConv.getSILResultType(TEC), + OwnershipKind::Owned); + SmallVector directResults; + extractAllElements(result, loc, SGF.B, directResults); + + SGF.B.createBranch(loc, SGF.ReturnDest.getBlock(), directResults); + } else { + // Apply a non-throwing function and forward its results straight to the + // return block. + auto *apply = SGF.B.createApply(loc, functionRef, subs, params); + extractAllElements(apply, loc, SGF.B, directResults); + + SGF.B.createBranch(loc, SGF.ReturnDest.getBlock(), directResults); + } +} + +void SILGenFunction::emitBackDeploymentThunk(SILDeclRef thunk) { + // Generate code equivalent to: + // + // func X_thunk(...) async throws -> ... { + // if #available(...) { + // return try await X(...) + // } else { + // return try await X_fallback(...) + // } + // } + + assert(thunk.isBackDeploymentThunk()); + + auto loc = thunk.getAsRegularLocation(); + loc.markAutoGenerated(); + Scope scope(Cleanups, CleanupLocation(loc)); + auto FD = cast(thunk.getDecl()); + + F.setGenericEnvironment(SGM.Types.getConstantGenericEnvironment(thunk)); + + emitBasicProlog(FD->getParameters(), FD->getImplicitSelfDecl(), + FD->getResultInterfaceType(), FD, FD->hasThrows(), + FD->getThrowsLoc()); + prepareEpilog(FD->getResultInterfaceType(), FD->hasThrows(), + CleanupLocation(FD)); + + // Gather the entry block's arguments up so that we can forward them. + SmallVector paramsForForwarding; + SILBasicBlock *entryBlock = getFunction().getEntryBlock(); + for (SILArgument *arg : + make_range(entryBlock->args_begin(), entryBlock->args_end())) { + paramsForForwarding.emplace_back(arg); + } + + SILBasicBlock *availableBB = createBasicBlock("availableBB"); + SILBasicBlock *unavailableBB = createBasicBlock("unavailableBB"); + + // if #available(...) { + // + // } else { + // + // } + emitBackDeployIfAvailableCondition(*this, FD, loc, availableBB, + unavailableBB); + + // : + // return (try)? (await)? (self.)?X(...) + { + B.emitBlock(availableBB); + SILDeclRef original = + thunk.asBackDeploymentKind(SILDeclRef::BackDeploymentKind::None); + emitBackDeployForwardApplyAndReturnOrThrow(*this, FD, loc, original, + paramsForForwarding); + } + + // : + // return (try)? (await)? (self.)?X_fallback(...) + { + B.emitBlock(unavailableBB); + SILDeclRef fallback = + thunk.asBackDeploymentKind(SILDeclRef::BackDeploymentKind::Fallback); + emitBackDeployForwardApplyAndReturnOrThrow(*this, FD, loc, fallback, + paramsForForwarding); + } + + emitEpilog(FD); +} diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index 644ce60a55983..79703bf9f9f9d 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -2034,6 +2034,14 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction CanSILFunctionType toType, bool reorderSelf); + //===--------------------------------------------------------------------===// + // Back Deployment thunks + //===--------------------------------------------------------------------===// + + /// Invokes an original function if it is available at runtime. Otherwise, + /// invokes a fallback copy of the function emitted into the client. + void emitBackDeploymentThunk(SILDeclRef thunk); + //===---------------------------------------------------------------------===// // Distributed Actors //===---------------------------------------------------------------------===// diff --git a/lib/SILGen/SILGenThunk.cpp b/lib/SILGen/SILGenThunk.cpp index ff968bc6314dc..2ddd40c854a6b 100644 --- a/lib/SILGen/SILGenThunk.cpp +++ b/lib/SILGen/SILGenThunk.cpp @@ -112,6 +112,12 @@ void SILGenModule::emitDistributedThunk(SILDeclRef thunk) { emitFunctionDefinition(thunk, getFunction(thunk, ForDefinition)); } +void SILGenModule::emitBackDeploymentThunk(SILDeclRef thunk) { + // Thunks are always emitted by need, so don't need delayed emission. + assert(thunk.isBackDeploymentThunk() && "back deployment thunks only"); + emitFunctionDefinition(thunk, getFunction(thunk, ForDefinition)); +} + SILValue SILGenFunction::emitGlobalFunctionRef(SILLocation loc, SILDeclRef constant, SILConstantInfo constantInfo, diff --git a/test/Demangle/Inputs/manglings.txt b/test/Demangle/Inputs/manglings.txt index a04cd0ab889c7..6566f3957c4d8 100644 --- a/test/Demangle/Inputs/manglings.txt +++ b/test/Demangle/Inputs/manglings.txt @@ -425,4 +425,5 @@ $s16distributed_test1XC7computeyS2iFTF ---> distributed accessor for distributed $s27distributed_actor_accessors7MyActorC7simple2ySSSiFTETFHF ---> accessible function runtime record for distributed accessor for distributed thunk for distributed_actor_accessors.MyActor.simple2(Swift.Int) -> Swift.String $s1A3bar1aySSYt_tF ---> A.bar(a: _const Swift.String) -> () $s1t1fyyFSiAA3StrVcs7KeyPathCyADSiGcfu_SiADcfu0_33_556644b740b1b333fecb81e55a7cce98ADSiTf3npk_n ---> function signature specialization ]> of implicit closure #2 (t.Str) -> Swift.Int in implicit closure #1 (Swift.KeyPath) -> (t.Str) -> Swift.Int in t.f() -> () - +$s21back_deploy_attribute0A12DeployedFuncyyFTwb ---> back deployment thunk for back_deploy_attribute.backDeployedFunc() -> () +$s21back_deploy_attribute0A12DeployedFuncyyFTwB ---> back deployment fallback for back_deploy_attribute.backDeployedFunc() -> () diff --git a/test/ModuleInterface/back-deploy-attr.swift b/test/ModuleInterface/back-deploy-attr.swift index ee0bc6cbd1317..0357d7eb5d967 100644 --- a/test/ModuleInterface/back-deploy-attr.swift +++ b/test/ModuleInterface/back-deploy-attr.swift @@ -13,8 +13,6 @@ // RUN: %target-swift-frontend -emit-module -o /dev/null -merge-modules %t/Test.swiftmodule -disable-objc-attr-requires-foundation-module -emit-module-interface-path %t/TestFromModule.swiftinterface -module-name Test // RUN: %FileCheck %s --check-prefix FROMMODULE --check-prefix CHECK < %t/TestFromModule.swiftinterface -// FIXME(backDeploy): Verify that function bodies are emitted - public struct TopLevelStruct { // CHECK: @_backDeploy(macOS 11.0) // FROMSOURCE: public func backDeployedFunc1_SinglePlatform() -> Swift.Int { return 42 } diff --git a/test/SILGen/back_deploy_attribute.swift b/test/SILGen/back_deploy_attribute.swift deleted file mode 100644 index 4ec673383a79d..0000000000000 --- a/test/SILGen/back_deploy_attribute.swift +++ /dev/null @@ -1,36 +0,0 @@ -// RUN: %target-swift-emit-silgen %s | %FileCheck %s -// RUN: %target-swift-emit-silgen %s -target %target-cpu-apple-macosx10.50 | %FileCheck %s -// RUN: %target-swift-emit-silgen %s -target %target-cpu-apple-macosx10.60 | %FileCheck %s -// REQUIRES: OS=macosx - -// CHECK-LABEL: sil [serialized] [available 10.51] [ossa] @$s21back_deploy_attribute0A12DeployedFuncyyF : $@convention(thin) () -> () -@available(macOS 10.51, *) -@_backDeploy(macOS 10.50) -public func backDeployedFunc() {} - -@available(macOS 10.50, *) -public struct TopLevelStruct { - // CHECK-LABEL: sil [serialized] [available 10.51] [ossa] @$s21back_deploy_attribute14TopLevelStructV0a8DeployedF4FuncyyF : $@convention(method) (TopLevelStruct) -> () - @available(macOS 10.51, *) - @_backDeploy(macOS 10.50) - public func backDeployedStructFunc() {} -} - - -// FIXME(backDeploy): Verify SIL in a caller that requires back deployment - - -// CHECK-LABLEL: sil hidden [available 10.51] [ossa] @$s21back_deploy_attribute21alwaysAvailableCalleryyAA14TopLevelStructVF : $@convention(thin) (TopLevelStruct) -> () -// CHECK: bb0(%0 : $TopLevelStruct): -@available(macOS 10.51, *) -func alwaysAvailableCaller(_ s: TopLevelStruct) { - /// This function's availability meets the minimum availability of the APIs, so - /// no back deployment logic is required. - - // CHECK: %2 = function_ref @$s21back_deploy_attribute0A12DeployedFuncyyF : $@convention(thin) () -> () - // CHECK: %3 = apply %2() : $@convention(thin) () -> () - backDeployedFunc() - // CHECK: %4 = function_ref @$s21back_deploy_attribute14TopLevelStructV0a8DeployedF4FuncyyF : $@convention(method) (TopLevelStruct) -> () - // CHECK: %5 = apply %4(%0) : $@convention(method) (TopLevelStruct) -> () - s.backDeployedStructFunc() -} diff --git a/test/SILGen/back_deploy_attribute_func.swift b/test/SILGen/back_deploy_attribute_func.swift new file mode 100644 index 0000000000000..305af8967369e --- /dev/null +++ b/test/SILGen/back_deploy_attribute_func.swift @@ -0,0 +1,89 @@ +// RUN: %target-swift-emit-sil -parse-as-library -module-name back_deploy %s -target %target-cpu-apple-macosx10.50 -verify +// RUN: %target-swift-emit-silgen -parse-as-library -module-name back_deploy %s | %FileCheck %s +// RUN: %target-swift-emit-silgen -parse-as-library -module-name back_deploy %s -target %target-cpu-apple-macosx10.50 | %FileCheck %s +// RUN: %target-swift-emit-silgen -parse-as-library -module-name back_deploy %s -target %target-cpu-apple-macosx10.60 | %FileCheck %s + +// REQUIRES: OS=macosx + +// -- Fallback definition of trivialFunc() +// CHECK-LABEL: sil non_abi [serialized] [available 10.51] [ossa] @$s11back_deploy11trivialFuncyyFTwB : $@convention(thin) () -> () +// CHECK: bb0: +// CHECK: [[RESULT:%.*]] = tuple () +// CHECK: return [[RESULT]] : $() + +// -- Back deployment thunk for trivialFunc() +// CHECK-LABEL: sil non_abi [serialized] [thunk] [available 10.51] [ossa] @$s11back_deploy11trivialFuncyyFTwb : $@convention(thin) () -> () +// CHECK: bb0: +// CHECK: [[MAJOR:%.*]] = integer_literal $Builtin.Word, 10 +// CHECK: [[MINOR:%.*]] = integer_literal $Builtin.Word, 51 +// CHECK: [[PATCH:%.*]] = integer_literal $Builtin.Word, 0 +// CHECK: [[OSVFN:%.*]] = function_ref @$ss26_stdlib_isOSVersionAtLeastyBi1_Bw_BwBwtF : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1 +// CHECK: [[AVAIL:%.*]] = apply [[OSVFN]]([[MAJOR]], [[MINOR]], [[PATCH]]) : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1 +// CHECK: cond_br [[AVAIL]], [[AVAIL_BB:bb[0-9]+]], [[UNAVAIL_BB:bb[0-9]+]] +// +// CHECK: [[UNAVAIL_BB]]: +// CHECK: [[FALLBACKFN:%.*]] = function_ref @$s11back_deploy11trivialFuncyyFTwB : $@convention(thin) () -> () +// CHECK: {{%.*}} = apply [[FALLBACKFN]]() : $@convention(thin) () -> () +// CHECK: br [[RETURN_BB:bb[0-9]+]] +// +// CHECK: [[AVAIL_BB]]: +// CHECK: [[ORIGFN:%.*]] = function_ref @$s11back_deploy11trivialFuncyyF : $@convention(thin) () -> () +// CHECK: {{%.*}} = apply [[ORIGFN]]() : $@convention(thin) () -> () +// CHECK: br [[RETURN_BB]] +// +// CHECK: [[RETURN_BB]] +// CHECK: [[RESULT:%.*]] = tuple () +// CHECK: return [[RESULT]] : $() + +// -- Original definition of trivialFunc() +// CHECK-LABEL: sil [serialized] [available 10.51] [ossa] @$s11back_deploy11trivialFuncyyF : $@convention(thin) () -> () +@available(macOS 10.51, *) +@_backDeploy(macOS 10.50) +public func trivialFunc() {} + +// -- Fallback definition of isNumber(_:) +// CHECK-LABEL: sil non_abi [serialized] [available 10.51] [ossa] @$s11back_deploy8isNumberySbSiFTwB : $@convention(thin) (Int) -> Bool +// CHECK: bb0([[ARG_X:%.*]] : $Int): +// ... +// CHECK: return {{%.*}} : $Bool + +// -- Back deployment thunk for isNumber(_:) +// CHECK-LABEL: sil non_abi [serialized] [thunk] [available 10.51] [ossa] @$s11back_deploy8isNumberySbSiFTwb : $@convention(thin) (Int) -> Bool +// CHECK: bb0([[ARG_X:%.*]] : $Int): +// CHECK: [[MAJOR:%.*]] = integer_literal $Builtin.Word, 10 +// CHECK: [[MINOR:%.*]] = integer_literal $Builtin.Word, 51 +// CHECK: [[PATCH:%.*]] = integer_literal $Builtin.Word, 0 +// CHECK: [[OSVFN:%.*]] = function_ref @$ss26_stdlib_isOSVersionAtLeastyBi1_Bw_BwBwtF : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1 +// CHECK: [[AVAIL:%.*]] = apply [[OSVFN]]([[MAJOR]], [[MINOR]], [[PATCH]]) : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1 +// CHECK: cond_br [[AVAIL]], [[AVAIL_BB:bb[0-9]+]], [[UNAVAIL_BB:bb[0-9]+]] +// +// CHECK: [[UNAVAIL_BB]]: +// CHECK: [[FALLBACKFN:%.*]] = function_ref @$s11back_deploy8isNumberySbSiFTwB : $@convention(thin) (Int) -> Bool +// CHECK: [[RESULT:%.*]] = apply [[FALLBACKFN]]([[ARG_X]]) : $@convention(thin) (Int) -> Bool +// CHECK: br [[RETURN_BB:bb[0-9]+]]([[RESULT]] : $Bool) +// +// CHECK: [[AVAIL_BB]]: +// CHECK: [[ORIGFN:%.*]] = function_ref @$s11back_deploy8isNumberySbSiF : $@convention(thin) (Int) -> Bool +// CHECK: [[RESULT:%.*]] = apply [[ORIGFN]]([[ARG_X]]) : $@convention(thin) (Int) -> Bool +// CHECK: br [[RETURN_BB]]([[RESULT]] : $Bool) +// +// CHECK: [[RETURN_BB]]([[RETURN_BB_ARG:%.*]] : $Bool) +// CHECK: return [[RETURN_BB_ARG]] : $Bool + +// -- Original definition of isNumber(_:) +// CHECK-LABEL: sil [serialized] [available 10.51] [ossa] @$s11back_deploy8isNumberySbSiF : $@convention(thin) (Int) -> Bool +@available(macOS 10.51, *) +@_backDeploy(macOS 10.50) +public func isNumber(_ x: Int) -> Bool { + return true +} + +// CHECK-LABEL: sil hidden [available 10.51] [ossa] @$s11back_deploy6calleryyF : $@convention(thin) () -> () +@available(macOS 10.51, *) // FIXME(backDeploy): remove once typechecking allows +func caller() { + // -- Verify the thunk is called + // CHECK: {{%.*}} = function_ref @$s11back_deploy11trivialFuncyyFTwb : $@convention(thin) () -> () + trivialFunc() + // CHECK: {{%.*}} = function_ref @$s11back_deploy8isNumberySbSiFTwb : $@convention(thin) (Int) -> Bool + _ = isNumber(6) +} diff --git a/test/SILGen/back_deploy_attribute_generic_func.swift b/test/SILGen/back_deploy_attribute_generic_func.swift new file mode 100644 index 0000000000000..3f5e97f701a74 --- /dev/null +++ b/test/SILGen/back_deploy_attribute_generic_func.swift @@ -0,0 +1,53 @@ +// RUN: %target-swift-emit-sil -parse-as-library -module-name back_deploy %s -target %target-cpu-apple-macosx10.50 -verify +// RUN: %target-swift-emit-silgen -parse-as-library -module-name back_deploy %s | %FileCheck %s +// RUN: %target-swift-emit-silgen -parse-as-library -module-name back_deploy %s -target %target-cpu-apple-macosx10.50 | %FileCheck %s +// RUN: %target-swift-emit-silgen -parse-as-library -module-name back_deploy %s -target %target-cpu-apple-macosx10.60 | %FileCheck %s + +// REQUIRES: OS=macosx + +// -- Fallback definition of genericFunc() +// CHECK-LABEL: sil non_abi [serialized] [available 10.51] [ossa] @$s11back_deploy11genericFuncyxxlFTwB : $@convention(thin) (@in_guaranteed T) -> @out T +// CHECK: bb0([[OUT_ARG:%.*]] : $*T, [[IN_ARG:%.*]] : $*T): +// CHECK: copy_addr [[IN_ARG]] to [initialization] [[OUT_ARG]] : $*T +// CHECK: [[RESULT:%.*]] = tuple () +// CHECK: return [[RESULT]] : $() + +// -- Back deployment thunk for genericFunc() +// CHECK-LABEL: sil non_abi [serialized] [thunk] [available 10.51] [ossa] @$s11back_deploy11genericFuncyxxlFTwb : $@convention(thin) (@in_guaranteed T) -> @out T +// CHECK: bb0([[OUT_ARG:%.*]] : $*T, [[IN_ARG:%.*]] : $*T): +// CHECK: [[MAJOR:%.*]] = integer_literal $Builtin.Word, 10 +// CHECK: [[MINOR:%.*]] = integer_literal $Builtin.Word, 51 +// CHECK: [[PATCH:%.*]] = integer_literal $Builtin.Word, 0 +// CHECK: [[OSVFN:%.*]] = function_ref @$ss26_stdlib_isOSVersionAtLeastyBi1_Bw_BwBwtF : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1 +// CHECK: [[AVAIL:%.*]] = apply [[OSVFN]]([[MAJOR]], [[MINOR]], [[PATCH]]) : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1 +// CHECK: cond_br [[AVAIL]], [[AVAIL_BB:bb[0-9]+]], [[UNAVAIL_BB:bb[0-9]+]] +// +// CHECK: [[UNAVAIL_BB]]: +// CHECK: [[FALLBACKFN:%.*]] = function_ref @$s11back_deploy11genericFuncyxxlFTwB : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> @out τ_0_0 +// CHECK: {{%.*}} = apply [[FALLBACKFN]]([[OUT_ARG]], [[IN_ARG]]) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> @out τ_0_0 +// CHECK: br [[RETURN_BB:bb[0-9]+]] +// +// CHECK: [[AVAIL_BB]]: +// CHECK: [[ORIGFN:%.*]] = function_ref @$s11back_deploy11genericFuncyxxlF : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> @out τ_0_0 +// CHECK: {{%.*}} = apply [[ORIGFN]]([[OUT_ARG]], [[IN_ARG]]) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> @out τ_0_0 +// CHECK: br [[RETURN_BB]] +// +// CHECK: [[RETURN_BB]] +// CHECK: [[RESULT:%.*]] = tuple () +// CHECK: return [[RESULT]] : $() + +// -- Original definition of genericFunc() +// CHECK-LABEL: sil [serialized] [available 10.51] [ossa] @$s11back_deploy11genericFuncyxxlF : $@convention(thin) (@in_guaranteed T) -> @out T +@available(macOS 10.51, *) +@_backDeploy(macOS 10.50) +public func genericFunc(_ t: T) -> T { + return t +} + +// CHECK-LABEL: sil hidden [available 10.51] [ossa] @$s11back_deploy6calleryyF : $@convention(thin) () -> () +@available(macOS 10.51, *) // FIXME(backDeploy): remove once typechecking allows +func caller() { + // -- Verify the thunk is called + // CHECK: {{%.*}} = function_ref @$s11back_deploy11genericFuncyxxlFTwb : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> @out τ_0_0 + _ = genericFunc(Int32(1)) +} diff --git a/test/SILGen/back_deploy_attribute_struct_method.swift b/test/SILGen/back_deploy_attribute_struct_method.swift new file mode 100644 index 0000000000000..d30013a9c4b3c --- /dev/null +++ b/test/SILGen/back_deploy_attribute_struct_method.swift @@ -0,0 +1,54 @@ +// RUN: %target-swift-emit-sil -parse-as-library -module-name back_deploy %s -target %target-cpu-apple-macosx10.50 -verify +// RUN: %target-swift-emit-silgen -parse-as-library -module-name back_deploy %s | %FileCheck %s +// RUN: %target-swift-emit-silgen -parse-as-library -module-name back_deploy %s -target %target-cpu-apple-macosx10.50 | %FileCheck %s +// RUN: %target-swift-emit-silgen -parse-as-library -module-name back_deploy %s -target %target-cpu-apple-macosx10.60 | %FileCheck %s + +// REQUIRES: OS=macosx + +@available(macOS 10.50, *) +public struct TopLevelStruct { + // -- Fallback definition for TopLevelStruct.trivialMethod() + // CHECK-LABEL: sil non_abi [serialized] [available 10.51] [ossa] @$s11back_deploy14TopLevelStructV13trivialMethodyyFTwB : $@convention(method) (TopLevelStruct) -> () + // CHECK: bb0({{%.*}} : $TopLevelStruct): + // CHECK: [[RESULT:%.*]] = tuple () + // CHECK: return [[RESULT]] : $() + + // -- Back deployment thunk for TopLevelStruct.trivialMethod() + // CHECK-LABEL: sil non_abi [serialized] [thunk] [available 10.51] [ossa] @$s11back_deploy14TopLevelStructV13trivialMethodyyFTwb : $@convention(method) (TopLevelStruct) -> () + // CHECK: bb0([[BB0_ARG:%.*]] : $TopLevelStruct): + // CHECK: [[MAJOR:%.*]] = integer_literal $Builtin.Word, 10 + // CHECK: [[MINOR:%.*]] = integer_literal $Builtin.Word, 51 + // CHECK: [[PATCH:%.*]] = integer_literal $Builtin.Word, 0 + // CHECK: [[OSVFN:%.*]] = function_ref @$ss26_stdlib_isOSVersionAtLeastyBi1_Bw_BwBwtF : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1 + // CHECK: [[AVAIL:%.*]] = apply [[OSVFN]]([[MAJOR]], [[MINOR]], [[PATCH]]) : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1 + // CHECK: cond_br [[AVAIL]], [[AVAIL_BB:bb[0-9]+]], [[UNAVAIL_BB:bb[0-9]+]] + // + // CHECK: [[UNAVAIL_BB]]: + // CHECK: [[FALLBACKFN:%.*]] = function_ref @$s11back_deploy14TopLevelStructV13trivialMethodyyFTwB : $@convention(method) (TopLevelStruct) -> () + // CHECK: {{%.*}} = apply [[FALLBACKFN]]([[BB0_ARG]]) : $@convention(method) (TopLevelStruct) -> () + // CHECK: br [[RETURN_BB:bb[0-9]+]] + // + // CHECK: [[AVAIL_BB]]: + // CHECK: [[ORIGFN:%.*]] = function_ref @$s11back_deploy14TopLevelStructV13trivialMethodyyF : $@convention(method) (TopLevelStruct) -> () + // CHECK: {{%.*}} = apply [[ORIGFN]]([[BB0_ARG]]) : $@convention(method) (TopLevelStruct) -> () + // CHECK: br [[RETURN_BB]] + // + // CHECK: [[RETURN_BB]] + // CHECK: [[RESULT:%.*]] = tuple () + // CHECK: return [[RESULT]] : $() + + // -- Original definition of TopLevelStruct.trivialMethod() + // CHECK-LABEL: sil [serialized] [available 10.51] [ossa] @$s11back_deploy14TopLevelStructV13trivialMethodyyF : $@convention(method) (TopLevelStruct) -> () + @available(macOS 10.51, *) + @_backDeploy(macOS 10.50) + public func trivialMethod() {} +} + +// CHECK-LABEL: sil hidden [available 10.51] [ossa] @$s11back_deploy6calleryyAA14TopLevelStructVF : $@convention(thin) (TopLevelStruct) -> () +// CHECK: bb0([[STRUCT_ARG:%.*]] : $TopLevelStruct): +@available(macOS 10.51, *) // FIXME(backDeploy): remove once typechecking allows +func caller(_ s: TopLevelStruct) { + // -- Verify the thunk is called + // CHECK: {{%.*}} = function_ref @$s11back_deploy14TopLevelStructV13trivialMethodyyFTwb : $@convention(method) (TopLevelStruct) -> () + s.trivialMethod() +} diff --git a/test/SILGen/back_deploy_attribute_throwing_func.swift b/test/SILGen/back_deploy_attribute_throwing_func.swift new file mode 100644 index 0000000000000..6be38efeedf2a --- /dev/null +++ b/test/SILGen/back_deploy_attribute_throwing_func.swift @@ -0,0 +1,63 @@ +// RUN: %target-swift-emit-sil -parse-as-library -module-name back_deploy %s -target %target-cpu-apple-macosx10.50 -verify +// RUN: %target-swift-emit-silgen -parse-as-library -module-name back_deploy %s | %FileCheck %s +// RUN: %target-swift-emit-silgen -parse-as-library -module-name back_deploy %s -target %target-cpu-apple-macosx10.50 | %FileCheck %s +// RUN: %target-swift-emit-silgen -parse-as-library -module-name back_deploy %s -target %target-cpu-apple-macosx10.60 | %FileCheck %s + +// REQUIRES: OS=macosx + +// -- Fallback definition of throwingFunc() +// CHECK-LABEL: sil non_abi [serialized] [available 10.51] [ossa] @$s11back_deploy12throwingFuncyyKFTwB : $@convention(thin) () -> @error Error +// CHECK: bb0: +// CHECK: [[RESULT:%.*]] = tuple () +// CHECK: return [[RESULT]] : $() + +// -- Back deployment thunk for throwingFunc() +// CHECK-LABEL: sil non_abi [serialized] [thunk] [available 10.51] [ossa] @$s11back_deploy12throwingFuncyyKFTwb : $@convention(thin) () -> @error Error +// CHECK: bb0: +// CHECK: [[MAJOR:%.*]] = integer_literal $Builtin.Word, 10 +// CHECK: [[MINOR:%.*]] = integer_literal $Builtin.Word, 51 +// CHECK: [[PATCH:%.*]] = integer_literal $Builtin.Word, 0 +// CHECK: [[OSVFN:%.*]] = function_ref @$ss26_stdlib_isOSVersionAtLeastyBi1_Bw_BwBwtF : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1 +// CHECK: [[AVAIL:%.*]] = apply [[OSVFN]]([[MAJOR]], [[MINOR]], [[PATCH]]) : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1 +// CHECK: cond_br [[AVAIL]], [[AVAIL_BB:bb[0-9]+]], [[UNAVAIL_BB:bb[0-9]+]] +// +// CHECK: [[UNAVAIL_BB]]: +// CHECK: [[FALLBACKFN:%.*]] = function_ref @$s11back_deploy12throwingFuncyyKFTwB : $@convention(thin) () -> @error Error +// CHECK: try_apply [[FALLBACKFN]]() : $@convention(thin) () -> @error Error, normal [[UNAVAIL_NORMAL_BB:bb[0-9]+]], error [[UNAVAIL_ERROR_BB:bb[0-9]+]] +// +// CHECK: [[UNAVAIL_ERROR_BB]]([[ARG:%.*]] : @owned $Error): +// CHECK: br [[RETHROW_BB:bb[0-9]+]]([[ARG]] : $Error) +// +// CHECK: [[UNAVAIL_NORMAL_BB]]([[ARG:%.*]] : $()): +// CHECK: br [[RETURN_BB:bb[0-9]+]] +// +// CHECK: [[AVAIL_BB]]: +// CHECK: [[ORIGFN:%.*]] = function_ref @$s11back_deploy12throwingFuncyyKF : $@convention(thin) () -> @error Error +// CHECK: try_apply [[ORIGFN]]() : $@convention(thin) () -> @error Error, normal [[AVAIL_NORMAL_BB:bb[0-9]+]], error [[AVAIL_ERROR_BB:bb[0-9]+]] +// +// CHECK: [[AVAIL_ERROR_BB]]([[ARG:%.*]] : @owned $Error): +// CHECK: br [[RETHROW_BB]]([[ARG]] : $Error) +// +// CHECK: [[AVAIL_NORMAL_BB]]([[ARG:%.*]] : $()): +// CHECK: br [[RETURN_BB]] +// +// CHECK: [[RETURN_BB]] +// CHECK: [[RESULT:%.*]] = tuple () +// CHECK: return [[RESULT]] : $() +// +// CHECK: [[RETHROW_BB]]([[RETHROW_BB_ARG:%.*]] : @owned $Error) +// CHECK: throw [[RETHROW_BB_ARG]] : $Error + +// -- Original definition of throwingFunc() +// CHECK-LABEL: sil [serialized] [available 10.51] [ossa] @$s11back_deploy12throwingFuncyyKF : $@convention(thin) () -> @error Error +@available(macOS 10.51, *) +@_backDeploy(macOS 10.50) +public func throwingFunc() throws {} + +// CHECK-LABEL: sil hidden [available 10.51] [ossa] @$s11back_deploy6calleryyKF : $@convention(thin) () -> @error Error +@available(macOS 10.51, *) // FIXME(backDeploy): remove once typechecking allows +func caller() throws { + // -- Verify the thunk is called + // CHECK: {{%.*}} = function_ref @$s11back_deploy12throwingFuncyyKFTwb : $@convention(thin) () -> @error Error + try throwingFunc() +}