diff --git a/docs/ABI/Mangling.rst b/docs/ABI/Mangling.rst index 3b08b3266fdd6..f50f7d764acef 100644 --- a/docs/ABI/Mangling.rst +++ b/docs/ABI/Mangling.rst @@ -143,6 +143,7 @@ Globals global ::= opaque-type 'Ho' // opaque type descriptor runtime record #endif global ::= protocol-conformance 'Hc' // protocol conformance runtime record + global ::= global 'HF' // accessible function runtime record global ::= nominal-type 'Mo' // class metadata immediate member base offset @@ -218,6 +219,7 @@ types where the metadata itself has unknown layout.) global ::= global 'TD' // dynamic dispatch thunk global ::= global 'Td' // direct method reference thunk global ::= global 'TE' // distributed actor thunk + global ::= global 'TF' // distributed method accessor 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 diff --git a/include/swift/ABI/Metadata.h b/include/swift/ABI/Metadata.h index b32dc943ff5c8..02affd003b9d4 100644 --- a/include/swift/ABI/Metadata.h +++ b/include/swift/ABI/Metadata.h @@ -5036,6 +5036,30 @@ class DynamicReplacementScope uint32_t getFlags() { return flags; } }; +/// An "accessible" function that can be looked up based on a string key, +/// and then called through a fully-abstracted entry point whose arguments +/// can be constructed in code. +template +struct TargetAccessibleFunctionRecord final { +public: + /// The name of the function, which is a unique string assigned to the + /// function so it can be looked up later. + RelativeDirectPointer Name; + + /// The Swift function type, encoded as a mangled name. + RelativeDirectPointer FunctionType; + + /// The fully-abstracted function to call. + /// + /// Could be a sync or async function pointer depending on flags. + RelativeDirectPointer Function; + + /// Flags providing more information about the function. + AccessibleFunctionFlags Flags; +}; + +using AccessibleFunctionRecord = TargetAccessibleFunctionRecord; + } // end namespace swift #pragma clang diagnostic pop diff --git a/include/swift/ABI/MetadataValues.h b/include/swift/ABI/MetadataValues.h index 1792101cf1ec4..b79884df29918 100644 --- a/include/swift/ABI/MetadataValues.h +++ b/include/swift/ABI/MetadataValues.h @@ -2312,6 +2312,21 @@ enum class ContinuationStatus : size_t { Resumed = 2 }; +/// Flags that go in a TargetAccessibleFunction structure. +class AccessibleFunctionFlags : public FlagSet { +public: + enum { + /// Whether this is a "distributed" actor function. + Distributed = 0, + }; + + explicit AccessibleFunctionFlags(uint32_t bits) : FlagSet(bits) {} + constexpr AccessibleFunctionFlags() {} + + /// Whether the this is a "distributed" actor function. + FLAGSET_DEFINE_FLAG_ACCESSORS(Distributed, isDistributed, setDistributed) +}; + } // end namespace swift #endif // SWIFT_ABI_METADATAVALUES_H diff --git a/include/swift/AST/ASTMangler.h b/include/swift/AST/ASTMangler.h index 134085c1f7789..dc752268bad18 100644 --- a/include/swift/AST/ASTMangler.h +++ b/include/swift/AST/ASTMangler.h @@ -104,6 +104,8 @@ class ASTMangler : public Mangler { SwiftAsObjCThunk, ObjCAsSwiftThunk, DistributedThunk, + DistributedMethodAccessor, + AccessibleFunctionRecord }; ASTMangler(bool DWARFMangling = false) diff --git a/include/swift/Demangling/DemangleNodes.def b/include/swift/Demangling/DemangleNodes.def index d357c5c42c535..f72304a6a27f5 100644 --- a/include/swift/Demangling/DemangleNodes.def +++ b/include/swift/Demangling/DemangleNodes.def @@ -71,6 +71,7 @@ CONTEXT_NODE(Destructor) CONTEXT_NODE(DidSet) NODE(Directness) NODE(DistributedThunk) +NODE(DistributedMethodAccessor) NODE(DynamicAttribute) NODE(DirectMethodReferenceAttribute) NODE(DynamicSelf) @@ -325,5 +326,8 @@ NODE(IndexSubset) NODE(AsyncAwaitResumePartialFunction) NODE(AsyncSuspendResumePartialFunction) +// Added in Swift 5.6 +NODE(AccessibleFunctionRecord) + #undef CONTEXT_NODE #undef NODE diff --git a/include/swift/IRGen/Linking.h b/include/swift/IRGen/Linking.h index 1191f1024a706..fd23355ad72e7 100644 --- a/include/swift/IRGen/Linking.h +++ b/include/swift/IRGen/Linking.h @@ -481,6 +481,16 @@ class LinkEntity { /// name is known. /// The pointer is a const char* of the name. KnownAsyncFunctionPointer, + + /// The pointer is SILFunction* + DistributedMethodAccessor, + /// An async function pointer for a distributed method accessor. + /// The pointer is a SILFunction*. + DistributedMethodAccessorAsyncPointer, + + /// Accessible function record, which describes a function that can be + /// looked up by name by the runtime. + AccessibleFunctionRecord, }; friend struct llvm::DenseMapInfo; @@ -1236,6 +1246,13 @@ class LinkEntity { Kind, unsigned(LinkEntity::Kind::PartialApplyForwarderAsyncFunctionPointer)); break; + case LinkEntity::Kind::DistributedMethodAccessor: { + entity.Data = LINKENTITY_SET_FIELD( + Kind, + unsigned(LinkEntity::Kind::DistributedMethodAccessorAsyncPointer)); + break; + } + default: llvm_unreachable("Link entity kind cannot have an async function pointer"); } @@ -1263,6 +1280,24 @@ class LinkEntity { return entity; } + static LinkEntity forDistributedMethodAccessor(SILFunction *method) { + LinkEntity entity; + entity.Pointer = method; + entity.SecondaryPointer = nullptr; + entity.Data = + LINKENTITY_SET_FIELD(Kind, unsigned(Kind::DistributedMethodAccessor)); + return entity; + } + + static LinkEntity forAccessibleFunctionRecord(SILFunction *func) { + LinkEntity entity; + entity.Pointer = func; + entity.SecondaryPointer = nullptr; + entity.Data = + LINKENTITY_SET_FIELD(Kind, unsigned(Kind::AccessibleFunctionRecord)); + return entity; + } + LinkEntity getUnderlyingEntityForAsyncFunctionPointer() const { LinkEntity entity; entity.Pointer = Pointer; @@ -1294,6 +1329,11 @@ class LinkEntity { Kind, unsigned(LinkEntity::Kind::PartialApplyForwarder)); break; + case LinkEntity::Kind::DistributedMethodAccessorAsyncPointer: + entity.Data = LINKENTITY_SET_FIELD( + Kind, unsigned(LinkEntity::Kind::DistributedMethodAccessor)); + break; + default: llvm_unreachable("Link entity is not an async function pointer"); } @@ -1341,7 +1381,9 @@ class LinkEntity { return getKind() == Kind::AsyncFunctionPointer || getKind() == Kind::DynamicallyReplaceableFunctionVariable || getKind() == Kind::DynamicallyReplaceableFunctionKey || - getKind() == Kind::SILFunction; + getKind() == Kind::SILFunction || + getKind() == Kind::DistributedMethodAccessor || + getKind() == Kind::AccessibleFunctionRecord; } SILFunction *getSILFunction() const { diff --git a/include/swift/Runtime/AccessibleFunction.h b/include/swift/Runtime/AccessibleFunction.h new file mode 100644 index 0000000000000..a69d6b262b838 --- /dev/null +++ b/include/swift/Runtime/AccessibleFunction.h @@ -0,0 +1,34 @@ +//===---- AcceesibleFunction.h - Runtime accessible functions ---*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// +// +// The runtime interface for functions accessible by name. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_RUNTIME_ACCESSIBLE_FUNCTION_H +#define SWIFT_RUNTIME_ACCESSIBLE_FUNCTION_H + +#include "swift/ABI/Metadata.h" + +#include + +namespace swift { +namespace runtime { + +SWIFT_RUNTIME_STDLIB_SPI const AccessibleFunctionRecord * +swift_findAccessibleFunction(const char *targetNameStart, + size_t targetNameLength); + +} // end namespace runtime +} // end namespace swift + +#endif diff --git a/include/swift/SIL/SILFunction.h b/include/swift/SIL/SILFunction.h index c2888227c87d4..b735222eeaccc 100644 --- a/include/swift/SIL/SILFunction.h +++ b/include/swift/SIL/SILFunction.h @@ -59,6 +59,10 @@ enum IsExactSelfClass_t { IsNotExactSelfClass, IsExactSelfClass, }; +enum IsDistributed_t { + IsNotDistributed, + IsDistributed, +}; enum class PerformanceConstraints : uint8_t { None = 0, @@ -294,6 +298,9 @@ class SILFunction /// invoked with a `self` argument of the exact base class type. unsigned ExactSelfClass : 1; + /// Check whether this is a distributed method. + unsigned IsDistributed : 1; + /// True if this function is inlined at least once. This means that the /// debug info keeps a pointer to this function. unsigned Inlined : 1; @@ -373,7 +380,8 @@ class SILFunction SubclassScope classSubclassScope, Inline_t inlineStrategy, EffectsKind E, const SILDebugScope *debugScope, IsDynamicallyReplaceable_t isDynamic, - IsExactSelfClass_t isExactSelfClass); + IsExactSelfClass_t isExactSelfClass, + IsDistributed_t isDistributed); static SILFunction * create(SILModule &M, SILLinkage linkage, StringRef name, @@ -381,6 +389,7 @@ class SILFunction Optional loc, IsBare_t isBareSILFunction, IsTransparent_t isTrans, IsSerialized_t isSerialized, ProfileCounter entryCount, IsDynamicallyReplaceable_t isDynamic, + IsDistributed_t isDistributed, IsExactSelfClass_t isExactSelfClass, IsThunk_t isThunk = IsNotThunk, SubclassScope classSubclassScope = SubclassScope::NotApplicable, @@ -399,7 +408,8 @@ class SILFunction Inline_t inlineStrategy, EffectsKind E, const SILDebugScope *DebugScope, IsDynamicallyReplaceable_t isDynamic, - IsExactSelfClass_t isExactSelfClass); + IsExactSelfClass_t isExactSelfClass, + IsDistributed_t isDistributed); /// Set has ownership to the given value. True means that the function has /// ownership, false means it does not. @@ -747,6 +757,14 @@ class SILFunction ExactSelfClass = t; } + IsDistributed_t isDistributed() const { + return IsDistributed_t(IsDistributed); + } + void + setIsDistributed(IsDistributed_t value = IsDistributed_t::IsDistributed) { + IsDistributed = value; + } + /// Get the DeclContext of this function. DeclContext *getDeclContext() const { return DeclCtxt; } diff --git a/include/swift/SIL/SILFunctionBuilder.h b/include/swift/SIL/SILFunctionBuilder.h index b992fce2232c2..73d251ccd407d 100644 --- a/include/swift/SIL/SILFunctionBuilder.h +++ b/include/swift/SIL/SILFunctionBuilder.h @@ -67,7 +67,8 @@ class SILFunctionBuilder { IsSerialized_t isSerialized, ProfileCounter entryCount, IsThunk_t isThunk, - IsDynamicallyReplaceable_t isDynamic); + IsDynamicallyReplaceable_t isDynamic, + IsDistributed_t isDistributed); /// Return the declaration of a function, or create it if it doesn't exist. SILFunction *getOrCreateFunction( @@ -75,6 +76,7 @@ class SILFunctionBuilder { CanSILFunctionType type, IsBare_t isBareSILFunction, IsTransparent_t isTransparent, IsSerialized_t isSerialized, IsDynamicallyReplaceable_t isDynamic, + IsDistributed_t isDistributed, ProfileCounter entryCount = ProfileCounter(), IsThunk_t isThunk = IsNotThunk, SubclassScope subclassScope = SubclassScope::NotApplicable); @@ -102,6 +104,7 @@ class SILFunctionBuilder { Optional loc, IsBare_t isBareSILFunction, IsTransparent_t isTrans, IsSerialized_t isSerialized, IsDynamicallyReplaceable_t isDynamic, + IsDistributed_t isDistributed, ProfileCounter entryCount = ProfileCounter(), IsThunk_t isThunk = IsNotThunk, SubclassScope subclassScope = SubclassScope::NotApplicable, diff --git a/include/swift/SIL/TypeSubstCloner.h b/include/swift/SIL/TypeSubstCloner.h index 90c31bec1621f..b06104a1a283c 100644 --- a/include/swift/SIL/TypeSubstCloner.h +++ b/include/swift/SIL/TypeSubstCloner.h @@ -446,7 +446,7 @@ class TypeSubstCloner : public SILClonerWithScopes { ParentFunction->getLocation(), MangledName, SILLinkage::Shared, ParentFunction->getLoweredFunctionType(), ParentFunction->isBare(), ParentFunction->isTransparent(), ParentFunction->isSerialized(), - IsNotDynamic, 0, ParentFunction->isThunk(), + IsNotDynamic, IsNotDistributed, 0, ParentFunction->isThunk(), ParentFunction->getClassSubclassScope()); // Increment the ref count for the inlined function, so it doesn't // get deleted before we can emit abstract debug info for it. diff --git a/lib/AST/ASTMangler.cpp b/lib/AST/ASTMangler.cpp index 3bdbdc7af9e19..1d61aa7fc59ed 100644 --- a/lib/AST/ASTMangler.cpp +++ b/lib/AST/ASTMangler.cpp @@ -849,6 +849,8 @@ void ASTMangler::appendSymbolKind(SymbolKind SKind) { case SymbolKind::SwiftAsObjCThunk: return appendOperator("To"); case SymbolKind::ObjCAsSwiftThunk: return appendOperator("TO"); case SymbolKind::DistributedThunk: return appendOperator("TE"); + case SymbolKind::DistributedMethodAccessor: return appendOperator("TF"); + case SymbolKind::AccessibleFunctionRecord: return appendOperator("HF"); } } diff --git a/lib/Demangling/Demangler.cpp b/lib/Demangling/Demangler.cpp index 90953faf46d36..1f07fcca398ae 100644 --- a/lib/Demangling/Demangler.cpp +++ b/lib/Demangling/Demangler.cpp @@ -133,12 +133,14 @@ bool swift::Demangle::isFunctionAttr(Node::Kind kind) { case Node::Kind::OutlinedBridgedMethod: case Node::Kind::MergedFunction: case Node::Kind::DistributedThunk: + case Node::Kind::DistributedMethodAccessor: case Node::Kind::DynamicallyReplaceableFunctionImpl: case Node::Kind::DynamicallyReplaceableFunctionKey: case Node::Kind::DynamicallyReplaceableFunctionVar: case Node::Kind::AsyncFunctionPointer: case Node::Kind::AsyncAwaitResumePartialFunction: case Node::Kind::AsyncSuspendResumePartialFunction: + case Node::Kind::AccessibleFunctionRecord: return true; default: return false; @@ -799,7 +801,7 @@ NodePointer Demangler::demangleOperator() { return createWithChild( Node::Kind::ProtocolConformanceRefInProtocolModule, popProtocol()); - // Runtime records (type/protocol/conformance) + // Runtime records (type/protocol/conformance/function) case 'c': return createWithChild(Node::Kind::ProtocolConformanceDescriptorRecord, popProtocolConformance()); @@ -809,6 +811,8 @@ NodePointer Demangler::demangleOperator() { return createWithChild(Node::Kind::OpaqueTypeDescriptorRecord, popNode()); case 'r': return createWithChild(Node::Kind::ProtocolDescriptorRecord, popProtocol()); + case 'F': + return createNode(Node::Kind::AccessibleFunctionRecord); default: pushBack(); @@ -2363,6 +2367,7 @@ NodePointer Demangler::demangleThunkOrSpecialization() { case 'D': return createNode(Node::Kind::DynamicAttribute); case 'd': return createNode(Node::Kind::DirectMethodReferenceAttribute); case 'E': return createNode(Node::Kind::DistributedThunk); + case 'F': return createNode(Node::Kind::DistributedMethodAccessor); case 'a': return createNode(Node::Kind::PartialApplyObjCForwarder); case 'A': return createNode(Node::Kind::PartialApplyForwarder); case 'm': return createNode(Node::Kind::MergedFunction); diff --git a/lib/Demangling/NodePrinter.cpp b/lib/Demangling/NodePrinter.cpp index 216fd2d5d456f..f9d3ab1456ea6 100644 --- a/lib/Demangling/NodePrinter.cpp +++ b/lib/Demangling/NodePrinter.cpp @@ -559,6 +559,7 @@ class NodePrinter { case Node::Kind::ProtocolConformanceRefInProtocolModule: case Node::Kind::ProtocolConformanceRefInOtherModule: case Node::Kind::DistributedThunk: + case Node::Kind::DistributedMethodAccessor: case Node::Kind::DynamicallyReplaceableFunctionKey: case Node::Kind::DynamicallyReplaceableFunctionImpl: case Node::Kind::DynamicallyReplaceableFunctionVar: @@ -585,6 +586,7 @@ class NodePrinter { case Node::Kind::IndexSubset: case Node::Kind::AsyncAwaitResumePartialFunction: case Node::Kind::AsyncSuspendResumePartialFunction: + case Node::Kind::AccessibleFunctionRecord: return false; } printer_unreachable("bad node kind"); @@ -1997,6 +1999,16 @@ NodePointer NodePrinter::print(NodePointer Node, unsigned depth, Printer << "distributed thunk for "; } return nullptr; + case Node::Kind::DistributedMethodAccessor: + if (!Options.ShortenThunk) { + Printer << "distributed method accessor for "; + } + return nullptr; + case Node::Kind::AccessibleFunctionRecord: + if (!Options.ShortenThunk) { + Printer << "accessible function runtime record for "; + } + return nullptr; case Node::Kind::DynamicallyReplaceableFunctionKey: if (!Options.ShortenThunk) { Printer << "dynamically replaceable key for "; diff --git a/lib/Demangling/OldRemangler.cpp b/lib/Demangling/OldRemangler.cpp index 7240aa4e198b1..028b514c55090 100644 --- a/lib/Demangling/OldRemangler.cpp +++ b/lib/Demangling/OldRemangler.cpp @@ -735,6 +735,12 @@ Remangler::mangleDistributedThunk(Node *node, unsigned depth) { return ManglingError::Success; } +ManglingError +Remangler::mangleDistributedMethodAccessor(Node *node, unsigned depth) { + Buffer << "TF"; + return ManglingError::Success; +} + ManglingError Remangler::mangleDynamicallyReplaceableFunctionImpl(Node *node, unsigned depth) { @@ -2778,3 +2784,9 @@ Demangle::mangleNodeAsObjcCString(NodePointer node, return remangler.getBufferStr().data(); } + +ManglingError Remangler::mangleAccessibleFunctionRecord(Node *node, + unsigned depth) { + Buffer << "HF"; + return ManglingError::Success; +} diff --git a/lib/Demangling/Remangler.cpp b/lib/Demangling/Remangler.cpp index 1dc2ee9a6186d..6d0534d7b6e67 100644 --- a/lib/Demangling/Remangler.cpp +++ b/lib/Demangling/Remangler.cpp @@ -1611,12 +1611,14 @@ ManglingError Remangler::mangleGlobal(Node *node, unsigned depth) { case Node::Kind::DirectMethodReferenceAttribute: case Node::Kind::MergedFunction: case Node::Kind::DistributedThunk: + case Node::Kind::DistributedMethodAccessor: case Node::Kind::DynamicallyReplaceableFunctionKey: case Node::Kind::DynamicallyReplaceableFunctionImpl: case Node::Kind::DynamicallyReplaceableFunctionVar: case Node::Kind::AsyncFunctionPointer: case Node::Kind::AsyncAwaitResumePartialFunction: case Node::Kind::AsyncSuspendResumePartialFunction: + case Node::Kind::AccessibleFunctionRecord: mangleInReverseOrder = true; break; default: @@ -2248,6 +2250,12 @@ Remangler::mangleDistributedThunk(Node *node, unsigned depth) { return ManglingError::Success; } +ManglingError +Remangler::mangleDistributedMethodAccessor(Node *node, unsigned depth) { + Buffer << "TF"; + return ManglingError::Success; +} + ManglingError Remangler::mangleDynamicallyReplaceableFunctionImpl(Node *node, unsigned depth) { @@ -3374,6 +3382,12 @@ ManglingError Remangler::mangleGlobalVariableOnceDeclList(Node *node, return ManglingError::Success; } +ManglingError Remangler::mangleAccessibleFunctionRecord(Node *node, + unsigned depth) { + Buffer << "HF"; + return ManglingError::Success; +} + } // anonymous namespace /// The top-level interface to the remangler. diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index dcbfa17a1134f..6e7054527af24 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -1272,6 +1272,11 @@ void IRGenerator::emitTypeMetadataRecords() { } } +void IRGenerator::emitAccessibleFunctions() { + for (auto &m : *this) + m.second->emitAccessibleFunctions(); +} + static void deleteAndReenqueueForEmissionValuesDependentOnCanonicalPrespecializedMetadataRecords( IRGenModule &IGM, CanType typeWithCanonicalMetadataPrespecialization, @@ -3889,6 +3894,10 @@ void IRGenModule::addProtocolConformance(ConformanceDescription &&record) { ProtocolConformances.push_back(std::move(record)); } +void IRGenModule::addAccessibleFunction(SILFunction *func) { + AccessibleFunctions.push_back(func); +} + /// Emit the protocol conformance list and return it (if asContiguousArray is /// true, otherwise the records are emitted as individual globals and /// nullptr is returned). @@ -4090,6 +4099,85 @@ llvm::Constant *IRGenModule::emitTypeMetadataRecords(bool asContiguousArray) { return nullptr; } +void IRGenModule::emitAccessibleFunctions() { + if (AccessibleFunctions.empty()) + return; + + StringRef sectionName; + switch (TargetInfo.OutputObjectFormat) { + case llvm::Triple::GOFF: + case llvm::Triple::UnknownObjectFormat: + llvm_unreachable("Don't know how to emit accessible functions for " + "the selected object format."); + case llvm::Triple::MachO: + sectionName = "__TEXT, __swift5_acfuncs, regular"; + break; + case llvm::Triple::ELF: + case llvm::Triple::Wasm: + sectionName = "swift5_accessible_functions"; + break; + case llvm::Triple::XCOFF: + case llvm::Triple::COFF: + sectionName = ".sw5acfn$B"; + break; + } + + for (auto *func : AccessibleFunctions) { + auto mangledRecordName = + LinkEntity::forAccessibleFunctionRecord(func).mangleAsString(); + + auto var = new llvm::GlobalVariable( + Module, AccessibleFunctionRecordTy, /*isConstant*/ true, + llvm::GlobalValue::PrivateLinkage, /*initializer*/ nullptr, + mangledRecordName); + + std::string mangledFunctionName = + LinkEntity::forSILFunction(func).mangleAsString(); + llvm::Constant *name = getAddrOfGlobalString( + mangledFunctionName, /*willBeRelativelyAddressed*/ true); + llvm::Constant *relativeName = emitDirectRelativeReference(name, var, {}); + + GenericSignature signature; + if (auto *env = func->getGenericEnvironment()) { + signature = env->getGenericSignature(); + } + + llvm::Constant *type = getTypeRef(func->getLoweredFunctionType(), signature, + MangledTypeRefRole::Metadata) + .first; + llvm::Constant *relativeType = emitDirectRelativeReference(type, var, {1}); + + llvm::Constant *funcAddr = nullptr; + if (func->isDistributed()) { + funcAddr = getAddrOfAsyncFunctionPointer( + LinkEntity::forDistributedMethodAccessor(func)); + } else if (func->isAsync()) { + funcAddr = getAddrOfAsyncFunctionPointer(func); + } else { + funcAddr = getAddrOfSILFunction(func, NotForDefinition); + } + + llvm::Constant *relativeFuncAddr = + emitDirectRelativeReference(funcAddr, var, {2}); + + AccessibleFunctionFlags flagsVal; + flagsVal.setDistributed(func->isDistributed()); + + llvm::Constant *flags = + llvm::ConstantInt::get(Int32Ty, flagsVal.getOpaqueValue()); + + llvm::Constant *recordFields[] = {relativeName, relativeType, + relativeFuncAddr, flags}; + auto record = + llvm::ConstantStruct::get(AccessibleFunctionRecordTy, recordFields); + var->setInitializer(record); + var->setSection(sectionName); + var->setAlignment(llvm::MaybeAlign(4)); + disableAddressSanitizer(*this, var); + addUsedGlobal(var); + } +} + /// Fetch a global reference to a reference to the given Objective-C class. /// The result is of type ObjCClassPtrTy->getPointerTo(). Address IRGenModule::getAddrOfObjCClassRef(ClassDecl *theClass) { diff --git a/lib/IRGen/GenDistributed.cpp b/lib/IRGen/GenDistributed.cpp index 135d6966864c4..3a1b6e821c03f 100644 --- a/lib/IRGen/GenDistributed.cpp +++ b/lib/IRGen/GenDistributed.cpp @@ -17,8 +17,13 @@ #include "GenDistributed.h" #include "BitPatternBuilder.h" +#include "CallEmission.h" +#include "Callee.h" #include "ClassTypeInfo.h" #include "ExtraInhabitants.h" +#include "GenCall.h" +#include "GenDecl.h" +#include "GenMeta.h" #include "GenProto.h" #include "GenType.h" #include "IRGenDebugInfo.h" @@ -26,8 +31,11 @@ #include "IRGenModule.h" #include "LoadableTypeInfo.h" #include "ScalarPairTypeInfo.h" -#include "swift/AST/ProtocolConformanceRef.h" #include "swift/ABI/MetadataValues.h" +#include "swift/AST/ExtInfo.h" +#include "swift/AST/ProtocolConformanceRef.h" +#include "swift/IRGen/Linking.h" +#include "swift/SIL/SILFunction.h" using namespace swift; using namespace irgen; @@ -53,3 +61,347 @@ llvm::Value *irgen::emitDistributedActorInitializeRemote( return result; } + +namespace { + +struct AllocationInfo { + SILType Type; + const TypeInfo &TI; + StackAddress Addr; +}; + +class DistributedAccessor { + IRGenModule &IGM; + IRGenFunction &IGF; + + /// Underlying distributed method for this accessor. + SILFunction *Method; + + /// The interface type of this accessor function. + CanSILFunctionType AccessorType; + /// The asynchronous context associated with this accessor. + AsyncContextLayout AsyncLayout; + + /// The list of all arguments that were allocated on the stack. + SmallVector AllocatedArguments; + +public: + DistributedAccessor(IRGenFunction &IGF, SILFunction *method, + CanSILFunctionType accessorTy); + + void emit(); + +private: + void computeArguments(llvm::Value *argumentBuffer, Explosion &arguments); + + FunctionPointer getPointerToMethod() const; + + Callee getCalleeForDistributedMethod(llvm::Value *self) const; +}; + +} // end namespace + +/// Compute a type of a distributed method accessor function based +/// on the provided distributed method. +static CanSILFunctionType getAccessorType(IRGenModule &IGM, + SILFunction *DistMethod) { + auto &Context = IGM.Context; + + auto getRawPointerParmeter = [&]() { + auto ptrType = Context.getUnsafeRawPointerType(); + return SILParameterInfo(ptrType->getCanonicalType(), + ParameterConvention::Direct_Guaranteed); + }; + + // `self` of the distributed actor is going to be passed as an argument + // to this accessor function. + auto extInfo = SILExtInfoBuilder() + .withRepresentation(SILFunctionTypeRepresentation::Thick) + .withAsync() + .build(); + + auto methodTy = DistMethod->getLoweredFunctionType(); + + assert(methodTy->isAsync()); + assert(methodTy->hasErrorResult()); + + // Accessor gets argument value buffer and a reference to `self` of + // the actor and produces a call to the distributed thunk forwarding + // its result(s) out. + return SILFunctionType::get( + /*genericSignature=*/nullptr, extInfo, SILCoroutineKind::None, + ParameterConvention::Direct_Guaranteed, + {/*argumentBuffer=*/getRawPointerParmeter(), + /*resultBuffer=*/getRawPointerParmeter()}, + /*Yields=*/{}, + /*Results=*/{}, + /*ErrorResult=*/methodTy->getErrorResult(), + /*patternSubs=*/SubstitutionMap(), + /*invocationSubs=*/SubstitutionMap(), Context); +} + +llvm::Function * +IRGenModule::getAddrOfDistributedMethodAccessor(SILFunction *F, + ForDefinition_t forDefinition) { + auto entity = LinkEntity::forDistributedMethodAccessor(F); + + llvm::Function *&entry = GlobalFuncs[entity]; + if (entry) { + if (forDefinition) + updateLinkageForDefinition(*this, entry, entity); + return entry; + } + + Signature signature = getSignature(getAccessorType(*this, F)); + LinkInfo link = LinkInfo::get(*this, entity, forDefinition); + + return createFunction(*this, link, signature); +} + +void IRGenModule::emitDistributedMethodAccessor(SILFunction *method) { + assert(method->isDistributed()); + + auto *f = getAddrOfDistributedMethodAccessor(method, ForDefinition); + if (!f->isDeclaration()) + return; + + IRGenFunction IGF(*this, f); + DistributedAccessor(IGF, method, getAccessorType(*this, method)).emit(); +} + +DistributedAccessor::DistributedAccessor(IRGenFunction &IGF, + SILFunction *method, + CanSILFunctionType accessorTy) + : IGM(IGF.IGM), IGF(IGF), Method(method), AccessorType(accessorTy), + AsyncLayout(getAsyncContextLayout( + IGM, AccessorType, AccessorType, SubstitutionMap(), + /*suppress generics*/ true, + FunctionPointer::Kind( + FunctionPointer::BasicKind::AsyncFunctionPointer))) {} + +void DistributedAccessor::computeArguments(llvm::Value *argumentBuffer, + Explosion &arguments) { + auto fnType = Method->getLoweredFunctionType(); + + // Cover all of the arguments except to `self` of the actor. + auto parameters = fnType->getParameters().drop_back(); + + // If there are no parameters to extract, we are done. + if (parameters.empty()) + return; + + auto offset = + IGF.createAlloca(IGM.Int8PtrTy, IGM.getPointerAlignment(), "offset"); + IGF.Builder.CreateLifetimeStart(offset, IGM.getPointerSize()); + + // Initialize "offset" with the address of the base of the argument buffer. + IGF.Builder.CreateStore(argumentBuffer, offset); + + for (const auto ¶m : parameters) { + auto paramTy = param.getSILStorageInterfaceType(); + const TypeInfo &typeInfo = IGF.getTypeInfo(paramTy); + + // 1. Load current offset. + llvm::Value *currentOffset = IGF.Builder.CreateLoad(offset, "elt_offset"); + + // 2. Cast the pointer to the type of the element. + Address eltPtr = IGF.Builder.CreateBitCast( + Address(currentOffset, IGM.getPointerAlignment()), + IGM.getStoragePointerType(paramTy)); + + // 3. Adjust typed pointer to the alignement of the type. + auto alignedOffset = typeInfo.roundUpToTypeAlignment(IGF, eltPtr, paramTy); + + if (paramTy.isObject()) { + auto &nativeSchema = typeInfo.nativeParameterValueSchema(IGM); + // If schema is empty, skip to the next argument. + if (nativeSchema.empty()) + continue; + } + + // 4. Create an exploded version of the type to pass as an + // argument to distributed method. + + switch (param.getConvention()) { + case ParameterConvention::Indirect_In: + case ParameterConvention::Indirect_In_Constant: { + // The +1 argument is passed indirectly, so we need to copy it into + // a temporary. + + auto stackAddr = typeInfo.allocateStack(IGF, paramTy, "arg.temp"); + auto argPtr = stackAddr.getAddress().getAddress(); + + typeInfo.initializeWithCopy(IGF, stackAddr.getAddress(), alignedOffset, + paramTy, /*isOutlined=*/false); + arguments.add(argPtr); + + // Remember to deallocate later. + AllocatedArguments.push_back({paramTy, typeInfo, stackAddr}); + break; + } + + case ParameterConvention::Indirect_In_Guaranteed: { + // The argument is +0, so we can use the address of the param in + // the context directly. + arguments.add(alignedOffset.getAddress()); + break; + } + + case ParameterConvention::Indirect_Inout: + case ParameterConvention::Indirect_InoutAliasable: + llvm_unreachable("indirect parameters are not supported"); + + case ParameterConvention::Direct_Guaranteed: + case ParameterConvention::Direct_Unowned: { + cast(typeInfo).loadAsTake(IGF, alignedOffset, + arguments); + break; + } + + case ParameterConvention::Direct_Owned: + // Copy the value out at +1. + cast(typeInfo).loadAsCopy(IGF, alignedOffset, + arguments); + } + + // 6. Move the offset to the beginning of the next element, unless + // this is the last element. + if (param != parameters.back()) { + llvm::Value *typeSize = typeInfo.getSize(IGF, paramTy); + + llvm::Value *addr = alignedOffset.getAddress(); + addr = IGF.Builder.CreatePtrToInt(addr, IGM.IntPtrTy); + llvm::Value *nextOffset = IGF.Builder.CreateIntToPtr( + IGF.Builder.CreateAdd(addr, typeSize), IGM.Int8PtrTy); + + IGF.Builder.CreateStore(nextOffset, offset); + } + } + + IGF.Builder.CreateLifetimeEnd(offset, IGM.getPointerSize()); +} + +void DistributedAccessor::emit() { + auto methodTy = Method->getLoweredFunctionType(); + SILFunctionConventions targetConv(methodTy, IGF.getSILModule()); + SILFunctionConventions accessorConv(AccessorType, IGF.getSILModule()); + TypeExpansionContext expansionContext = IGM.getMaximalTypeExpansionContext(); + + auto params = IGF.collectParameters(); + + auto directResultTy = targetConv.getSILResultType(expansionContext); + const auto &directResultTI = IGM.getTypeInfo(directResultTy); + + Explosion arguments; + + unsigned numAsyncContextParams = + (unsigned)AsyncFunctionArgumentIndex::Context + 1; + (void)params.claim(numAsyncContextParams); + + // UnsafeRawPointer that holds all of the argument values. + auto *argBuffer = params.claimNext(); + // UnsafeRawPointer that is used to store the result. + auto *resultBuffer = params.claimNext(); + // Reference to a `self` of the actor to be called. + auto *actorSelf = params.claimNext(); + + GenericContextScope scope(IGM, methodTy->getInvocationGenericSignature()); + + // Preliminary: Setup async context for this accessor. + { + auto asyncContextIdx = + Signature::forAsyncEntry(IGM, AccessorType, + /*useSpecialConvention*/ false) + .getAsyncContextIndex(); + + auto entity = LinkEntity::forDistributedMethodAccessor(Method); + emitAsyncFunctionEntry(IGF, AsyncLayout, entity, asyncContextIdx); + emitAsyncFunctionPointer(IGM, IGF.CurFn, entity, AsyncLayout.getSize()); + } + + auto *typedResultBuffer = IGF.Builder.CreateBitCast( + resultBuffer, IGM.getStoragePointerType(directResultTy)); + + if (targetConv.getNumIndirectSILResults()) { + // Since tuples are not allowed as valid result types (because they cannot + // conform to protocols), there could be only a single indirect result type + // associated with distributed method. + assert(targetConv.getNumIndirectSILResults() == 1); + arguments.add(typedResultBuffer); + } + + // Step one is to load all of the data from argument buffer, + // so it could be forwarded to the distributed method. + computeArguments(argBuffer, arguments); + + // Step two, let's form and emit a call to the distributed method + // using computed argument explosion. + { + Explosion result; + Explosion error; + + auto callee = getCalleeForDistributedMethod(actorSelf); + auto emission = + getCallEmission(IGF, callee.getSwiftContext(), std::move(callee)); + + emission->begin(); + emission->setArgs(arguments, /*isOutlined=*/false, + /*witnessMetadata=*/nullptr); + + // Load result of the thunk into the location provided by the caller. + // This would only generate code for direct results, if thunk has an + // indirect result (e.g. large struct) it result buffer would be passed + // as an argument. + { + Address resultAddr(typedResultBuffer, + directResultTI.getBestKnownAlignment()); + emission->emitToMemory(resultAddr, cast(directResultTI), + /*isOutlined=*/false); + } + + // Both accessor and distributed method are always `async throws` + // so we need to load error value (if any) from the slot. + { + assert(methodTy->hasErrorResult()); + + SILType errorType = accessorConv.getSILErrorType(expansionContext); + Address calleeErrorSlot = + emission->getCalleeErrorSlot(errorType, /*isCalleeAsync=*/true); + error.add(IGF.Builder.CreateLoad(calleeErrorSlot)); + } + + emission->end(); + + // Deallocate all of the copied arguments. + { + for (auto &entry : AllocatedArguments) + entry.TI.deallocateStack(IGF, entry.Addr, entry.Type); + } + + Explosion voidResult; + emitAsyncReturn(IGF, AsyncLayout, + accessorConv.getSILResultType(expansionContext), + AccessorType, voidResult, error); + } +} + +FunctionPointer DistributedAccessor::getPointerToMethod() const { + auto fnType = Method->getLoweredFunctionType(); + auto fpKind = classifyFunctionPointerKind(Method); + auto signature = IGM.getSignature(fnType, fpKind.useSpecialConvention()); + + auto *fnPtr = + IGM.getAddrOfSILFunction(Method, NotForDefinition, + /*isDynamicallyReplaceable=*/false, + /*shouldCallPreviousImplementation=*/false); + + return FunctionPointer::forDirect(fpKind, fnPtr, /*secondary=*/nullptr, + signature); +} + +Callee +DistributedAccessor::getCalleeForDistributedMethod(llvm::Value *self) const { + auto fnType = Method->getLoweredFunctionType(); + CalleeInfo info{fnType, fnType, SubstitutionMap()}; + return {std::move(info), getPointerToMethod(), self}; +} diff --git a/lib/IRGen/IRGen.cpp b/lib/IRGen/IRGen.cpp index 4a52cda0ab55e..7d86facb6975c 100644 --- a/lib/IRGen/IRGen.cpp +++ b/lib/IRGen/IRGen.cpp @@ -1118,6 +1118,7 @@ GeneratedModule IRGenRequest::evaluate(Evaluator &evaluator, IGM.emitSwiftProtocols(/*asContiguousArray*/ false); IGM.emitProtocolConformances(/*asContiguousArray*/ false); IGM.emitTypeMetadataRecords(/*asContiguousArray*/ false); + IGM.emitAccessibleFunctions(); IGM.emitBuiltinReflectionMetadata(); IGM.emitReflectionMetadataVersion(); irgen.emitEagerClassInitialization(); @@ -1361,6 +1362,8 @@ static void performParallelIRGeneration(IRGenDescriptor desc) { irgen.emitTypeMetadataRecords(); + irgen.emitAccessibleFunctions(); + irgen.emitReflectionMetadataVersion(); irgen.emitEagerClassInitialization(); diff --git a/lib/IRGen/IRGenModule.cpp b/lib/IRGen/IRGenModule.cpp index 89c9cb80551b6..7f355a56b82ce 100644 --- a/lib/IRGen/IRGenModule.cpp +++ b/lib/IRGen/IRGenModule.cpp @@ -600,6 +600,10 @@ IRGenModule::IRGenModule(IRGenerator &irgen, DynamicReplacementKeyTy = createStructType(*this, "swift.dyn_repl_key", {RelativeAddressTy, Int32Ty}); + AccessibleFunctionRecordTy = createStructType( + *this, "swift.accessible_function", + {RelativeAddressTy, RelativeAddressTy, RelativeAddressTy, Int32Ty}); + AsyncFunctionPointerTy = createStructType(*this, "swift.async_func_pointer", {RelativeAddressTy, Int32Ty}, true); SwiftContextTy = llvm::StructType::create(getLLVMContext(), "swift.context"); diff --git a/lib/IRGen/IRGenModule.h b/lib/IRGen/IRGenModule.h index f83e353331088..853827163eefb 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -401,6 +401,10 @@ class IRGenerator { /// Emit type metadata records for types without explicit protocol conformance. void emitTypeMetadataRecords(); + /// Emit type metadata recrods for functions that can be looked up by name at + /// runtime. + void emitAccessibleFunctions(); + /// Emit reflection metadata records for builtin and imported types referenced /// from this module. void emitBuiltinReflectionMetadata(); @@ -728,6 +732,8 @@ class IRGenModule { *DynamicReplacementLinkEntryPtrTy; // %link_entry* llvm::StructType *DynamicReplacementKeyTy; // { i32, i32} + llvm::StructType *AccessibleFunctionRecordTy; // { i32*, i32*, i32} + llvm::StructType *AsyncFunctionPointerTy; // { i32, i32 } llvm::StructType *SwiftContextTy; llvm::StructType *SwiftTaskTy; @@ -1035,11 +1041,13 @@ class IRGenModule { void addObjCClass(llvm::Constant *addr, bool nonlazy); void addObjCClassStub(llvm::Constant *addr); void addProtocolConformance(ConformanceDescription &&conformance); + void addAccessibleFunction(SILFunction *func); llvm::Constant *emitSwiftProtocols(bool asContiguousArray); llvm::Constant *emitProtocolConformances(bool asContiguousArray); llvm::Constant *emitTypeMetadataRecords(bool asContiguousArray); - llvm::Constant *emitFieldDescriptors(); + + void emitAccessibleFunctions(); llvm::Constant *getConstantSignedFunctionPointer(llvm::Constant *fn, CanSILFunctionType fnType); @@ -1168,6 +1176,9 @@ class IRGenModule { /// List of ExtensionDecls corresponding to the generated /// categories. SmallVector ObjCCategoryDecls; + /// List of all of the functions, which can be lookup by name + /// up at runtime. + SmallVector AccessibleFunctions; /// Map of Objective-C protocols and protocol references, bitcast to i8*. /// The interesting global variables relating to an ObjC protocol. @@ -1631,6 +1642,12 @@ private: \ Address getAddrOfObjCISAMask(); + llvm::Function * + getAddrOfDistributedMethodAccessor(SILFunction *F, + ForDefinition_t forDefinition); + + void emitDistributedMethodAccessor(SILFunction *method); + /// Retrieve the generic signature for the current generic context, or null if no /// generic environment is active. CanGenericSignature getCurGenericContext(); diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index a55c4258127f8..d2076c41bfbb1 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -2199,6 +2199,13 @@ void IRGenSILFunction::emitSILFunction() { IGM.noteSwiftAsyncFunctionDef(); } + // Emit distributed accessor, and mark the thunk as accessible + // by name at runtime through it. + if (CurSILFn->isDistributed() && CurSILFn->isThunk()) { + IGM.emitDistributedMethodAccessor(CurSILFn); + IGM.addAccessibleFunction(CurSILFn); + } + // Configure the dominance resolver. // TODO: consider re-using a dom analysis from the PassManager // TODO: consider using a cheaper analysis at -O0 diff --git a/lib/IRGen/Linking.cpp b/lib/IRGen/Linking.cpp index c58a3627f2e2d..24a3b16a6718f 100644 --- a/lib/IRGen/Linking.cpp +++ b/lib/IRGen/Linking.cpp @@ -469,7 +469,8 @@ std::string LinkEntity::mangleAsString() const { case Kind::DispatchThunkAsyncFunctionPointer: case Kind::DispatchThunkInitializerAsyncFunctionPointer: case Kind::DispatchThunkAllocatorAsyncFunctionPointer: - case Kind::PartialApplyForwarderAsyncFunctionPointer: { + case Kind::PartialApplyForwarderAsyncFunctionPointer: + case Kind::DistributedMethodAccessorAsyncPointer: { std::string Result(getUnderlyingEntityForAsyncFunctionPointer() .mangleAsString()); Result.append("Tu"); @@ -492,11 +493,24 @@ std::string LinkEntity::mangleAsString() const { Result.append("Tu"); return Result; } - case Kind::PartialApplyForwarder: + case Kind::PartialApplyForwarder: { std::string Result; Result = std::string(static_cast(Pointer)->getName()); return Result; } + + case Kind::DistributedMethodAccessor: { + std::string Result(getSILFunction()->getName()); + Result.append("TF"); + return Result; + } + + case Kind::AccessibleFunctionRecord: { + std::string Result(getSILFunction()->getName()); + Result.append("HF"); + return Result; + } + } llvm_unreachable("bad entity kind!"); } @@ -790,11 +804,14 @@ SILLinkage LinkEntity::getLinkage(ForDefinition_t forDefinition) const { case Kind::DispatchThunkInitializerAsyncFunctionPointer: case Kind::DispatchThunkAllocatorAsyncFunctionPointer: case Kind::PartialApplyForwarderAsyncFunctionPointer: + case Kind::DistributedMethodAccessorAsyncPointer: return getUnderlyingEntityForAsyncFunctionPointer() .getLinkage(forDefinition); case Kind::KnownAsyncFunctionPointer: return SILLinkage::PublicExternal; case Kind::PartialApplyForwarder: + case Kind::DistributedMethodAccessor: + case Kind::AccessibleFunctionRecord: return SILLinkage::Private; } llvm_unreachable("bad link entity kind"); @@ -821,6 +838,7 @@ bool LinkEntity::isContextDescriptor() const { case Kind::DispatchThunkInitializerAsyncFunctionPointer: case Kind::DispatchThunkAllocatorAsyncFunctionPointer: case Kind::PartialApplyForwarderAsyncFunctionPointer: + case Kind::DistributedMethodAccessorAsyncPointer: case Kind::MethodDescriptor: case Kind::MethodDescriptorDerivative: case Kind::MethodDescriptorInitializer: @@ -885,6 +903,8 @@ bool LinkEntity::isContextDescriptor() const { case Kind::CanonicalPrespecializedGenericTypeCachingOnceToken: case Kind::PartialApplyForwarder: case Kind::KnownAsyncFunctionPointer: + case Kind::DistributedMethodAccessor: + case Kind::AccessibleFunctionRecord: return false; } llvm_unreachable("invalid descriptor"); @@ -1005,11 +1025,14 @@ llvm::Type *LinkEntity::getDefaultDeclarationType(IRGenModule &IGM) const { case Kind::DispatchThunkAllocatorAsyncFunctionPointer: case Kind::DistributedThunkAsyncFunctionPointer: case Kind::PartialApplyForwarderAsyncFunctionPointer: + case Kind::DistributedMethodAccessorAsyncPointer: case Kind::AsyncFunctionPointerAST: case Kind::KnownAsyncFunctionPointer: return IGM.AsyncFunctionPointerTy; case Kind::PartialApplyForwarder: return IGM.FunctionPtrTy; + case Kind::AccessibleFunctionRecord: + return IGM.AccessibleFunctionRecordTy; default: llvm_unreachable("declaration LLVM type not specified"); } @@ -1040,12 +1063,14 @@ Alignment LinkEntity::getAlignment(IRGenModule &IGM) const { case Kind::MethodDescriptorAllocator: case Kind::OpaqueTypeDescriptor: case Kind::OpaqueTypeDescriptorRecord: + case Kind::AccessibleFunctionRecord: return Alignment(4); case Kind::AsyncFunctionPointer: case Kind::DispatchThunkAsyncFunctionPointer: case Kind::DispatchThunkInitializerAsyncFunctionPointer: case Kind::DispatchThunkAllocatorAsyncFunctionPointer: case Kind::PartialApplyForwarderAsyncFunctionPointer: + case Kind::DistributedMethodAccessorAsyncPointer: case Kind::KnownAsyncFunctionPointer: case Kind::ObjCClassRef: case Kind::ObjCClass: @@ -1092,7 +1117,8 @@ bool LinkEntity::isWeakImported(ModuleDecl *module) const { return false; case Kind::DynamicallyReplaceableFunctionKey: case Kind::DynamicallyReplaceableFunctionVariable: - case Kind::SILFunction: { + case Kind::SILFunction: + case Kind::DistributedMethodAccessor: { return getSILFunction()->isWeakImported(); } @@ -1193,6 +1219,7 @@ bool LinkEntity::isWeakImported(ModuleDecl *module) const { case Kind::ReflectionFieldDescriptor: case Kind::CoroutineContinuationPrototype: case Kind::DifferentiabilityWitness: + case Kind::AccessibleFunctionRecord: return false; case Kind::AsyncFunctionPointer: @@ -1200,6 +1227,7 @@ bool LinkEntity::isWeakImported(ModuleDecl *module) const { case Kind::DispatchThunkInitializerAsyncFunctionPointer: case Kind::DispatchThunkAllocatorAsyncFunctionPointer: case Kind::PartialApplyForwarderAsyncFunctionPointer: + case Kind::DistributedMethodAccessorAsyncPointer: return getUnderlyingEntityForAsyncFunctionPointer() .isWeakImported(module); case Kind::KnownAsyncFunctionPointer: @@ -1328,8 +1356,15 @@ DeclContext *LinkEntity::getDeclContextForEmission() const { case Kind::DispatchThunkInitializerAsyncFunctionPointer: case Kind::DispatchThunkAllocatorAsyncFunctionPointer: case Kind::PartialApplyForwarderAsyncFunctionPointer: + case Kind::DistributedMethodAccessorAsyncPointer: return getUnderlyingEntityForAsyncFunctionPointer() .getDeclContextForEmission(); + + case Kind::DistributedMethodAccessor: + case Kind::AccessibleFunctionRecord: { + auto *funcDC = getSILFunction()->getDeclContext(); + return funcDC->getParentModule(); + } } llvm_unreachable("invalid decl kind"); } diff --git a/lib/SIL/IR/SILFunction.cpp b/lib/SIL/IR/SILFunction.cpp index fe90ca4fec84e..c3c9cf017808c 100644 --- a/lib/SIL/IR/SILFunction.cpp +++ b/lib/SIL/IR/SILFunction.cpp @@ -86,6 +86,7 @@ SILFunction::create(SILModule &M, SILLinkage linkage, StringRef name, IsBare_t isBareSILFunction, IsTransparent_t isTrans, IsSerialized_t isSerialized, ProfileCounter entryCount, IsDynamicallyReplaceable_t isDynamic, + IsDistributed_t isDistributed, IsExactSelfClass_t isExactSelfClass, IsThunk_t isThunk, SubclassScope classSubclassScope, Inline_t inlineStrategy, @@ -108,14 +109,15 @@ SILFunction::create(SILModule &M, SILLinkage linkage, StringRef name, // deleted. And afterwards the same specialization is created again. fn->init(linkage, name, loweredType, genericEnv, loc, isBareSILFunction, isTrans, isSerialized, entryCount, isThunk, classSubclassScope, - inlineStrategy, E, debugScope, isDynamic, isExactSelfClass); + inlineStrategy, E, debugScope, isDynamic, isExactSelfClass, + isDistributed); assert(fn->empty()); } else { fn = new (M) SILFunction(M, linkage, name, loweredType, genericEnv, loc, isBareSILFunction, isTrans, isSerialized, entryCount, isThunk, classSubclassScope, inlineStrategy, E, debugScope, - isDynamic, isExactSelfClass); + isDynamic, isExactSelfClass, isDistributed); } if (entry) entry->setValue(fn); @@ -139,12 +141,13 @@ SILFunction::SILFunction(SILModule &Module, SILLinkage Linkage, StringRef Name, Inline_t inlineStrategy, EffectsKind E, const SILDebugScope *DebugScope, IsDynamicallyReplaceable_t isDynamic, - IsExactSelfClass_t isExactSelfClass) + IsExactSelfClass_t isExactSelfClass, + IsDistributed_t isDistributed) : SwiftObjectHeader(registeredMetatype), Module(Module), Availability(AvailabilityContext::alwaysAvailable()) { init(Linkage, Name, LoweredType, genericEnv, Loc, isBareSILFunction, isTrans, isSerialized, entryCount, isThunk, classSubclassScope, inlineStrategy, - E, DebugScope, isDynamic, isExactSelfClass); + E, DebugScope, isDynamic, isExactSelfClass, isDistributed); // Set our BB list to have this function as its parent. This enables us to // splice efficiently basic blocks in between functions. @@ -161,7 +164,8 @@ void SILFunction::init(SILLinkage Linkage, StringRef Name, Inline_t inlineStrategy, EffectsKind E, const SILDebugScope *DebugScope, IsDynamicallyReplaceable_t isDynamic, - IsExactSelfClass_t isExactSelfClass) { + IsExactSelfClass_t isExactSelfClass, + IsDistributed_t isDistributed) { this->Name = Name; this->LoweredType = LoweredType; this->GenericEnv = genericEnv; @@ -180,6 +184,7 @@ void SILFunction::init(SILLinkage Linkage, StringRef Name, this->IsWeakImported = false; this->IsDynamicReplaceable = isDynamic; this->ExactSelfClass = isExactSelfClass; + this->IsDistributed = isDistributed; this->Inlined = false; this->Zombie = false; this->HasOwnership = true, @@ -678,6 +683,9 @@ SILFunction::isPossiblyUsedExternally() const { if (ReplacedFunction) return true; + if (isDistributed() && isThunk()) + return true; + return swift::isPossiblyUsedExternally(linkage, getModule().isWholeModule()); } diff --git a/lib/SIL/IR/SILFunctionBuilder.cpp b/lib/SIL/IR/SILFunctionBuilder.cpp index 65171d0aeb71a..1499e0d0f6914 100644 --- a/lib/SIL/IR/SILFunctionBuilder.cpp +++ b/lib/SIL/IR/SILFunctionBuilder.cpp @@ -21,7 +21,8 @@ using namespace swift; SILFunction *SILFunctionBuilder::getOrCreateFunction( SILLocation loc, StringRef name, SILLinkage linkage, CanSILFunctionType type, IsBare_t isBareSILFunction, IsTransparent_t isTransparent, IsSerialized_t isSerialized, - IsDynamicallyReplaceable_t isDynamic, ProfileCounter entryCount, + IsDynamicallyReplaceable_t isDynamic, IsDistributed_t isDistributed, + ProfileCounter entryCount, IsThunk_t isThunk, SubclassScope subclassScope) { assert(!type->isNoEscape() && "Function decls always have escaping types."); if (auto fn = mod.lookUpFunction(name)) { @@ -33,8 +34,8 @@ SILFunction *SILFunctionBuilder::getOrCreateFunction( auto fn = SILFunction::create(mod, linkage, name, type, nullptr, loc, isBareSILFunction, isTransparent, isSerialized, - entryCount, isDynamic, IsNotExactSelfClass, - isThunk, subclassScope); + entryCount, isDynamic, isDistributed, + IsNotExactSelfClass, isThunk, subclassScope); fn->setDebugScope(new (mod) SILDebugScope(loc, fn)); return fn; } @@ -229,9 +230,15 @@ SILFunction *SILFunctionBuilder::getOrCreateFunction( IsTrans = IsNotTransparent; } + IsDistributed_t IsDistributed = IsDistributed_t::IsNotDistributed; + // Mark both distributed thunks and methods as distributed. + if (constant.hasFuncDecl() && constant.getFuncDecl()->isDistributed()) { + IsDistributed = IsDistributed_t::IsDistributed; + } + auto *F = SILFunction::create(mod, linkage, name, constantType, nullptr, None, IsNotBare, IsTrans, IsSer, entryCount, IsDyn, - IsNotExactSelfClass, + IsDistributed, IsNotExactSelfClass, IsNotThunk, constant.getSubclassScope(), inlineStrategy, EK); F->setDebugScope(new (mod) SILDebugScope(loc, F)); @@ -280,10 +287,10 @@ SILFunction *SILFunctionBuilder::getOrCreateSharedFunction( SILLocation loc, StringRef name, CanSILFunctionType type, IsBare_t isBareSILFunction, IsTransparent_t isTransparent, IsSerialized_t isSerialized, ProfileCounter entryCount, IsThunk_t isThunk, - IsDynamicallyReplaceable_t isDynamic) { + IsDynamicallyReplaceable_t isDynamic, IsDistributed_t isDistributed) { return getOrCreateFunction(loc, name, SILLinkage::Shared, type, isBareSILFunction, isTransparent, isSerialized, - isDynamic, entryCount, isThunk, + isDynamic, isDistributed, entryCount, isThunk, SubclassScope::NotApplicable); } @@ -292,12 +299,13 @@ SILFunction *SILFunctionBuilder::createFunction( GenericEnvironment *genericEnv, Optional loc, IsBare_t isBareSILFunction, IsTransparent_t isTrans, IsSerialized_t isSerialized, IsDynamicallyReplaceable_t isDynamic, - ProfileCounter entryCount, IsThunk_t isThunk, SubclassScope subclassScope, + IsDistributed_t isDistributed, ProfileCounter entryCount, + IsThunk_t isThunk, SubclassScope subclassScope, Inline_t inlineStrategy, EffectsKind EK, SILFunction *InsertBefore, const SILDebugScope *DebugScope) { return SILFunction::create(mod, linkage, name, loweredType, genericEnv, loc, isBareSILFunction, isTrans, isSerialized, - entryCount, isDynamic, IsNotExactSelfClass, - isThunk, subclassScope, + entryCount, isDynamic, isDistributed, + IsNotExactSelfClass, isThunk, subclassScope, inlineStrategy, EK, InsertBefore, DebugScope); } diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index f944283617e0a..f84715bac7798 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -2884,6 +2884,9 @@ void SILFunction::print(SILPrintContext &PrintCtx) const { if (isDynamicallyReplaceable()) { OS << "[dynamically_replacable] "; } + if (isDistributed()) { + OS << "[distributed] "; + } if (isExactSelfClass()) { OS << "[exact_self_class] "; } diff --git a/lib/SIL/Parser/ParseSIL.cpp b/lib/SIL/Parser/ParseSIL.cpp index 4b6d4bef91339..f7d5b7539ae2e 100644 --- a/lib/SIL/Parser/ParseSIL.cpp +++ b/lib/SIL/Parser/ParseSIL.cpp @@ -955,6 +955,7 @@ static bool parseDeclSILOptional(bool *isTransparent, bool *hasOwnershipSSA, IsThunk_t *isThunk, IsDynamicallyReplaceable_t *isDynamic, + IsDistributed_t *isDistributed, IsExactSelfClass_t *isExactSelfClass, SILFunction **dynamicallyReplacedFunction, Identifier *objCReplacementFor, @@ -987,6 +988,8 @@ static bool parseDeclSILOptional(bool *isTransparent, *isSerialized = IsSerialized; else if (isDynamic && SP.P.Tok.getText() == "dynamically_replacable") *isDynamic = IsDynamic; + else if (isDistributed && SP.P.Tok.getText() == "distributed") + *isDistributed = IsDistributed; else if (isExactSelfClass && SP.P.Tok.getText() == "exact_self_class") *isExactSelfClass = IsExactSelfClass; else if (isSerialized && SP.P.Tok.getText() == "serializable") @@ -6265,6 +6268,7 @@ bool SILParserState::parseDeclSIL(Parser &P) { IsSerialized_t isSerialized = IsNotSerialized; bool isCanonical = false; IsDynamicallyReplaceable_t isDynamic = IsNotDynamic; + IsDistributed_t isDistributed = IsNotDistributed; IsExactSelfClass_t isExactSelfClass = IsNotExactSelfClass; bool hasOwnershipSSA = false; IsThunk_t isThunk = IsNotThunk; @@ -6284,9 +6288,10 @@ bool SILParserState::parseDeclSIL(Parser &P) { if (parseSILLinkage(FnLinkage, P) || parseDeclSILOptional( &isTransparent, &isSerialized, &isCanonical, &hasOwnershipSSA, - &isThunk, &isDynamic, &isExactSelfClass, &DynamicallyReplacedFunction, - &objCReplacementFor, &specialPurpose, &inlineStrategy, - &optimizationMode, &perfConstr, nullptr, &isWeakImported, &availability, + &isThunk, &isDynamic, &isDistributed, &isExactSelfClass, + &DynamicallyReplacedFunction, &objCReplacementFor, &specialPurpose, + &inlineStrategy, &optimizationMode, &perfConstr, nullptr, + &isWeakImported, &availability, &isWithoutActuallyEscapingThunk, &Semantics, &SpecAttrs, &ClangDecl, &MRK, FunctionState, M) || P.parseToken(tok::at_sign, diag::expected_sil_function_name) || @@ -6318,6 +6323,7 @@ bool SILParserState::parseDeclSIL(Parser &P) { FunctionState.F->setOwnershipEliminated(); FunctionState.F->setThunk(IsThunk_t(isThunk)); FunctionState.F->setIsDynamic(isDynamic); + FunctionState.F->setIsDistributed(isDistributed); FunctionState.F->setIsExactSelfClass(isExactSelfClass); FunctionState.F->setDynamicallyReplacedFunction( DynamicallyReplacedFunction); @@ -6495,7 +6501,7 @@ bool SILParserState::parseSILGlobal(Parser &P) { SILParser State(P); if (parseSILLinkage(GlobalLinkage, P) || parseDeclSILOptional(nullptr, &isSerialized, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, &isLet, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, State, M) || @@ -6548,7 +6554,7 @@ bool SILParserState::parseSILProperty(Parser &P) { if (parseDeclSILOptional(nullptr, &Serialized, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr, SP, M)) + nullptr, nullptr, nullptr, nullptr, nullptr, SP, M)) return true; ValueDecl *VD; @@ -6617,7 +6623,7 @@ bool SILParserState::parseSILVTable(Parser &P) { if (parseDeclSILOptional(nullptr, &Serialized, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, VTableState, M)) return true; @@ -7134,7 +7140,7 @@ bool SILParserState::parseSILWitnessTable(Parser &P) { if (parseDeclSILOptional(nullptr, &isSerialized, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, WitnessState, M)) return true; diff --git a/lib/SIL/Parser/SILParserFunctionBuilder.h b/lib/SIL/Parser/SILParserFunctionBuilder.h index 1714056902008..45667d023b26a 100644 --- a/lib/SIL/Parser/SILParserFunctionBuilder.h +++ b/lib/SIL/Parser/SILParserFunctionBuilder.h @@ -28,7 +28,7 @@ class LLVM_LIBRARY_VISIBILITY SILParserFunctionBuilder { SILLocation loc) { auto *result = builder.createFunction( SILLinkage::Private, name, ty, nullptr, loc, IsNotBare, - IsNotTransparent, IsNotSerialized, IsNotDynamic); + IsNotTransparent, IsNotSerialized, IsNotDynamic, IsNotDistributed); result->setDebugScope(new (builder.mod) SILDebugScope(loc, result)); return result; } diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index 475585ee756cf..f3febf7c8ed8f 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -603,7 +603,8 @@ SILGenModule::getKeyPathProjectionCoroutine(bool isReadAccess, IsNotBare, IsNotTransparent, IsNotSerialized, - IsNotDynamic); + IsNotDynamic, + IsNotDistributed); return fn; } @@ -1622,7 +1623,7 @@ SILFunction *SILGenModule::emitLazyGlobalInitializer(StringRef funcName, SILGenFunctionBuilder builder(*this); auto *f = builder.createFunction( SILLinkage::Private, funcName, initSILType, nullptr, SILLocation(binding), - IsNotBare, IsNotTransparent, IsNotSerialized, IsNotDynamic); + IsNotBare, IsNotTransparent, IsNotSerialized, IsNotDynamic, IsNotDistributed); f->setSpecialPurpose(SILFunction::Purpose::GlobalInitOnceFunction); f->setDebugScope(new (M) SILDebugScope(RegularLocation(binding), f)); auto dc = binding->getDeclContext(); diff --git a/lib/SILGen/SILGenBridging.cpp b/lib/SILGen/SILGenBridging.cpp index a2818560bb300..1d9b726e7ec3c 100644 --- a/lib/SILGen/SILGenBridging.cpp +++ b/lib/SILGen/SILGenBridging.cpp @@ -1523,7 +1523,8 @@ SILFunction *SILGenFunction::emitNativeAsyncToForeignThunk(SILDeclRef thunk) { F.isSerialized(), ProfileCounter(), IsThunk, - IsNotDynamic); + IsNotDynamic, + IsNotDistributed); auto closureRef = B.createFunctionRef(loc, closure); diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 9c8e71884cf25..a5efd1a041344 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -2779,7 +2779,7 @@ static SILFunction *getOrCreateKeyPathGetter(SILGenModule &SGM, (expansion == ResilienceExpansion::Minimal ? IsSerializable : IsNotSerialized), - ProfileCounter(), IsThunk, IsNotDynamic); + ProfileCounter(), IsThunk, IsNotDynamic, IsNotDistributed); if (!thunk->empty()) return thunk; @@ -2927,7 +2927,7 @@ static SILFunction *getOrCreateKeyPathSetter(SILGenModule &SGM, (expansion == ResilienceExpansion::Minimal ? IsSerializable : IsNotSerialized), - ProfileCounter(), IsThunk, IsNotDynamic); + ProfileCounter(), IsThunk, IsNotDynamic, IsNotDistributed); if (!thunk->empty()) return thunk; @@ -3105,7 +3105,7 @@ getOrCreateKeyPathEqualsAndHash(SILGenModule &SGM, (expansion == ResilienceExpansion::Minimal ? IsSerializable : IsNotSerialized), - ProfileCounter(), IsThunk, IsNotDynamic); + ProfileCounter(), IsThunk, IsNotDynamic, IsNotDistributed); if (!equals->empty()) { return; } @@ -3282,7 +3282,7 @@ getOrCreateKeyPathEqualsAndHash(SILGenModule &SGM, (expansion == ResilienceExpansion::Minimal ? IsSerializable : IsNotSerialized), - ProfileCounter(), IsThunk, IsNotDynamic); + ProfileCounter(), IsThunk, IsNotDynamic, IsNotDistributed); if (!hash->empty()) { return; } diff --git a/lib/SILGen/SILGenFunction.cpp b/lib/SILGen/SILGenFunction.cpp index 080e2582c56cd..e9fc159ef09d7 100644 --- a/lib/SILGen/SILGenFunction.cpp +++ b/lib/SILGen/SILGenFunction.cpp @@ -657,7 +657,7 @@ void SILGenFunction::emitArtificialTopLevel(Decl *mainDecl) { auto NSStringFromClassFn = builder.getOrCreateFunction( mainClass, "NSStringFromClass", SILLinkage::PublicExternal, NSStringFromClassType, IsBare, IsTransparent, IsNotSerialized, - IsNotDynamic); + IsNotDynamic, IsNotDistributed); auto NSStringFromClass = B.createFunctionRef(mainClass, NSStringFromClassFn); SILValue metaTy = B.createMetatype(mainClass, SILType::getPrimitiveObjectType(mainClassMetaty)); @@ -747,7 +747,7 @@ void SILGenFunction::emitArtificialTopLevel(Decl *mainDecl) { auto NSApplicationMainFn = builder.getOrCreateFunction( mainClass, "NSApplicationMain", SILLinkage::PublicExternal, NSApplicationMainType, IsBare, IsTransparent, IsNotSerialized, - IsNotDynamic); + IsNotDynamic, IsNotDistributed); auto NSApplicationMain = B.createFunctionRef(mainClass, NSApplicationMainFn); SILValue args[] = { argc, argv }; diff --git a/lib/SILGen/SILGenPoly.cpp b/lib/SILGen/SILGenPoly.cpp index 5b55968d725a6..62c47e04629a8 100644 --- a/lib/SILGen/SILGenPoly.cpp +++ b/lib/SILGen/SILGenPoly.cpp @@ -3692,7 +3692,7 @@ ManagedValue SILGenFunction::getThunkedAutoDiffLinearMap( SILGenFunctionBuilder fb(SGM); auto *thunk = fb.getOrCreateSharedFunction( loc, name, thunkDeclType, IsBare, IsTransparent, IsSerialized, - ProfileCounter(), IsReabstractionThunk, IsNotDynamic); + ProfileCounter(), IsReabstractionThunk, IsNotDynamic, IsNotDistributed); // Partially-apply the thunk to `linearMap` and return the thunked value. auto getThunkedResult = [&]() { @@ -3961,6 +3961,7 @@ SILFunction *SILGenModule::getOrCreateCustomDerivativeThunk( loc, name, linkage, thunkFnTy, IsBare, IsNotTransparent, customDerivativeFn->isSerialized(), customDerivativeFn->isDynamicallyReplaceable(), + customDerivativeFn->isDistributed(), customDerivativeFn->getEntryCount(), IsThunk, customDerivativeFn->getClassSubclassScope()); // This thunk may be publicly exposed and cannot be transparent. diff --git a/lib/SILGen/SILGenThunk.cpp b/lib/SILGen/SILGenThunk.cpp index df5ad2a51506f..ff968bc6314dc 100644 --- a/lib/SILGen/SILGenThunk.cpp +++ b/lib/SILGen/SILGenThunk.cpp @@ -60,7 +60,8 @@ SILFunction *SILGenModule::getDynamicThunk(SILDeclRef constant, SILGenFunctionBuilder builder(*this); auto F = builder.getOrCreateFunction( constant.getDecl(), name, SILLinkage::Shared, constantTy, IsBare, - IsTransparent, IsSerializable, IsNotDynamic, ProfileCounter(), IsThunk); + IsTransparent, IsSerializable, IsNotDynamic, IsNotDistributed, + ProfileCounter(), IsThunk); if (F->empty()) { // Emit the thunk if we haven't yet. @@ -257,7 +258,8 @@ SILFunction *SILGenModule::getOrCreateForeignAsyncCompletionHandlerImplFunction( IsBare, IsTransparent, IsSerializable, ProfileCounter(), IsThunk, - IsNotDynamic); + IsNotDynamic, + IsNotDistributed); if (F->empty()) { // Emit the implementation. @@ -516,7 +518,7 @@ getOrCreateReabstractionThunk(CanSILFunctionType thunkType, SILGenFunctionBuilder builder(*this); return builder.getOrCreateSharedFunction( loc, name, thunkDeclType, IsBare, IsTransparent, serializable, - ProfileCounter(), IsReabstractionThunk, IsNotDynamic); + ProfileCounter(), IsReabstractionThunk, IsNotDynamic, IsNotDistributed); } SILFunction *SILGenModule::getOrCreateDerivativeVTableThunk( @@ -538,7 +540,7 @@ SILFunction *SILGenModule::getOrCreateDerivativeVTableThunk( auto *thunk = builder.getOrCreateFunction( derivativeFnDecl, name, SILLinkage::Private, constantTy, IsBare, IsTransparent, derivativeFnDeclRef.isSerialized(), IsNotDynamic, - ProfileCounter(), IsThunk); + IsNotDistributed, ProfileCounter(), IsThunk); if (!thunk->empty()) return thunk; diff --git a/lib/SILGen/SILGenType.cpp b/lib/SILGen/SILGenType.cpp index 114819a1d3241..1f095a281ba16 100644 --- a/lib/SILGen/SILGenType.cpp +++ b/lib/SILGen/SILGenType.cpp @@ -193,7 +193,7 @@ SILGenModule::emitVTableMethod(ClassDecl *theClass, SILLinkage::Private, name, overrideInfo.SILFnType, genericEnv, loc, IsBare, IsNotTransparent, IsNotSerialized, IsNotDynamic, - ProfileCounter(), IsThunk); + IsNotDistributed, ProfileCounter(), IsThunk); thunk->setDebugScope(new (M) SILDebugScope(loc, thunk)); PrettyStackTraceSILFunction trace("generating vtable thunk", thunk); @@ -784,8 +784,8 @@ SILFunction *SILGenModule::emitProtocolWitness( auto *f = builder.createFunction( linkage, nameBuffer, witnessSILFnType, genericEnv, SILLocation(witnessRef.getDecl()), IsNotBare, IsTransparent, isSerialized, - IsNotDynamic, ProfileCounter(), IsThunk, SubclassScope::NotApplicable, - InlineStrategy); + IsNotDynamic, IsNotDistributed, ProfileCounter(), IsThunk, + SubclassScope::NotApplicable, InlineStrategy); f->setDebugScope(new (M) SILDebugScope(RegularLocation(witnessRef.getDecl()), f)); @@ -856,7 +856,7 @@ static SILFunction *emitSelfConformanceWitness(SILGenModule &SGM, auto *f = builder.createFunction( linkage, name, witnessSILFnType, genericEnv, SILLocation(requirement.getDecl()), IsNotBare, IsTransparent, - IsSerialized, IsNotDynamic, ProfileCounter(), IsThunk, + IsSerialized, IsNotDynamic, IsNotDistributed, ProfileCounter(), IsThunk, SubclassScope::NotApplicable, InlineDefault); f->setDebugScope(new (SGM.M) diff --git a/lib/SILOptimizer/Differentiation/JVPCloner.cpp b/lib/SILOptimizer/Differentiation/JVPCloner.cpp index 13511db45d2e4..926ef1d900e50 100644 --- a/lib/SILOptimizer/Differentiation/JVPCloner.cpp +++ b/lib/SILOptimizer/Differentiation/JVPCloner.cpp @@ -1704,7 +1704,8 @@ void JVPCloner::Implementation::prepareForDifferentialGeneration() { linkage, context.getASTContext().getIdentifier(diffName).str(), diffType, diffGenericEnv, original->getLocation(), original->isBare(), IsNotTransparent, jvp->isSerialized(), - original->isDynamicallyReplaceable()); + original->isDynamicallyReplaceable(), + original->isDistributed()); differential->setDebugScope( new (module) SILDebugScope(original->getLocation(), differential)); diff --git a/lib/SILOptimizer/Differentiation/Thunk.cpp b/lib/SILOptimizer/Differentiation/Thunk.cpp index 95932e9c7c2df..e9f5a88cafc7d 100644 --- a/lib/SILOptimizer/Differentiation/Thunk.cpp +++ b/lib/SILOptimizer/Differentiation/Thunk.cpp @@ -324,7 +324,7 @@ SILFunction *getOrCreateReabstractionThunk(SILOptFunctionBuilder &fb, auto *thunk = fb.getOrCreateSharedFunction( loc, name, thunkDeclType, IsBare, IsTransparent, IsSerialized, - ProfileCounter(), IsReabstractionThunk, IsNotDynamic); + ProfileCounter(), IsReabstractionThunk, IsNotDynamic, IsNotDistributed); if (!thunk->empty()) return thunk; @@ -582,7 +582,7 @@ getOrCreateSubsetParametersThunkForLinearMap( auto loc = parentThunk->getLocation(); auto *thunk = fb.getOrCreateSharedFunction( loc, thunkName, thunkType, IsBare, IsTransparent, IsSerialized, - ProfileCounter(), IsThunk, IsNotDynamic); + ProfileCounter(), IsThunk, IsNotDynamic, IsNotDistributed); if (!thunk->empty()) return {thunk, interfaceSubs}; @@ -863,7 +863,7 @@ getOrCreateSubsetParametersThunkForDerivativeFunction( auto loc = origFnOperand.getLoc(); auto *thunk = fb.getOrCreateSharedFunction( loc, thunkName, thunkType, IsBare, IsTransparent, caller->isSerialized(), - ProfileCounter(), IsThunk, IsNotDynamic); + ProfileCounter(), IsThunk, IsNotDynamic, IsNotDistributed); if (!thunk->empty()) return {thunk, interfaceSubs}; diff --git a/lib/SILOptimizer/Differentiation/VJPCloner.cpp b/lib/SILOptimizer/Differentiation/VJPCloner.cpp index 840330d7230fb..2e07f82ef1767 100644 --- a/lib/SILOptimizer/Differentiation/VJPCloner.cpp +++ b/lib/SILOptimizer/Differentiation/VJPCloner.cpp @@ -996,7 +996,7 @@ SILFunction *VJPCloner::Implementation::createEmptyPullback() { linkage, context.getASTContext().getIdentifier(pbName).str(), pbType, pbGenericEnv, original->getLocation(), original->isBare(), IsNotTransparent, vjp->isSerialized(), - original->isDynamicallyReplaceable()); + original->isDynamicallyReplaceable(), original->isDistributed()); pullback->setDebugScope(new (module) SILDebugScope(original->getLocation(), pullback)); return pullback; diff --git a/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp b/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp index 03ed4864d3306..47173c70b4aac 100644 --- a/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp +++ b/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp @@ -631,9 +631,9 @@ void ExistentialTransform::createExistentialSpecializedFunction() { NewF = FunctionBuilder.createFunction( linkage, Name, NewFTy, NewFGenericEnv, F->getLocation(), F->isBare(), - F->isTransparent(), F->isSerialized(), IsNotDynamic, F->getEntryCount(), - F->isThunk(), F->getClassSubclassScope(), F->getInlineStrategy(), - F->getEffectsKind(), nullptr, F->getDebugScope()); + F->isTransparent(), F->isSerialized(), IsNotDynamic, IsNotDistributed, + F->getEntryCount(), F->isThunk(), F->getClassSubclassScope(), + F->getInlineStrategy(), F->getEffectsKind(), nullptr, F->getDebugScope()); /// Set the semantics attributes for the new function. for (auto &Attr : F->getSemanticsAttrs()) diff --git a/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.cpp b/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.cpp index 7f8754d6ed07a..5e7c98db5d7e8 100644 --- a/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.cpp +++ b/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.cpp @@ -521,8 +521,8 @@ void FunctionSignatureTransform::createFunctionSignatureOptimizedFunction() { // classSubclassScope. TransformDescriptor.OptimizedFunction = FunctionBuilder.createFunction( linkage, Name, NewFTy, NewFGenericEnv, F->getLocation(), F->isBare(), - F->isTransparent(), F->isSerialized(), IsNotDynamic, F->getEntryCount(), - F->isThunk(), + F->isTransparent(), F->isSerialized(), IsNotDynamic, IsNotDistributed, + F->getEntryCount(), F->isThunk(), /*classSubclassScope=*/SubclassScope::NotApplicable, F->getInlineStrategy(), F->getEffectsKind(), nullptr, F->getDebugScope()); SILFunction *NewF = TransformDescriptor.OptimizedFunction.get(); diff --git a/lib/SILOptimizer/IPO/CapturePropagation.cpp b/lib/SILOptimizer/IPO/CapturePropagation.cpp index 2a9f2e9a9b477..8aff807cc96f6 100644 --- a/lib/SILOptimizer/IPO/CapturePropagation.cpp +++ b/lib/SILOptimizer/IPO/CapturePropagation.cpp @@ -262,9 +262,9 @@ SILFunction *CapturePropagation::specializeConstClosure(PartialApplyInst *PAI, SILFunction *NewF = FuncBuilder.createFunction( SILLinkage::Shared, Name, NewFTy, GenericEnv, OrigF->getLocation(), OrigF->isBare(), OrigF->isTransparent(), Serialized, IsNotDynamic, - OrigF->getEntryCount(), OrigF->isThunk(), OrigF->getClassSubclassScope(), - OrigF->getInlineStrategy(), OrigF->getEffectsKind(), - /*InsertBefore*/ OrigF, OrigF->getDebugScope()); + IsNotDistributed, OrigF->getEntryCount(), OrigF->isThunk(), + OrigF->getClassSubclassScope(), OrigF->getInlineStrategy(), + OrigF->getEffectsKind(), /*InsertBefore*/ OrigF, OrigF->getDebugScope()); if (!OrigF->hasOwnership()) { NewF->setOwnershipEliminated(); } diff --git a/lib/SILOptimizer/IPO/ClosureSpecializer.cpp b/lib/SILOptimizer/IPO/ClosureSpecializer.cpp index f270b84c4aaa5..74d767c907893 100644 --- a/lib/SILOptimizer/IPO/ClosureSpecializer.cpp +++ b/lib/SILOptimizer/IPO/ClosureSpecializer.cpp @@ -693,8 +693,8 @@ ClosureSpecCloner::initCloned(SILOptFunctionBuilder &FunctionBuilder, getSpecializedLinkage(ClosureUser, ClosureUser->getLinkage()), ClonedName, ClonedTy, ClosureUser->getGenericEnvironment(), ClosureUser->getLocation(), IsBare, ClosureUser->isTransparent(), - CallSiteDesc.isSerialized(), IsNotDynamic, ClosureUser->getEntryCount(), - ClosureUser->isThunk(), + CallSiteDesc.isSerialized(), IsNotDynamic, IsNotDistributed, + ClosureUser->getEntryCount(), ClosureUser->isThunk(), /*classSubclassScope=*/SubclassScope::NotApplicable, ClosureUser->getInlineStrategy(), ClosureUser->getEffectsKind(), ClosureUser, ClosureUser->getDebugScope()); diff --git a/lib/SILOptimizer/Mandatory/CapturePromotion.cpp b/lib/SILOptimizer/Mandatory/CapturePromotion.cpp index 6ee1af50d6131..5dc665216f172 100644 --- a/lib/SILOptimizer/Mandatory/CapturePromotion.cpp +++ b/lib/SILOptimizer/Mandatory/CapturePromotion.cpp @@ -465,7 +465,7 @@ ClosureCloner::initCloned(SILOptFunctionBuilder &functionBuilder, auto *fn = functionBuilder.createFunction( orig->getLinkage(), clonedName, clonedTy, orig->getGenericEnvironment(), orig->getLocation(), orig->isBare(), IsNotTransparent, serialized, - IsNotDynamic, orig->getEntryCount(), orig->isThunk(), + IsNotDynamic, IsNotDistributed, orig->getEntryCount(), orig->isThunk(), orig->getClassSubclassScope(), orig->getInlineStrategy(), orig->getEffectsKind(), orig, orig->getDebugScope()); for (auto &attr : orig->getSemanticsAttrs()) diff --git a/lib/SILOptimizer/Mandatory/Differentiation.cpp b/lib/SILOptimizer/Mandatory/Differentiation.cpp index 23a57616a87d0..f7f6579f9404a 100644 --- a/lib/SILOptimizer/Mandatory/Differentiation.cpp +++ b/lib/SILOptimizer/Mandatory/Differentiation.cpp @@ -795,7 +795,8 @@ static SILFunction *createEmptyVJP(ADContext &context, witness->getLinkage(), context.getASTContext().getIdentifier(vjpName).str(), vjpType, vjpGenericEnv, original->getLocation(), original->isBare(), - IsNotTransparent, isSerialized, original->isDynamicallyReplaceable()); + IsNotTransparent, isSerialized, original->isDynamicallyReplaceable(), + original->isDistributed()); vjp->setDebugScope(new (module) SILDebugScope(original->getLocation(), vjp)); LLVM_DEBUG(llvm::dbgs() << "VJP type: " << vjp->getLoweredFunctionType() @@ -836,7 +837,8 @@ static SILFunction *createEmptyJVP(ADContext &context, witness->getLinkage(), context.getASTContext().getIdentifier(jvpName).str(), jvpType, jvpGenericEnv, original->getLocation(), original->isBare(), - IsNotTransparent, isSerialized, original->isDynamicallyReplaceable()); + IsNotTransparent, isSerialized, original->isDynamicallyReplaceable(), + original->isDistributed()); jvp->setDebugScope(new (module) SILDebugScope(original->getLocation(), jvp)); LLVM_DEBUG(llvm::dbgs() << "JVP type: " << jvp->getLoweredFunctionType() @@ -870,7 +872,7 @@ static void emitFatalError(ADContext &context, SILFunction *f, auto *fatalErrorFn = fnBuilder.getOrCreateFunction( loc, fatalErrorFuncName, SILLinkage::PublicExternal, fatalErrorFnType, IsNotBare, IsNotTransparent, IsNotSerialized, IsNotDynamic, - ProfileCounter(), IsNotThunk); + IsNotDistributed, ProfileCounter(), IsNotThunk); auto *fatalErrorFnRef = builder.createFunctionRef(loc, fatalErrorFn); builder.createApply(loc, fatalErrorFnRef, SubstitutionMap(), {}); builder.createUnreachable(loc); @@ -1029,7 +1031,8 @@ static SILValue promoteCurryThunkApplicationToDifferentiableFunction( auto *newThunk = fb.getOrCreateFunction( loc, newThunkName, getSpecializedLinkage(thunk, thunk->getLinkage()), thunkType, thunk->isBare(), thunk->isTransparent(), thunk->isSerialized(), - thunk->isDynamicallyReplaceable(), ProfileCounter(), thunk->isThunk()); + thunk->isDynamicallyReplaceable(), thunk->isDistributed(), + ProfileCounter(), thunk->isThunk()); // If new thunk is newly created: clone the old thunk body, wrap the // returned function value with an `differentiable_function` // instruction, and process the `differentiable_function` instruction. diff --git a/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp b/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp index 0a0cff60a4b5e..caf1498c00013 100644 --- a/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp +++ b/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp @@ -718,7 +718,8 @@ SILFunction *PromotedParamCloner::initCloned(SILOptFunctionBuilder &FuncBuilder, swift::getSpecializedLinkage(Orig, Orig->getLinkage()), ClonedName, ClonedTy, Orig->getGenericEnvironment(), Orig->getLocation(), Orig->isBare(), Orig->isTransparent(), Serialized, IsNotDynamic, - Orig->getEntryCount(), Orig->isThunk(), Orig->getClassSubclassScope(), + IsNotDistributed, Orig->getEntryCount(), Orig->isThunk(), + Orig->getClassSubclassScope(), Orig->getInlineStrategy(), Orig->getEffectsKind(), Orig, Orig->getDebugScope()); for (auto &Attr : Orig->getSemanticsAttrs()) { diff --git a/lib/SILOptimizer/Transforms/Outliner.cpp b/lib/SILOptimizer/Transforms/Outliner.cpp index 70106b2997745..daa8aa407f787 100644 --- a/lib/SILOptimizer/Transforms/Outliner.cpp +++ b/lib/SILOptimizer/Transforms/Outliner.cpp @@ -334,7 +334,7 @@ BridgedProperty::outline(SILModule &M) { auto *Fun = FuncBuilder.getOrCreateFunction( ObjCMethod->getLoc(), name, SILLinkage::Shared, FunctionType, IsNotBare, - IsNotTransparent, IsSerializable, IsNotDynamic); + IsNotTransparent, IsSerializable, IsNotDynamic, IsNotDistributed); bool NeedsDefinition = Fun->empty(); if (Release) { @@ -1046,7 +1046,7 @@ ObjCMethodCall::outline(SILModule &M) { auto *Fun = FuncBuilder.getOrCreateFunction( ObjCMethod->getLoc(), name, SILLinkage::Shared, FunctionType, IsNotBare, - IsNotTransparent, IsSerializable, IsNotDynamic); + IsNotTransparent, IsSerializable, IsNotDynamic, IsNotDistributed); bool NeedsDefinition = Fun->empty(); // Call the outlined function. diff --git a/lib/SILOptimizer/UtilityPasses/BugReducerTester.cpp b/lib/SILOptimizer/UtilityPasses/BugReducerTester.cpp index e616d6c9b75fc..fdda8c1ec1fbf 100644 --- a/lib/SILOptimizer/UtilityPasses/BugReducerTester.cpp +++ b/lib/SILOptimizer/UtilityPasses/BugReducerTester.cpp @@ -92,7 +92,7 @@ class BugReducerTester : public SILFunctionTransform { SILFunction *F = FunctionBuilder.getOrCreateSharedFunction( RegularLocation::getAutoGeneratedLocation(), RuntimeCrasherFunctionName, FuncType, IsBare, IsNotTransparent, IsSerialized, ProfileCounter(), - IsNotThunk, IsNotDynamic); + IsNotThunk, IsNotDynamic, IsNotDistributed); if (F->isDefinition()) return F; diff --git a/lib/SILOptimizer/Utils/GenericCloner.cpp b/lib/SILOptimizer/Utils/GenericCloner.cpp index 8513525c8e11c..7a0c8385de5b4 100644 --- a/lib/SILOptimizer/Utils/GenericCloner.cpp +++ b/lib/SILOptimizer/Utils/GenericCloner.cpp @@ -42,8 +42,8 @@ SILFunction *GenericCloner::createDeclaration( getSpecializedLinkage(Orig, Orig->getLinkage()), NewName, ReInfo.getSpecializedType(), ReInfo.getSpecializedGenericEnvironment(), Orig->getLocation(), Orig->isBare(), Orig->isTransparent(), - ReInfo.isSerialized(), IsNotDynamic, Orig->getEntryCount(), - Orig->isThunk(), Orig->getClassSubclassScope(), + ReInfo.isSerialized(), IsNotDynamic, IsNotDistributed, + Orig->getEntryCount(), Orig->isThunk(), Orig->getClassSubclassScope(), Orig->getInlineStrategy(), Orig->getEffectsKind(), Orig, Orig->getDebugScope()); for (auto &Attr : Orig->getSemanticsAttrs()) { diff --git a/lib/SILOptimizer/Utils/Generics.cpp b/lib/SILOptimizer/Utils/Generics.cpp index d22e37fabd2c2..44e0481c1df22 100644 --- a/lib/SILOptimizer/Utils/Generics.cpp +++ b/lib/SILOptimizer/Utils/Generics.cpp @@ -2201,7 +2201,8 @@ class ReabstractionThunkGenerator { SILFunction *ReabstractionThunkGenerator::createThunk() { SILFunction *Thunk = FunctionBuilder.getOrCreateSharedFunction( Loc, ThunkName, ReInfo.getSubstitutedType(), IsBare, IsTransparent, - ReInfo.isSerialized(), ProfileCounter(), IsThunk, IsNotDynamic); + ReInfo.isSerialized(), ProfileCounter(), IsThunk, IsNotDynamic, + IsNotDistributed); // Re-use an existing thunk. if (!Thunk->empty()) return Thunk; diff --git a/lib/Serialization/DeserializeSIL.cpp b/lib/Serialization/DeserializeSIL.cpp index 184b79646f09c..7c61557de8af9 100644 --- a/lib/Serialization/DeserializeSIL.cpp +++ b/lib/Serialization/DeserializeSIL.cpp @@ -529,7 +529,7 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, optimizationMode, perfConstr, subclassScope, hasCReferences, effect, numSpecAttrs, hasQualifiedOwnership, isWeakImported, LIST_VER_TUPLE_PIECES(available), - isDynamic, isExactSelfClass; + isDynamic, isExactSelfClass, isDistributed; ArrayRef SemanticsIDs; SILFunctionLayout::readRecord( scratch, rawLinkage, isTransparent, isSerialized, isThunk, @@ -537,8 +537,8 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, optimizationMode, perfConstr, subclassScope, hasCReferences, effect, numSpecAttrs, hasQualifiedOwnership, isWeakImported, LIST_VER_TUPLE_PIECES(available), - isDynamic, isExactSelfClass, funcTyID, replacedFunctionID, genericSigID, - clangNodeOwnerID, SemanticsIDs); + isDynamic, isExactSelfClass, isDistributed, funcTyID, replacedFunctionID, + genericSigID, clangNodeOwnerID, SemanticsIDs); if (funcTyID == 0) { LLVM_DEBUG(llvm::dbgs() << "SILFunction typeID is 0.\n"); @@ -672,6 +672,7 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, fn->setIsDynamic(IsDynamicallyReplaceable_t(isDynamic)); fn->setIsExactSelfClass(IsExactSelfClass_t(isExactSelfClass)); + fn->setIsDistributed(IsDistributed_t(isDistributed)); if (replacedFunction) fn->setDynamicallyReplacedFunction(replacedFunction); if (!replacedObjectiveCFunc.empty()) @@ -2961,7 +2962,7 @@ bool SILDeserializer::hasSILFunction(StringRef Name, optimizationMode, perfConstr, subclassScope, hasCReferences, effect, numSpecAttrs, hasQualifiedOwnership, isWeakImported, LIST_VER_TUPLE_PIECES(available), - isDynamic, isExactSelfClass; + isDynamic, isExactSelfClass, isDistributed; ArrayRef SemanticsIDs; SILFunctionLayout::readRecord( scratch, rawLinkage, isTransparent, isSerialized, isThunk, @@ -2969,8 +2970,8 @@ bool SILDeserializer::hasSILFunction(StringRef Name, optimizationMode, perfConstr, subclassScope, hasCReferences, effect, numSpecAttrs, hasQualifiedOwnership, isWeakImported, LIST_VER_TUPLE_PIECES(available), - isDynamic, isExactSelfClass, funcTyID, replacedFunctionID, genericSigID, - clangOwnerID, SemanticsIDs); + isDynamic, isExactSelfClass, isDistributed, funcTyID, replacedFunctionID, + genericSigID, clangOwnerID, SemanticsIDs); auto linkage = fromStableSILLinkage(rawLinkage); if (!linkage) { LLVM_DEBUG(llvm::dbgs() << "invalid linkage code " << rawLinkage diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 7ebb3a73c43ad..8742dd2f73d67 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -56,7 +56,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 652; // @main cleanup +const uint16_t SWIFTMODULE_VERSION_MINOR = 653; // `IsDistributed` bit on SILFunction /// A standard hash seed used for all string hashes in a serialized module. /// diff --git a/lib/Serialization/SILFormat.h b/lib/Serialization/SILFormat.h index b446fd91544dc..570843d139518 100644 --- a/lib/Serialization/SILFormat.h +++ b/lib/Serialization/SILFormat.h @@ -293,6 +293,7 @@ namespace sil_block { BC_AVAIL_TUPLE, // availability for weak linking BCFixed<1>, // is dynamically replacable BCFixed<1>, // exact self class + BCFixed<1>, // is distributed TypeIDField, // SILFunctionType DeclIDField, // SILFunction name or 0 (replaced function) GenericSignatureIDField, diff --git a/lib/Serialization/SILSerializationFunctionBuilder.h b/lib/Serialization/SILSerializationFunctionBuilder.h index 8bb8aab4be8e6..1e9e9c351c1f7 100644 --- a/lib/Serialization/SILSerializationFunctionBuilder.h +++ b/lib/Serialization/SILSerializationFunctionBuilder.h @@ -30,8 +30,8 @@ class LLVM_LIBRARY_VISIBILITY SILSerializationFunctionBuilder { return builder.createFunction( SILLinkage::Private, name, type.getAs(), nullptr, loc, IsNotBare, IsNotTransparent, - IsNotSerialized, IsNotDynamic, ProfileCounter(), IsNotThunk, - SubclassScope::NotApplicable); + IsNotSerialized, IsNotDynamic, IsNotDistributed, ProfileCounter(), + IsNotThunk, SubclassScope::NotApplicable); } void setHasOwnership(SILFunction *f, bool newValue) { diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp index 9197739df346a..efedb34d3cd41 100644 --- a/lib/Serialization/SerializeSIL.cpp +++ b/lib/Serialization/SerializeSIL.cpp @@ -490,7 +490,8 @@ void SILSerializer::writeSILFunction(const SILFunction &F, bool DeclOnly) { (unsigned)numSpecAttrs, (unsigned)F.hasOwnership(), F.isAlwaysWeakImported(), LIST_VER_TUPLE_PIECES(available), (unsigned)F.isDynamicallyReplaceable(), (unsigned)F.isExactSelfClass(), - FnID, replacedFunctionID, genericSigID, clangNodeOwnerID, SemanticsIDs); + (unsigned)F.isDistributed(), FnID, replacedFunctionID, genericSigID, + clangNodeOwnerID, SemanticsIDs); if (NoBody) return; diff --git a/stdlib/public/Concurrency/Actor.cpp b/stdlib/public/Concurrency/Actor.cpp index fe43f825f9ba0..adf599995e5d6 100644 --- a/stdlib/public/Concurrency/Actor.cpp +++ b/stdlib/public/Concurrency/Actor.cpp @@ -27,6 +27,7 @@ #include "../CompatibilityOverride/CompatibilityOverride.h" #include "swift/Runtime/Atomic.h" +#include "swift/Runtime/AccessibleFunction.h" #include "swift/Runtime/Casting.h" #include "swift/Runtime/Once.h" #include "swift/Runtime/Mutex.h" @@ -2004,3 +2005,77 @@ bool DefaultActorImpl::isDistributedRemote() { auto state = CurrentState.load(std::memory_order_relaxed); return state.Flags.isDistributedRemote(); } + +static const AccessibleFunctionRecord * +findDistributedAccessor(const char *targetNameStart, size_t targetNameLength) { + if (auto *func = runtime::swift_findAccessibleFunction(targetNameStart, + targetNameLength)) { + assert(func->Flags.isDistributed()); + return func; + } + return nullptr; +} + +/// func _executeDistributedTarget( +/// on: AnyObject, +/// _ targetName: UnsafePointer, +/// _ targetNameLength: UInt, +/// argumentBuffer: Builtin.RawPointer, +/// resultBuffer: Builtin.RawPointer) async throws +using TargetExecutorSignature = + AsyncSignature; + +SWIFT_CC(swiftasync) +SWIFT_RUNTIME_STDLIB_SPI +TargetExecutorSignature::FunctionType swift_distributed_execute_target; + +/// Accessor takes: +/// - an async context +/// - an argument buffer as a raw pointer +/// - a result buffer as a raw pointer +/// - a reference to an actor to execute method on. +using DistributedAccessorSignature = + AsyncSignature; + +SWIFT_CC(swiftasync) +static DistributedAccessorSignature::ContinuationType + swift_distributed_execute_target_resume; + +SWIFT_CC(swiftasync) +static void ::swift_distributed_execute_target_resume( + SWIFT_ASYNC_CONTEXT AsyncContext *context, SWIFT_CONTEXT void *error) { + auto parentCtx = context->Parent; + auto resumeInParent = + reinterpret_cast( + parentCtx->ResumeParent); + swift_task_dealloc(context); + return resumeInParent(parentCtx, error); +} + +SWIFT_CC(swiftasync) +void ::swift_distributed_execute_target( + SWIFT_ASYNC_CONTEXT AsyncContext *callerContext, DefaultActor *actor, + const char *targetNameStart, size_t targetNameLength, void *argumentBuffer, + void *resultBuffer) { + auto *accessor = findDistributedAccessor(targetNameStart, targetNameLength); + + if (!accessor) + return; + + auto *asyncFnPtr = reinterpret_cast< + const AsyncFunctionPointer *>(accessor); + + DistributedAccessorSignature::FunctionType *accessorEntry = + asyncFnPtr->Function.get(); + + AsyncContext *calleeContext = reinterpret_cast( + swift_task_alloc(asyncFnPtr->ExpectedContextSize)); + + calleeContext->Parent = callerContext; + calleeContext->ResumeParent = reinterpret_cast( + swift_distributed_execute_target_resume); + + accessorEntry(calleeContext, argumentBuffer, resultBuffer, actor); +} diff --git a/stdlib/public/SwiftShims/MetadataSections.h b/stdlib/public/SwiftShims/MetadataSections.h index 939b49d969d05..47de4f0ea70bc 100644 --- a/stdlib/public/SwiftShims/MetadataSections.h +++ b/stdlib/public/SwiftShims/MetadataSections.h @@ -64,6 +64,7 @@ struct MetadataSections { MetadataSectionRange swift5_replac2; MetadataSectionRange swift5_builtin; MetadataSectionRange swift5_capture; + MetadataSectionRange swift5_accessible_functions; }; #ifdef __cplusplus diff --git a/stdlib/public/runtime/AccessibleFunction.cpp b/stdlib/public/runtime/AccessibleFunction.cpp new file mode 100644 index 0000000000000..16569337f6ae3 --- /dev/null +++ b/stdlib/public/runtime/AccessibleFunction.cpp @@ -0,0 +1,159 @@ +//===---- AccessibleFunction.cpp - Swift protocol conformance checking ----===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// +// +// Checking and caching of Swift accessible functions. +// +//===----------------------------------------------------------------------===// + +#include "ImageInspection.h" +#include "Private.h" +#include "swift/Basic/Lazy.h" +#include "swift/Demangling/Demangler.h" +#include "swift/Runtime/AccessibleFunction.h" +#include "swift/Runtime/Concurrent.h" +#include "swift/Runtime/Metadata.h" + +#include + +using namespace swift; + +#pragma mark Accessible function cache +namespace { + +struct AccessibleFunctionsSection { + const AccessibleFunctionRecord *Begin, *End; + + AccessibleFunctionsSection(const AccessibleFunctionRecord *begin, + const AccessibleFunctionRecord *end) + : Begin(begin), End(end) {} + + AccessibleFunctionsSection(const void *ptr, uintptr_t size) { + auto bytes = reinterpret_cast(ptr); + Begin = reinterpret_cast(ptr); + End = reinterpret_cast(bytes + size); + } + + const AccessibleFunctionRecord *begin() const { return Begin; } + const AccessibleFunctionRecord *end() const { return End; } +}; + +struct AccessibleFunctionCacheEntry { +private: + const char *Name; + size_t NameLength; + + const AccessibleFunctionRecord *Func; + +public: + AccessibleFunctionCacheEntry(llvm::StringRef name, + const AccessibleFunctionRecord *func) + : Func(func) { + char *Name = reinterpret_cast(malloc(name.size())); + memcpy(Name, name.data(), name.size()); + + this->Name = Name; + this->NameLength = name.size(); + } + + const AccessibleFunctionRecord *getFunction() const { return Func; } + + bool matchesKey(llvm::StringRef name) { + return name == llvm::StringRef{Name, NameLength}; + } + + friend llvm::hash_code hash_value(const AccessibleFunctionCacheEntry &value) { + return hash_value(llvm::StringRef{value.Name, value.NameLength}); + } + + template + static size_t getExtraAllocationSize(T &&...ignored) { + return 0; + } +}; + +struct AccessibleFunctionsState { + ConcurrentReadableHashMap Cache; + ConcurrentReadableArray SectionsToScan; + + AccessibleFunctionsState() { + initializeAccessibleFunctionsLookup(); + } +}; + +static Lazy Functions; + +} // end anonymous namespace + +static void _registerAccessibleFunctions(AccessibleFunctionsState &C, + AccessibleFunctionsSection section) { + C.SectionsToScan.push_back(section); +} + +void swift::addImageAccessibleFunctionsBlockCallbackUnsafe(const void *functions, + uintptr_t size) { + assert( + size % sizeof(AccessibleFunctionRecord) == 0 && + "accessible function section not a multiple of AccessibleFunctionRecord"); + + auto &C = Functions.unsafeGetAlreadyInitialized(); + _registerAccessibleFunctions(C, AccessibleFunctionsSection{functions, size}); +} + +void swift::addImageAccessibleFunctionsBlockCallback(const void *functions, + uintptr_t size) { + Functions.get(); + addImageAccessibleFunctionsBlockCallbackUnsafe(functions, size); +} + +static const AccessibleFunctionRecord * +_searchForFunctionRecord(AccessibleFunctionsState &S, llvm::StringRef name) { + for (const auto §ion : S.SectionsToScan.snapshot()) { + for (auto &record : section) { + auto recordName = + swift::Demangle::makeSymbolicMangledNameStringRef(record.Name.get()); + if (recordName == name) + return &record; + } + } + return nullptr; +} + +SWIFT_RUNTIME_STDLIB_SPI +const AccessibleFunctionRecord * +swift::runtime::swift_findAccessibleFunction(const char *targetNameStart, + size_t targetNameLength) { + auto &S = Functions.get(); + + llvm::StringRef name{targetNameStart, targetNameLength}; + + // Look for an existing entry. + { + auto snapshot = S.Cache.snapshot(); + if (auto E = snapshot.find(name)) + return E->getFunction(); + } + + // If entry doesn't exist (either record doesn't exist, hasn't been loaded, or + // requested yet), let's try to find it and add to the cache. + + auto *function = _searchForFunctionRecord(S, name); + if (function) { + S.Cache.getOrInsert( + name, [&](AccessibleFunctionCacheEntry *entry, bool created) { + if (created) + new (entry) AccessibleFunctionCacheEntry{name, function}; + return true; + }); + } + + return function; +} diff --git a/stdlib/public/runtime/CMakeLists.txt b/stdlib/public/runtime/CMakeLists.txt index 77ca35cac3431..5a5216f3135dc 100644 --- a/stdlib/public/runtime/CMakeLists.txt +++ b/stdlib/public/runtime/CMakeLists.txt @@ -68,7 +68,8 @@ set(swift_runtime_sources ReflectionMirror.cpp RuntimeInvocationsTracking.cpp SwiftDtoa.cpp - SwiftTLSContext.cpp) + SwiftTLSContext.cpp + AccessibleFunction.cpp) # Acknowledge that the following sources are known. set(LLVM_OPTIONAL_SOURCES diff --git a/stdlib/public/runtime/ImageInspection.h b/stdlib/public/runtime/ImageInspection.h index b205b7c211dc8..159405246ef1b 100644 --- a/stdlib/public/runtime/ImageInspection.h +++ b/stdlib/public/runtime/ImageInspection.h @@ -73,6 +73,9 @@ void initializeTypeMetadataRecordLookup(); /// Load the metadata from the image necessary to perform dynamic replacements. void initializeDynamicReplacementLookup(); +/// Load the metadata from the image necessary to find functions by name. +void initializeAccessibleFunctionsLookup(); + // Callbacks to register metadata from an image to the runtime. void addImageProtocolsBlockCallback(const void *start, uintptr_t size); void addImageProtocolsBlockCallbackUnsafe(const void *start, uintptr_t size); @@ -87,6 +90,10 @@ void addImageTypeMetadataRecordBlockCallbackUnsafe(const void *start, void addImageDynamicReplacementBlockCallback(const void *start, uintptr_t size, const void *start2, uintptr_t size2); +void addImageAccessibleFunctionsBlockCallback(const void *start, + uintptr_t size); +void addImageAccessibleFunctionsBlockCallbackUnsafe(const void *start, + uintptr_t size); int lookupSymbol(const void *address, SymbolInfo *info); diff --git a/stdlib/public/runtime/ImageInspectionCommon.cpp b/stdlib/public/runtime/ImageInspectionCommon.cpp index c70e886c06806..34e3eab5b2b91 100644 --- a/stdlib/public/runtime/ImageInspectionCommon.cpp +++ b/stdlib/public/runtime/ImageInspectionCommon.cpp @@ -81,6 +81,13 @@ void swift_addNewDSOImage(const void *addr) { replacements, dynamic_replacements.length, replacements_some, dynamic_replacements_some.length); } + + const auto &accessible_funcs_section = sections->swift5_accessible_functions; + const void *functions = + reinterpret_cast(accessible_funcs_section.start); + if (accessible_funcs_section.length) + swift::addImageAccessibleFunctionsBlockCallback( + functions, accessible_funcs_section.length); } void swift::initializeProtocolLookup() { @@ -131,6 +138,21 @@ void swift::initializeTypeMetadataRecordLookup() { void swift::initializeDynamicReplacementLookup() { } +void swift::initializeAccessibleFunctionsLookup() { + const swift::MetadataSections *sections = registered; + while (true) { + const swift::MetadataSectionRange &functions = + sections->swift5_accessible_functions; + if (functions.length) + addImageAccessibleFunctionsBlockCallbackUnsafe( + reinterpret_cast(functions.start), functions.length); + + if (sections->next == registered) + break; + sections = sections->next; + } +} + #ifndef NDEBUG SWIFT_RUNTIME_EXPORT @@ -177,4 +199,4 @@ size_t swift_getMetadataSectionCount() { #endif // !defined(__MACH__) -#endif // SWIFT_RUNTIME_IMAGEINSPECTIONCOMMON_H \ No newline at end of file +#endif // SWIFT_RUNTIME_IMAGEINSPECTIONCOMMON_H diff --git a/stdlib/public/runtime/ImageInspectionCommon.h b/stdlib/public/runtime/ImageInspectionCommon.h index 56f1ea9774fd3..d5638d059ce3f 100644 --- a/stdlib/public/runtime/ImageInspectionCommon.h +++ b/stdlib/public/runtime/ImageInspectionCommon.h @@ -34,6 +34,9 @@ /// This lives within SEG_TEXT. #define MachODynamicReplacementSection "__swift5_replace" #define MachODynamicReplacementSomeSection "__swift5_replac2" +/// The Mach-O section name for the section containing accessible functions. +/// This lives within SEG_TEXT. +#define MachOAccessibleFunctionsSection "__swift5_acfuncs" #define MachOTextSegment "__TEXT" diff --git a/stdlib/public/runtime/ImageInspectionMachO.cpp b/stdlib/public/runtime/ImageInspectionMachO.cpp index b39816492828d..47b72ca71a665 100644 --- a/stdlib/public/runtime/ImageInspectionMachO.cpp +++ b/stdlib/public/runtime/ImageInspectionMachO.cpp @@ -43,6 +43,8 @@ constexpr const char DynamicReplacementSection[] = MachODynamicReplacementSection; constexpr const char DynamicReplacementSomeSection[] = MachODynamicReplacementSomeSection; +constexpr const char AccessibleFunctionsSection[] = + MachOAccessibleFunctionsSection; constexpr const char TextSegment[] = MachOTextSegment; #if __POINTER_WIDTH__ == 64 @@ -159,6 +161,12 @@ void swift::initializeDynamicReplacementLookup() { addImageDynamicReplacementBlockCallback>); } +void swift::initializeAccessibleFunctionsLookup() { + REGISTER_FUNC( + addImageCallback); +} + #if SWIFT_STDLIB_HAS_DLADDR int swift::lookupSymbol(const void *address, SymbolInfo *info) { Dl_info dlinfo; diff --git a/stdlib/public/runtime/ImageInspectionStatic.cpp b/stdlib/public/runtime/ImageInspectionStatic.cpp index 33181fad4a5b2..2fb1bfdeee217 100644 --- a/stdlib/public/runtime/ImageInspectionStatic.cpp +++ b/stdlib/public/runtime/ImageInspectionStatic.cpp @@ -74,5 +74,14 @@ void swift::initializeDynamicReplacementLookup() { return; addImageDynamicReplacementBlockCallback(start1, size1, start2, size2); } +void swift::initializeAccessibleFunctionsLookup() { + void *start; + uintptr_t size; + GET_SECTION_START_AND_SIZE(start, size, MachOTextSegment, + MachOAccessibleFunctionsSection); + if (start == nullptr || size == 0) + return; + addImageAccessibleFunctionBlockCallbackUnsafe(start, size); +} #endif // defined(__MACH__) && defined(SWIFT_RUNTIME_STATIC_IMAGE_INSPECTION) diff --git a/test/Demangle/Inputs/manglings.txt b/test/Demangle/Inputs/manglings.txt index 17001295459e5..8a6c306ef6653 100644 --- a/test/Demangle/Inputs/manglings.txt +++ b/test/Demangle/Inputs/manglings.txt @@ -421,3 +421,5 @@ $s13__lldb_expr_110$10016c2d8yXZ1B10$10016c2e0LLC ---> __lldb_expr_1.(unknown co $s__TJO ---> $s__TJO $s6Foobar7Vector2VAASdRszlE10simdMatrix5scale6rotate9translateSo0C10_double3x3aACySdG_SdAJtFZ0D4TypeL_aySd__GD ---> MatrixType #1 in static (extension in Foobar):Foobar.Vector2.simdMatrix(scale: Foobar.Vector2, rotate: Swift.Double, translate: Foobar.Vector2) -> __C.simd_double3x3 $s17distributed_thunk2DAC1fyyFTE ---> distributed thunk for distributed_thunk.DA.f() -> () +$s16distributed_test1XC7computeyS2iFTF ---> distributed method accessor for distributed_test.X.compute(Swift.Int) -> Swift.Int +$s27distributed_actor_accessors7MyActorC7simple2ySSSiFTETFHF ---> accessible function runtime record for distributed method accessor for distributed thunk for distributed_actor_accessors.MyActor.simple2(Swift.Int) -> Swift.String diff --git a/test/Distributed/distributed_actor_accessor_section_coff.swift b/test/Distributed/distributed_actor_accessor_section_coff.swift new file mode 100644 index 0000000000000..6306b5f47633d --- /dev/null +++ b/test/Distributed/distributed_actor_accessor_section_coff.swift @@ -0,0 +1,140 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend-emit-module -emit-module-path %t/FakeDistributedActorSystems.swiftmodule -module-name FakeDistributedActorSystems -disable-availability-checking %S/Inputs/FakeDistributedActorSystems.swift +// RUN: %target-swift-frontend -emit-irgen -module-name distributed_actor_accessors -enable-experimental-distributed -disable-availability-checking -I %t 2>&1 %s | %IRGenFileCheck %s + +// UNSUPPORTED: back_deploy_concurrency +// REQUIRES: concurrency +// REQUIRES: distributed + +// REQUIRES: OS=windows-msvc +// FIXME: Test is temporary disabled (no way to debug) +// REQUIRES: fix + +import _Distributed +import FakeDistributedActorSystems + +@available(SwiftStdlib 5.5, *) +typealias DefaultDistributedActorSystem = FakeActorSystem + +enum SimpleE : Codable { +case a +} + +enum E : Codable { +case a, b, c +} + +enum IndirectE : Codable { + case empty + indirect case test(_: Int) +} + +final class Obj : Codable, Sendable { + let x: Int + + init(x: Int) { + self.x = x + } +} + +struct LargeStruct : Codable { + var a: Int + var b: Int + var c: String + var d: Double +} + +@available(SwiftStdlib 5.6, *) +public distributed actor MyActor { + distributed func simple1(_: Int) { + } + + // `String` would be a direct result as a struct type + distributed func simple2(_: Int) -> String { + return "" + } + + // `String` is an object that gets exploded into two parameters + distributed func simple3(_: String) -> Int { + return 42 + } + + // Enum with a single case are special because they have an empty + // native schema so they are dropped from parameters/result. + distributed func single_case_enum(_ e: SimpleE) -> SimpleE { + return e + } + + distributed func with_indirect_enums(_: IndirectE, _: Int) -> IndirectE { + return .empty + } + + // Combination of multiple arguments, reference type and indirect result + // + // Note: Tuple types cannot be used here is either position because they + // cannot conform to protocols. + distributed func complex(_: [Int], _: Obj, _: String?, _: LargeStruct) -> LargeStruct { + fatalError() + } +} + +@available(SwiftStdlib 5.6, *) +public distributed actor MyOtherActor { + distributed func empty() { + } +} + + +/// ---> Let's check that distributed accessors and thunks are emitted as accessible functions + +/// -> `MyActor.simple1` +// CHECK: @"$s27distributed_actor_accessors7MyActorC7simple1yySiFTEHF" = private constant +// CHECK-SAME: @"symbolic Si___________pIetMHygzo_ 27distributed_actor_accessors7MyActorC s5ErrorP" +// CHECK-SAME: (%swift.async_func_pointer* @"$s27distributed_actor_accessors7MyActorC7simple1yySiFTETFTu" to i64) +// CHECK-SAME: , section ".sw5acfn$B", {{.*}} + +/// -> `MyActor.simple2` +// CHECK: @"$s27distributed_actor_accessors7MyActorC7simple2ySSSiFTEHF" = private constant +// CHECK-SAME: @"symbolic Si_____SS______pIetMHygozo_ 27distributed_actor_accessors7MyActorC s5ErrorP" +// CHECK-SAME: (%swift.async_func_pointer* @"$s27distributed_actor_accessors7MyActorC7simple2ySSSiFTETFTu" to i64 +// CHECK-SAME: , section ".sw5acfn$B", {{.*}} + +/// -> `MyActor.simple3` +// CHECK: @"$s27distributed_actor_accessors7MyActorC7simple3ySiSSFTEHF" = private constant +// CHECK-SAME: @"symbolic SS_____Si______pIetMHggdzo_ 27distributed_actor_accessors7MyActorC s5ErrorP" +// CHECK-SAME: (%swift.async_func_pointer* @"$s27distributed_actor_accessors7MyActorC7simple3ySiSSFTETFTu" to i64) +// CHECK-SAME: , section ".sw5acfn$B", {{.*}} + +/// -> `MyActor.single_case_enum` +// CHECK: @"$s27distributed_actor_accessors7MyActorC16single_case_enumyAA7SimpleEOAFFTEHF" = private constant +// CHECK-SAME: @"symbolic __________AA______pIetMHygdzo_ 27distributed_actor_accessors7SimpleEO AA7MyActorC s5ErrorP" +// CHECK-SAME: (%swift.async_func_pointer* @"$s27distributed_actor_accessors7MyActorC16single_case_enumyAA7SimpleEOAFFTETFTu" to i64) +// CHECK-SAME: , section ".sw5acfn$B", {{.*}} + +/// -> `MyActor.with_indirect_enums` +// CHECK: @"$s27distributed_actor_accessors7MyActorC19with_indirect_enumsyAA9IndirectEOAF_SitFTEHF" = private constant +// CHECK-SAME: @"symbolic _____Si_____AA______pIetMHgygozo_ 27distributed_actor_accessors9IndirectEO AA7MyActorC s5ErrorP" +// CHECK-SAME: (%swift.async_func_pointer* @"$s27distributed_actor_accessors7MyActorC19with_indirect_enumsyAA9IndirectEOAF_SitFTETFTu" to i64 +// CHECK-SAME: , section ".sw5acfn$B", {{.*}} + +/// -> `MyActor.complex` +// CHECK: @"$s27distributed_actor_accessors7MyActorC7complexyAA11LargeStructVSaySiG_AA3ObjCSSSgAFtFTEHF" = private constant +// CHECK-SAME: @"symbolic SaySiG_____SSSg__________AD______pIetMHgggngrzo_ 27distributed_actor_accessors3ObjC AA11LargeStructV AA7MyActorC s5ErrorP" +// CHECK-SAME: (%swift.async_func_pointer* @"$s27distributed_actor_accessors7MyActorC7complexyAA11LargeStructVSaySiG_AA3ObjCSSSgAFtFTETFTu" to i64) +// CHECK-SAME: , section ".sw5acfn$B", {{.*}} + +/// -> `MyOtherActor.empty` +// CHECK: @"$s27distributed_actor_accessors12MyOtherActorC5emptyyyFTEHF" = private constant +// CHECK-SAME: @"symbolic ___________pIetMHgzo_ 27distributed_actor_accessors12MyOtherActorC s5ErrorP" +// CHECK-SAME: (%swift.async_func_pointer* @"$s27distributed_actor_accessors12MyOtherActorC5emptyyyFTETFTu" to i64) +// CHECK-SAME: , section ".sw5acfn$B", {{.*}} + +// CHECK: @llvm.used = appending global [{{.*}} x i8*] [ +// CHECK-SAME: @"$s27distributed_actor_accessors7MyActorC7simple1yySiFTEHF" +// CHECK-SAME: @"$s27distributed_actor_accessors7MyActorC7simple2ySSSiFTEHF" +// CHECK-SAME: @"$s27distributed_actor_accessors7MyActorC7simple3ySiSSFTEHF" +// CHECK-SAME: @"$s27distributed_actor_accessors7MyActorC16single_case_enumyAA7SimpleEOAFFTEHF" +// CHECK-SAME: @"$s27distributed_actor_accessors7MyActorC19with_indirect_enumsyAA9IndirectEOAF_SitFTEHF" +// CHECK-SAME: @"$s27distributed_actor_accessors7MyActorC7complexyAA11LargeStructVSaySiG_AA3ObjCSSSgAFtFTEHF" +// CHECK-SAME: @"$s27distributed_actor_accessors12MyOtherActorC5emptyyyFTEHF" +// CHECK-SAME: ], section "llvm.metadata" diff --git a/test/Distributed/distributed_actor_accessor_section_elf.swift b/test/Distributed/distributed_actor_accessor_section_elf.swift new file mode 100644 index 0000000000000..f2253209671e6 --- /dev/null +++ b/test/Distributed/distributed_actor_accessor_section_elf.swift @@ -0,0 +1,138 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend-emit-module -emit-module-path %t/FakeDistributedActorSystems.swiftmodule -module-name FakeDistributedActorSystems -disable-availability-checking %S/Inputs/FakeDistributedActorSystems.swift +// RUN: %target-swift-frontend -emit-irgen -module-name distributed_actor_accessors -enable-experimental-distributed -disable-availability-checking -I %t 2>&1 %s | %IRGenFileCheck %s + +// UNSUPPORTED: back_deploy_concurrency +// REQUIRES: concurrency +// REQUIRES: distributed + +// REQUIRES: OS=linux-gnu + +import _Distributed +import FakeDistributedActorSystems + +@available(SwiftStdlib 5.5, *) +typealias DefaultDistributedActorSystem = FakeActorSystem + +enum SimpleE : Codable { +case a +} + +enum E : Codable { +case a, b, c +} + +enum IndirectE : Codable { + case empty + indirect case test(_: Int) +} + +final class Obj : Codable, Sendable { + let x: Int + + init(x: Int) { + self.x = x + } +} + +struct LargeStruct : Codable { + var a: Int + var b: Int + var c: String + var d: Double +} + +@available(SwiftStdlib 5.6, *) +public distributed actor MyActor { + distributed func simple1(_: Int) { + } + + // `String` would be a direct result as a struct type + distributed func simple2(_: Int) -> String { + return "" + } + + // `String` is an object that gets exploded into two parameters + distributed func simple3(_: String) -> Int { + return 42 + } + + // Enum with a single case are special because they have an empty + // native schema so they are dropped from parameters/result. + distributed func single_case_enum(_ e: SimpleE) -> SimpleE { + return e + } + + distributed func with_indirect_enums(_: IndirectE, _: Int) -> IndirectE { + return .empty + } + + // Combination of multiple arguments, reference type and indirect result + // + // Note: Tuple types cannot be used here is either position because they + // cannot conform to protocols. + distributed func complex(_: [Int], _: Obj, _: String?, _: LargeStruct) -> LargeStruct { + fatalError() + } +} + +@available(SwiftStdlib 5.6, *) +public distributed actor MyOtherActor { + distributed func empty() { + } +} + + +/// ---> Let's check that distributed accessors and thunks are emitted as accessible functions + +/// -> `MyActor.simple1` +// CHECK: @"$s27distributed_actor_accessors7MyActorC7simple1yySiFTEHF" = private constant +// CHECK-SAME: @"symbolic Si___________pIetMHygzo_ 27distributed_actor_accessors7MyActorC s5ErrorP" +// CHECK-SAME: (%swift.async_func_pointer* @"$s27distributed_actor_accessors7MyActorC7simple1yySiFTETFTu" to i64) +// CHECK-SAME: , section "swift5_accessible_functions", {{.*}} + +/// -> `MyActor.simple2` +// CHECK: @"$s27distributed_actor_accessors7MyActorC7simple2ySSSiFTEHF" = private constant +// CHECK-SAME: @"symbolic Si_____SS______pIetMHygozo_ 27distributed_actor_accessors7MyActorC s5ErrorP" +// CHECK-SAME: (%swift.async_func_pointer* @"$s27distributed_actor_accessors7MyActorC7simple2ySSSiFTETFTu" to i64) +// CHECK-SAME: , section "swift5_accessible_functions", {{.*}} + +/// -> `MyActor.simple3` +// CHECK: @"$s27distributed_actor_accessors7MyActorC7simple3ySiSSFTEHF" = private constant +// CHECK-SAME: @"symbolic SS_____Si______pIetMHggdzo_ 27distributed_actor_accessors7MyActorC s5ErrorP" +// CHECK-SAME: (%swift.async_func_pointer* @"$s27distributed_actor_accessors7MyActorC7simple3ySiSSFTETFTu" to i64) +// CHECK-SAME: , section "swift5_accessible_functions", {{.*}} + +/// -> `MyActor.single_case_enum` +// CHECK: @"$s27distributed_actor_accessors7MyActorC16single_case_enumyAA7SimpleEOAFFTEHF" = private constant +// CHECK-SAME: @"symbolic __________AA______pIetMHygdzo_ 27distributed_actor_accessors7SimpleEO AA7MyActorC s5ErrorP" +// CHECK-SAME: (%swift.async_func_pointer* @"$s27distributed_actor_accessors7MyActorC16single_case_enumyAA7SimpleEOAFFTETFTu" to i64) +// CHECK-SAME: , section "swift5_accessible_functions", {{.*}} + +/// -> `MyActor.with_indirect_enums` +// CHECK: @"$s27distributed_actor_accessors7MyActorC19with_indirect_enumsyAA9IndirectEOAF_SitFTEHF" = private constant +// CHECK-SAME: @"symbolic _____Si_____AA______pIetMHgygozo_ 27distributed_actor_accessors9IndirectEO AA7MyActorC s5ErrorP" +// CHECK-SAME: (%swift.async_func_pointer* @"$s27distributed_actor_accessors7MyActorC19with_indirect_enumsyAA9IndirectEOAF_SitFTETFTu" to i64) +// CHECK-SAME: , section "swift5_accessible_functions", {{.*}} + +/// -> `MyActor.complex` +// CHECK: @"$s27distributed_actor_accessors7MyActorC7complexyAA11LargeStructVSaySiG_AA3ObjCSSSgAFtFTEHF" = private constant +// CHECK-SAME: @"symbolic SaySiG_____SSSg__________AD______pIetMHgggngrzo_ 27distributed_actor_accessors3ObjC AA11LargeStructV AA7MyActorC s5ErrorP" +// CHECK-SAME: (%swift.async_func_pointer* @"$s27distributed_actor_accessors7MyActorC7complexyAA11LargeStructVSaySiG_AA3ObjCSSSgAFtFTETFTu" to i64) +// CHECK-SAME: , section "swift5_accessible_functions", {{.*}} + +/// -> `MyOtherActor.empty` +// CHECK: @"$s27distributed_actor_accessors12MyOtherActorC5emptyyyFTEHF" = private constant +// CHECK-SAME: @"symbolic ___________pIetMHgzo_ 27distributed_actor_accessors12MyOtherActorC s5ErrorP" +// CHECK-SAME: (%swift.async_func_pointer* @"$s27distributed_actor_accessors12MyOtherActorC5emptyyyFTETFTu" to i64) +// CHECK-SAME: , section "swift5_accessible_functions", {{.*}} + +// CHECK: @llvm.compiler.used = appending global [{{.*}} x i8*] [ +// CHECK-SAME: @"$s27distributed_actor_accessors7MyActorC7simple1yySiFTEHF" +// CHECK-SAME: @"$s27distributed_actor_accessors7MyActorC7simple2ySSSiFTEHF" +// CHECK-SAME: @"$s27distributed_actor_accessors7MyActorC7simple3ySiSSFTEHF" +// CHECK-SAME: @"$s27distributed_actor_accessors7MyActorC16single_case_enumyAA7SimpleEOAFFTEHF" +// CHECK-SAME: @"$s27distributed_actor_accessors7MyActorC19with_indirect_enumsyAA9IndirectEOAF_SitFTEHF" +// CHECK-SAME: @"$s27distributed_actor_accessors7MyActorC7complexyAA11LargeStructVSaySiG_AA3ObjCSSSgAFtFTEHF" +// CHECK-SAME: @"$s27distributed_actor_accessors12MyOtherActorC5emptyyyFTEHF" +// CHECK-SAME: ], section "llvm.metadata" diff --git a/test/Distributed/distributed_actor_accessor_section_macho.swift b/test/Distributed/distributed_actor_accessor_section_macho.swift new file mode 100644 index 0000000000000..b9e718976a7e7 --- /dev/null +++ b/test/Distributed/distributed_actor_accessor_section_macho.swift @@ -0,0 +1,139 @@ + +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend-emit-module -emit-module-path %t/FakeDistributedActorSystems.swiftmodule -module-name FakeDistributedActorSystems -disable-availability-checking %S/Inputs/FakeDistributedActorSystems.swift +// RUN: %target-swift-frontend -emit-irgen -module-name distributed_actor_accessors -enable-experimental-distributed -disable-availability-checking -I %t 2>&1 %s | %IRGenFileCheck %s + +// UNSUPPORTED: back_deploy_concurrency +// REQUIRES: concurrency +// REQUIRES: distributed + +// REQUIRES: VENDOR=apple + +import _Distributed +import FakeDistributedActorSystems + +@available(SwiftStdlib 5.5, *) +typealias DefaultDistributedActorSystem = FakeActorSystem + +enum SimpleE : Codable { +case a +} + +enum E : Codable { +case a, b, c +} + +enum IndirectE : Codable { + case empty + indirect case test(_: Int) +} + +final class Obj : Codable, Sendable { + let x: Int + + init(x: Int) { + self.x = x + } +} + +struct LargeStruct : Codable { + var a: Int + var b: Int + var c: String + var d: Double +} + +@available(SwiftStdlib 5.6, *) +public distributed actor MyActor { + distributed func simple1(_: Int) { + } + + // `String` would be a direct result as a struct type + distributed func simple2(_: Int) -> String { + return "" + } + + // `String` is an object that gets exploded into two parameters + distributed func simple3(_: String) -> Int { + return 42 + } + + // Enum with a single case are special because they have an empty + // native schema so they are dropped from parameters/result. + distributed func single_case_enum(_ e: SimpleE) -> SimpleE { + return e + } + + distributed func with_indirect_enums(_: IndirectE, _: Int) -> IndirectE { + return .empty + } + + // Combination of multiple arguments, reference type and indirect result + // + // Note: Tuple types cannot be used here is either position because they + // cannot conform to protocols. + distributed func complex(_: [Int], _: Obj, _: String?, _: LargeStruct) -> LargeStruct { + fatalError() + } +} + +@available(SwiftStdlib 5.6, *) +public distributed actor MyOtherActor { + distributed func empty() { + } +} + + +/// ---> Let's check that distributed accessors and thunks are emitted as accessible functions + +/// -> `MyActor.simple1` +// CHECK: @"$s27distributed_actor_accessors7MyActorC7simple1yySiFTEHF" = private constant +// CHECK-SAME: @"symbolic Si___________pIetMHygzo_ 27distributed_actor_accessors7MyActorC s5ErrorP" +// CHECK-SAME: (%swift.async_func_pointer* @"$s27distributed_actor_accessors7MyActorC7simple1yySiFTETFTu" to i{{32|64}}) +// CHECK-SAME: , section {{"swift5_accessible_functions"|".sw5acfn$B"|"__TEXT, __swift5_acfuncs, regular"}} + +/// -> `MyActor.simple2` +// CHECK: @"$s27distributed_actor_accessors7MyActorC7simple2ySSSiFTEHF" = private constant +// CHECK-SAME: @"symbolic Si_____SS______pIetMHygozo_ 27distributed_actor_accessors7MyActorC s5ErrorP" +// CHECK-SAME: (%swift.async_func_pointer* @"$s27distributed_actor_accessors7MyActorC7simple2ySSSiFTETFTu" to i{{32|64}}) +// CHECK-SAME: , section {{"swift5_accessible_functions"|".sw5acfn$B"|"__TEXT, __swift5_acfuncs, regular"}} + +/// -> `MyActor.simple3` +// CHECK: @"$s27distributed_actor_accessors7MyActorC7simple3ySiSSFTEHF" = private constant +// CHECK-SAME: @"symbolic SS_____Si______pIetMHggdzo_ 27distributed_actor_accessors7MyActorC s5ErrorP" +// CHECK-SAME: (%swift.async_func_pointer* @"$s27distributed_actor_accessors7MyActorC7simple3ySiSSFTETFTu" to i{{32|64}}) +// CHECK-SAME: , section {{"swift5_accessible_functions"|".sw5acfn$B"|"__TEXT, __swift5_acfuncs, regular"}} + +/// -> `MyActor.single_case_enum` +// CHECK: @"$s27distributed_actor_accessors7MyActorC16single_case_enumyAA7SimpleEOAFFTEHF" = private constant +// CHECK-SAME: @"symbolic __________AA______pIetMHygdzo_ 27distributed_actor_accessors7SimpleEO AA7MyActorC s5ErrorP" +// CHECK-SAME: (%swift.async_func_pointer* @"$s27distributed_actor_accessors7MyActorC16single_case_enumyAA7SimpleEOAFFTETFTu" to i{{32|64}}) +// CHECK-SAME: , section {{"swift5_accessible_functions"|".sw5acfn$B"|"__TEXT, __swift5_acfuncs, regular"}} + +/// -> `MyActor.with_indirect_enums` +// CHECK: @"$s27distributed_actor_accessors7MyActorC19with_indirect_enumsyAA9IndirectEOAF_SitFTEHF" = private constant +// CHECK-SAME: @"symbolic _____Si_____AA______pIetMHgygozo_ 27distributed_actor_accessors9IndirectEO AA7MyActorC s5ErrorP" +// CHECK-SAME: (%swift.async_func_pointer* @"$s27distributed_actor_accessors7MyActorC19with_indirect_enumsyAA9IndirectEOAF_SitFTETFTu" to i{{32|64}}) +// CHECK-SAME: , section {{"swift5_accessible_functions"|".sw5acfn$B"|"__TEXT, __swift5_acfuncs, regular"}} + +/// -> `MyActor.complex` +// CHECK: @"$s27distributed_actor_accessors7MyActorC7complexyAA11LargeStructVSaySiG_AA3ObjCSSSgAFtFTEHF" = private constant +// CHECK-SAME: @"symbolic SaySiG_____SSSg__________AD______pIetMHgggngrzo_ 27distributed_actor_accessors3ObjC AA11LargeStructV AA7MyActorC s5ErrorP" +// CHECK-SAME: (%swift.async_func_pointer* @"$s27distributed_actor_accessors7MyActorC7complexyAA11LargeStructVSaySiG_AA3ObjCSSSgAFtFTETFTu" to i{{32|64}}) +// CHECK-SAME: , section {{"swift5_accessible_functions"|".sw5acfn$B"|"__TEXT, __swift5_acfuncs, regular"}} + +/// -> `MyOtherActor.empty` +// CHECK: @"$s27distributed_actor_accessors12MyOtherActorC5emptyyyFTEHF" = private constant +// CHECK-SAME: @"symbolic ___________pIetMHgzo_ 27distributed_actor_accessors12MyOtherActorC s5ErrorP" +// CHECK-SAME: (%swift.async_func_pointer* @"$s27distributed_actor_accessors12MyOtherActorC5emptyyyFTETFTu" to i{{32|64}}) +// CHECK-SAME: , section {{"swift5_accessible_functions"|".sw5acfn$B"|"__TEXT, __swift5_acfuncs, regular"}} + +// CHECK: @llvm.used = appending global [{{.*}} x i8*] [ +// CHECK-SAME: @"$s27distributed_actor_accessors7MyActorC7simple1yySiFTEHF" +// CHECK-SAME: @"$s27distributed_actor_accessors7MyActorC7simple2ySSSiFTEHF" +// CHECK-SAME: @"$s27distributed_actor_accessors7MyActorC7simple3ySiSSFTEHF" +// CHECK-SAME: @"$s27distributed_actor_accessors7MyActorC16single_case_enumyAA7SimpleEOAFFTEHF" +// CHECK-SAME: @"$s27distributed_actor_accessors7MyActorC19with_indirect_enumsyAA9IndirectEOAF_SitFTEHF" +// CHECK-SAME: @"$s27distributed_actor_accessors7MyActorC7complexyAA11LargeStructVSaySiG_AA3ObjCSSSgAFtFTEHF" +// CHECK-SAME: @"$s27distributed_actor_accessors12MyOtherActorC5emptyyyFTEHF" +// CHECK-SAME: ], section "llvm.metadata" diff --git a/test/Distributed/distributed_actor_accessor_thunks_32bit.swift b/test/Distributed/distributed_actor_accessor_thunks_32bit.swift new file mode 100644 index 0000000000000..c37f1d63498c9 --- /dev/null +++ b/test/Distributed/distributed_actor_accessor_thunks_32bit.swift @@ -0,0 +1,336 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend-emit-module -emit-module-path %t/FakeDistributedActorSystems.swiftmodule -module-name FakeDistributedActorSystems -disable-availability-checking %S/Inputs/FakeDistributedActorSystems.swift +// RUN: %target-swift-frontend -module-name distributed_actor_accessors -emit-irgen -enable-experimental-distributed -disable-availability-checking -I %t 2>&1 %s | %IRGenFileCheck %s + +// UNSUPPORTED: back_deploy_concurrency +// REQUIRES: concurrency +// REQUIRES: distributed + +// REQUIRES: CPU=i386 + +// UNSUPPORTED: OS=windows-msvc + +import _Distributed +import FakeDistributedActorSystems + +@available(SwiftStdlib 5.5, *) +typealias DefaultDistributedActorSystem = FakeActorSystem + +enum SimpleE : Codable { +case a +} + +enum E : Codable { +case a, b, c +} + +enum IndirectE : Codable { + case empty + indirect case test(_: Int) +} + +final class Obj : Codable, Sendable { + let x: Int + + init(x: Int) { + self.x = x + } +} + +struct LargeStruct : Codable { + var a: Int + var b: Int + var c: String + var d: Double +} + +@available(SwiftStdlib 5.6, *) +public distributed actor MyActor { + distributed func simple1(_: Int) { + } + + // `String` would be a direct result as a struct type + distributed func simple2(_: Int) -> String { + return "" + } + + // `String` is an object that gets exploded into two parameters + distributed func simple3(_: String) -> Int { + return 42 + } + + // Enum with a single case are special because they have an empty + // native schema so they are dropped from parameters/result. + distributed func single_case_enum(_ e: SimpleE) -> SimpleE { + return e + } + + distributed func with_indirect_enums(_: IndirectE, _: Int) -> IndirectE { + return .empty + } + + // Combination of multiple arguments, reference type and indirect result + // + // Note: Tuple types cannot be used here is either position because they + // cannot conform to protocols. + distributed func complex(_: [Int], _: Obj, _: String?, _: LargeStruct) -> LargeStruct { + fatalError() + } +} + +@available(SwiftStdlib 5.6, *) +public distributed actor MyOtherActor { + distributed func empty() { + } +} + +/// ---> Thunk and distributed method accessor for `simple1` + +/// Let's make sure that accessor loads the data from the buffer and calls expected accessor + +// CHECK: define hidden swifttailcc void @"$s27distributed_actor_accessors7MyActorC7simple1yySiFTE" + +// CHECK: define internal swifttailcc void @"$s27distributed_actor_accessors7MyActorC7simple1yySiFTETF"(%swift.context* swiftasync %0, i8* %1, i8* %2, %swift.refcounted* swiftself %3) + +/// Read the current offset and cast an element to `Int` + +// CHECK: store i8* %1, i8** %offset +// CHECK-NEXT: %elt_offset = load i8*, i8** %offset +// CHECK-NEXT: [[ELT_PTR:%.*]] = bitcast i8* %elt_offset to %TSi* +// CHECK-NEXT: [[NATIVE_VAL_LOC:%.*]] = getelementptr inbounds %TSi, %TSi* [[ELT_PTR]], i32 0, i32 0 +// CHECK-NEXT: [[ARG_VAL:%.*]] = load i32, i32* [[NATIVE_VAL_LOC]] + +/// Retrieve an async pointer to the distributed thunk for `simple1` + +// CHECK: [[THUNK_LOC:%.*]] = add i32 ptrtoint (void (%swift.context*, i32, %T27distributed_actor_accessors7MyActorC*)* @"$s27distributed_actor_accessors7MyActorC7simple1yySiFTE" to i32), {{.*}} +// CHECK-NEXT: [[OPAQUE_THUNK_PTR:%.*]] = inttoptr i32 [[THUNK_LOC]] to i8* +// CHECK-NEXT: [[THUNK_PTR:%.*]] = bitcast i8* [[OPAQUE_THUNK_PTR]] to void (%swift.context*, i32, %T27distributed_actor_accessors7MyActorC*)* + +/// Call distributed thunk for `simple1` and `end` async context without results + +// CHECK: [[THUNK_PTR_REF:%.*]] = bitcast void (%swift.context*, i32, %T27distributed_actor_accessors7MyActorC*)* [[THUNK_PTR]] to i8* +// CHECK-NEXT: [[THUNK_RESULT:%.*]] = call { i8*, %swift.error* } (i32, i8*, i8*, ...) @llvm.coro.suspend.async.sl_p0i8p0s_swift.errorss({{.*}}, i8* [[THUNK_PTR_REF]], %swift.context* {{.*}}, i32 [[ARG_VAL]], %T27distributed_actor_accessors7MyActorC* {{.*}}) +// CHECK-NEXT: [[TASK_REF:%.*]] = extractvalue { i8*, %swift.error* } [[THUNK_RESULT]], 0 +// CHECK-NEXT: {{.*}} = call i8* @__swift_async_resume_project_context(i8* [[TASK_REF]]) +// CHECK: {{.*}} = call i1 (i8*, i1, ...) @llvm.coro.end.async({{.*}}, %swift.context* {{.*}}, %swift.error* {{.*}}) + +/// ---> Thunk and distributed method accessor for `simple2` + +// CHECK: define hidden swifttailcc void @"$s27distributed_actor_accessors7MyActorC7simple2ySSSiFTE" + +// CHECK: define internal swifttailcc void @"$s27distributed_actor_accessors7MyActorC7simple2ySSSiFTETF" + +// CHECK: [[COERCED_RESULT_SLOT:%.*]] = bitcast i8* {{.*}} to %TSS* + +/// !!! - We are not going to double-check argument extraction here since it's the same as `simple1`. +// CHECK: [[NATIVE_ARG_VAL:%.*]] = load i32, i32* {{.*}} + +/// Load async pointer to distributed thunk for `simple2` + +// CHECK: [[THUNK_LOC:%.*]] = add i32 ptrtoint (void (%swift.context*, i32, %T27distributed_actor_accessors7MyActorC*)* @"$s27distributed_actor_accessors7MyActorC7simple2ySSSiFTE" to i32), {{.*}} +// CHECK-NEXT: [[OPAQUE_THUNK_PTR:%.*]] = inttoptr i32 [[THUNK_LOC]] to i8* +// CHECK-NEXT: [[THUNK_PTR:%.*]] = bitcast i8* [[OPAQUE_THUNK_PTR]] to void (%swift.context*, i32, %T27distributed_actor_accessors7MyActorC*)* + +/// Call the thunk with extracted argument value + +// CHECK: [[THUNK_PTR_REF:%.*]] = bitcast void (%swift.context*, i32, %T27distributed_actor_accessors7MyActorC*)* [[THUNK_PTR]] to i8* +// CHECK-NEXT: [[THUNK_RESULT:%.*]] = call { i8*, i32, i32, i32, %swift.error* } (i32, i8*, i8*, ...) @llvm.coro.suspend.async.sl_p0i8i32i32i32p0s_swift.errorss({{.*}}, i8* [[THUNK_PTR_REF]], %swift.context* {{.*}}, i32 [[NATIVE_ARG_VAL]], %T27distributed_actor_accessors7MyActorC* {{.*}}) +// CHECK-NEXT: [[TASK_REF:%.*]] = extractvalue { i8*, i32, i32, i32, %swift.error* } [[THUNK_RESULT]], 0 +// CHECK-NEXT: {{.*}} = call i8* @__swift_async_resume_project_context(i8* [[TASK_REF]]) + +/// Initialize the result buffer with values produced by the thunk + +// CHECK: %._guts1 = getelementptr inbounds %TSS, %TSS* [[COERCED_RESULT_SLOT]], i32 0, i32 0 +// CHECK: store i32 {{.*}}, i32* %._guts1._object._count._value +// CHECK: store i8 {{.*}}, i8* %._guts1._object._discriminator._value +// CHECK: store i16 {{.*}}, i16* %._guts1._object._flags._value, align 2 + +// CHECK: {{.*}} = call i1 (i8*, i1, ...) @llvm.coro.end.async({{.*}}, %swift.context* {{.*}}, %swift.error* {{.*}}) + +/// ---> Thunk and distributed method accessor for `simple3` + +// CHECK: define hidden swifttailcc void @"$s27distributed_actor_accessors7MyActorC7simple3ySiSSFTE" + +/// !!! in `simple3` interesting bits are: argument value extraction (because string is exploded into N arguments) and call to distributed thunk +// CHECK: define internal swifttailcc void @"$s27distributed_actor_accessors7MyActorC7simple3ySiSSFTETF"(%swift.context* swiftasync {{.*}}, i8* [[ARG_BUFF:%.*]], i8* [[RESULT_BUFF:%.*]], %swift.refcounted* swiftself {{.*}}) + +// CHECK: [[TYPED_RESULT_BUFF:%.*]] = bitcast i8* [[RESULT_BUFF]] to %TSi* + +// CHECK: [[ELT_PTR:%.*]] = bitcast i8* %elt_offset to %TSS* +// CHECK-NEXT: %._guts = getelementptr inbounds %TSS, %TSS* [[ELT_PTR]], i32 0, i32 0 + +// CHECK: [[STR_SIZE:%.*]] = load i32, i32* %._guts._object._count._value +// CHECK-NEXT: %._guts._object._variant = getelementptr inbounds %Ts13_StringObjectV, %Ts13_StringObjectV* %._guts._object, i32 0, i32 1 +// CHECK-NEXT: [[NATIVE_STR_VAL_PTR:%.*]] = bitcast %Ts13_StringObjectV7VariantO* %._guts._object._variant to i32* +// CHECK-NEXT: [[STR_VAL:%.*]] = load i32, i32* [[NATIVE_STR_VAL_PTR]] + +/// Load pointer to a distributed thunk for `simple3` + +// CHECK: [[THUNK_LOC:%.*]] = add i32 ptrtoint (void (%swift.context*, i32, i32, i32, %T27distributed_actor_accessors7MyActorC*)* @"$s27distributed_actor_accessors7MyActorC7simple3ySiSSFTE" to i32) +// CHECK-NEXT: [[OPAQUE_THUNK_PTR:%.*]] = inttoptr i32 [[THUNK_LOC]] to i8* +// CHECK-NEXT: [[THUNK_PTR:%.*]] = bitcast i8* [[OPAQUE_THUNK_PTR]] to void (%swift.context*, i32, i32, i32, %T27distributed_actor_accessors7MyActorC*)* + +// CHECK: [[TMP_STR_ARG:%.*]] = bitcast { i32, i32, i32 }* %temp-coercion.coerced to %TSS* +// CHECK-NEXT: %._guts1 = getelementptr inbounds %TSS, %TSS* [[TMP_STR_ARG]], i32 0, i32 0 + +// CHECK: store i32 %10, i32* %._guts1._object._count._value, align 4 +// CHECK: store i32 %12, i32* %28, align 4 + +// CHECK: [[STR_ARG_SIZE_PTR:%.*]] = getelementptr inbounds { i32, i32, i32 }, { i32, i32, i32 }* %temp-coercion.coerced, i32 0, i32 0 +// CHECK: [[STR_ARG_SIZE:%.*]] = load i32, i32* [[STR_ARG_SIZE_PTR]] + +// CHECK: [[STR_ARG_VAL_PTR:%.*]] = getelementptr inbounds { i32, i32, i32 }, { i32, i32, i32 }* %temp-coercion.coerced, i32 0, i32 1 +// CHECK: [[STR_ARG_VAL:%.*]] = load i32, i32* [[STR_ARG_VAL_PTR]] + +// CHECK: [[STR_ARG_FLAGS_PTR:%.*]] = getelementptr inbounds { i32, i32, i32 }, { i32, i32, i32 }* %temp-coercion.coerced, i32 0, i32 2 +// CHECK: [[STR_ARG_FLAGS:%.*]] = load i32, i32* [[STR_ARG_FLAGS_PTR]] + +/// Call distributed thunk with exploaded string value + +// CHECK: [[OPAQUE_THUNK_PTR:%.*]] = bitcast void (%swift.context*, i32, i32, i32, %T27distributed_actor_accessors7MyActorC*)* [[THUNK_PTR]] to i8* +// CHECK-NEXT: [[THUNK_RESULT:%.*]] = call { i8*, i32, %swift.error* } (i32, i8*, i8*, ...) @llvm.coro.suspend.async.sl_p0i8i32p0s_swift.errorss({{.*}}, i8* [[OPAQUE_THUNK_PTR]], %swift.context* {{.*}}, i32 [[STR_ARG_SIZE]], i32 [[STR_ARG_VAL]], i32 [[STR_ARG_FLAGS]], %T27distributed_actor_accessors7MyActorC* {{.*}}) +// CHECK-NEXT: [[TASK_REF:%.*]] = extractvalue { i8*, i32, %swift.error* } [[THUNK_RESULT]], 0 +// CHECK-NEXT: {{.*}} = call i8* @__swift_async_resume_project_context(i8* [[TASK_REF]]) +// CHECK: [[INT_RES:%.*]] = extractvalue { i8*, i32, %swift.error* } [[THUNK_RESULT]], 1 +// CHECK: %._value = getelementptr inbounds %TSi, %TSi* [[TYPED_RESULT_BUFF]], i32 0, i32 0 +// CHECK: store i32 [[INT_RES]], i32* %._value +// CHECK: {{.*}} = call i1 (i8*, i1, ...) @llvm.coro.end.async({{.*}}, %swift.context* {{.*}}, %swift.error* {{.*}}) + +/// --> Thunk and distributed method accessor for `single_case_enum` + +// CHECK: define hidden swifttailcc void @"$s27distributed_actor_accessors7MyActorC16single_case_enumyAA7SimpleEOAFFTE" + +// CHECK: define internal swifttailcc void @"$s27distributed_actor_accessors7MyActorC16single_case_enumyAA7SimpleEOAFFTETF"(%swift.context* swiftasync %0, i8* [[BUFFER:%.*]], i8* [[RESULT_BUFF:%.*]], %swift.refcounted* swiftself {{.*}}) + +/// First, let's check that there were no loads from the argument buffer and no stores to "current offset". + +// CHECK: [[OFFSET:%.*]] = bitcast i8** %offset to i8* +// CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 4, i8* [[OFFSET]]) +// CHECK-NEXT: store i8* [[BUFFER]], i8** %offset +// CHECK-NEXT: %elt_offset = load i8*, i8** %offset +// CHECK-NEXT: [[ELT_PTR:.*]] = bitcast i8* %elt_offset to %T27distributed_actor_accessors7SimpleEO* +// CHECK-NEXT: [[OFFSET:%.*]] = bitcast i8** %offset to i8* +// CHECK-NEXT call void @llvm.lifetime.end.p0i8(i32 8, i8* [[OFFSET]]) + +/// Now, let's check that the call doesn't have any arguments and returns nothing. + +// CHECK: [[THUNK_REF:%.*]] = bitcast void (%swift.context*, %T27distributed_actor_accessors7MyActorC*)* {{.*}} to i8* +// CHECK: {{.*}} = call { i8*, %swift.error* } (i32, i8*, i8*, ...) @llvm.coro.suspend.async.sl_p0i8p0s_swift.errorss({{.*}}, i8* [[THUNK_REF]], %swift.context* {{.*}}, %T27distributed_actor_accessors7MyActorC* {{.*}}) +// CHECK: {{.*}} = call i1 (i8*, i1, ...) @llvm.coro.end.async({{.*}}, i8* {{.*}}, %swift.context* {{.*}}, %swift.error* {{.*}}) + +/// --> Thunk and distributed method accessor for `with_indirect_enums` + +// CHECK: define hidden swifttailcc void @"$s27distributed_actor_accessors7MyActorC19with_indirect_enumsyAA9IndirectEOAF_SitFTE" +// CHECK: define internal swifttailcc void @"$s27distributed_actor_accessors7MyActorC19with_indirect_enumsyAA9IndirectEOAF_SitFTETF" + +/// First, Load both arguments from the buffer. + +// CHECK: [[TYPED_RESULT_BUFF:%.*]] = bitcast i8* %2 to %T27distributed_actor_accessors9IndirectEO* +// CHECK: store i8* %1, i8** %offset +// CHECK-NEXT: %elt_offset = load i8*, i8** %offset + +// CHECK-NEXT: [[ENUM_PTR:%.*]] = bitcast i8* %elt_offset to %T27distributed_actor_accessors9IndirectEO* +// CHECK-NEXT: [[NATIVE_ENUM_PTR:%.*]] = bitcast %T27distributed_actor_accessors9IndirectEO* [[ENUM_PTR]] to i32* +// CHECK-NEXT: [[NATIVE_ENUM_VAL:%.*]] = load i32, i32* [[NATIVE_ENUM_PTR]] +// CHECK: [[ENUM_PTR_INT:%.*]] = ptrtoint %T27distributed_actor_accessors9IndirectEO* [[ENUM_PTR]] to i32 +// CHECK-NEXT: [[NEXT_ELT_LOC:%.*]] = add i32 [[ENUM_PTR_INT]], 4 +// CHECK-NEXT: [[NEXT_ELT_PTR:%.*]] = inttoptr i32 [[NEXT_ELT_LOC]] to i8* +// CHECK-NEXT: store i8* [[NEXT_ELT_PTR]], i8** %offset +// CHECK-NEXT: %elt_offset1 = load i8*, i8** %offset +// CHECK-NEXT: [[INT_PTR:%.*]] = bitcast i8* %elt_offset1 to %TSi* +// CHECK-NEXT: %._value = getelementptr inbounds %TSi, %TSi* [[INT_PTR]], i32 0, i32 0 +// CHECK-NEXT: [[NATIVE_INT_VAL:%.*]] = load i32, i32* %._value + +/// Call distributed thunk with extracted arguments. + +// CHECK: [[THUNK_RESULT:%.*]] = call { i8*, i32, %swift.error* } (i32, i8*, i8*, ...) @llvm.coro.suspend.async.sl_p0i8i32p0s_swift.errorss({{.*}}, %swift.context* {{.*}}, i32 [[NATIVE_ENUM_VAL]], i32 [[NATIVE_INT_VAL]], %T27distributed_actor_accessors7MyActorC* {{.*}}) +// CHECK-NEXT: [[TASK_REF:%.*]] = extractvalue { i8*, i32, %swift.error* } [[THUNK_RESULT]], 0 +// CHECK-NEXT: {{.*}} = call i8* @__swift_async_resume_project_context(i8* [[TASK_REF]]) +// CHECK: [[ENUM_RESULT:%.*]] = extractvalue { i8*, i32, %swift.error* } [[THUNK_RESULT]], 1 +// CHECK: [[NATIVE_RESULT_PTR:%.*]] = bitcast %T27distributed_actor_accessors9IndirectEO* [[TYPED_RESULT_BUFF]] to i32* +// CHECK-NEXT: store i32 [[ENUM_RESULT]], i32* [[NATIVE_RESULT_PTR]] + +// CHECK: {{.*}} = call i1 (i8*, i1, ...) @llvm.coro.end.async({{.*}}, %swift.context* {{.*}}, %swift.error* {{.*}}) + +/// ---> Thunk and distributed method for `complex` + +// CHECK: define hidden swifttailcc void @"$s27distributed_actor_accessors7MyActorC7complexyAA11LargeStructVSaySiG_AA3ObjCSSSgAFtFTE" + +// CHECK: define internal swifttailcc void @"$s27distributed_actor_accessors7MyActorC7complexyAA11LargeStructVSaySiG_AA3ObjCSSSgAFtFTETF"(%swift.context* swiftasync {{.*}}, i8* [[ARG_BUFF:%.*]], i8* [[RESULT_BUFF:%.*]], %swift.refcounted* swiftself {{.*}}) + +/// First, let's check that all of the different argument types here are loaded correctly. + +/// Cast result buffer to the expected result type (in this case its indirect opaque pointer) +// CHECK: [[TYPED_RESULT_BUFF:%.*]] = bitcast i8* [[RESULT_BUFF]] to %swift.opaque* + +/// -> [Int] + +// CHECK: %elt_offset = load i8*, i8** %offset +// CHECK-NEXT: [[ARR_PTR:%.*]] = bitcast i8* %elt_offset to %TSa* +// CHECK-NEXT: %._buffer = getelementptr inbounds %TSa, %TSa* [[ARR_PTR]], i32 0, i32 0 +// CHECK: [[NATIVE_ARR_VAL:%.*]] = load [[ARR_STORAGE_TYPE:%.*]], [[ARR_STORAGE_TYPE]]* %._buffer._storage +// CHECK: [[ARR_PTR_INT:%.*]] = ptrtoint %TSa* [[ARR_PTR]] to i32 +// CHECK-NEXT: [[NEXT_ELT:%.*]] = add i32 [[ARR_PTR_INT]], 4 +// CHECK-NEXT: [[OPAQUE_NEXT_ELT:%.*]] = inttoptr i32 [[NEXT_ELT]] to i8* +// CHECK-NEXT: store i8* [[OPAQUE_NEXT_ELT]], i8** %offset + +/// -> Obj + +// CHECK-NEXT: %elt_offset1 = load i8*, i8** %offset +// CHECK-NEXT: [[OBJ_PTR:%.*]] = bitcast i8* %elt_offset1 to %T27distributed_actor_accessors3ObjC** +// CHECK-NEXT: [[NATIVE_OBJ_VAL:%.*]] = load %T27distributed_actor_accessors3ObjC*, %T27distributed_actor_accessors3ObjC** [[OBJ_PTR]] +// CHECK: [[OBJ_PTR_INT:%.*]] = ptrtoint %T27distributed_actor_accessors3ObjC** [[OBJ_PTR]] to i32 +// CHECK-NEXT: [[NEXT_ELT:%.*]] = add i32 [[OBJ_PTR_INT]], 4 +// CHECK-NEXT: [[NEXT_ELT_PTR:%.*]] = inttoptr i32 [[NEXT_ELT]] to i8* +// CHECK-NEXT: store i8* [[NEXT_ELT_PTR]], i8** %offset + +/// -> String? + +// CHECK-NEXT: %elt_offset2 = load i8*, i8** %offset +// CHECK-NEXT: [[OPT_PTR:%.*]] = bitcast i8* %elt_offset2 to %TSSSg* +// CHECK-NEXT: [[NATIVE_OPT_PTR:%.*]] = bitcast %TSSSg* [[OPT_PTR]] to { i32, i32, i32 }* +// CHECK-NEXT: [[NATIVE_OPT_VAL_0_PTR:%.*]] = getelementptr inbounds { i32, i32, i32 }, { i32, i32, i32 }* [[NATIVE_OPT_PTR]], i32 0, i32 0 +// CHECK-NEXT: [[NATIVE_OPT_VAL_0:%.*]] = load i32, i32* [[NATIVE_OPT_VAL_0_PTR]] +// CHECK-NEXT: [[NATIVE_OPT_VAL_1_PTR:%.*]] = getelementptr inbounds { i32, i32, i32 }, { i32, i32, i32 }* [[NATIVE_OPT_PTR]], i32 0, i32 1 +// CHECK-NEXT: [[NATIVE_OPT_VAL_1:%.*]] = load i32, i32* [[NATIVE_OPT_VAL_1_PTR]] +// CHECK-NEXT: [[NATIVE_OPT_VAL_2_PTR:%.*]] = getelementptr inbounds { i32, i32, i32 }, { i32, i32, i32 }* [[NATIVE_OPT_PTR]], i32 0, i32 2 +// CHECK-NEXT: [[NATIVE_OPT_VAL_2:%.*]] = load i32, i32* [[NATIVE_OPT_VAL_2_PTR]] +// CHECK-NEXT: [[OPT_PTR_INT:%.*]] = ptrtoint %TSSSg* [[OPT_PTR]] to i32 +// CHECK-NEXT: [[NEXT_ELT:%.*]] = add i32 [[OPT_PTR_INT]], 12 +// CHECK-NEXT: [[NEXT_ELT_PTR:%.*]] = inttoptr i32 [[NEXT_ELT]] to i8* +// CHECK-NEXT: store i8* [[NEXT_ELT_PTR]], i8** %offset + +/// -> LargeStruct (passed indirectly) + +// CHECK-NEXT: %elt_offset3 = load i8*, i8** %offset +// CHECK-NEXT: [[STRUCT_PTR:%.*]] = bitcast i8* %elt_offset3 to %T27distributed_actor_accessors11LargeStructV* +// CHECK-NEXT: [[STRUCT_PTR_VAL:%.*]] = ptrtoint %T27distributed_actor_accessors11LargeStructV* [[STRUCT_PTR]] to i32 +// CHECK-NEXT: [[STRUCT_PTR_VAL_ADJ_1:%.*]] = add nuw i32 [[STRUCT_PTR_VAL]], 7 +// CHECK-NEXT: [[STRUCT_PTR_VAL_ADJ_2:%.*]] = and i32 [[STRUCT_PTR_VAL_ADJ_1]], -8 +// CHECK-NEXT: [[STRUCT_PTR:%.*]] = inttoptr i32 [[STRUCT_PTR_VAL_ADJ_2]] to %T27distributed_actor_accessors11LargeStructV* + + +// CHECK: [[INDIRECT_RESULT_BUFF:%.*]] = bitcast %swift.opaque* [[TYPED_RESULT_BUFF]] to %T27distributed_actor_accessors11LargeStructV* + +/// Now let's make sure that distributed thunk call uses the arguments correctly + +// CHECK: [[THUNK_RESULT:%.*]] = call { i8*, %swift.error* } (i32, i8*, i8*, ...) @llvm.coro.suspend.async.sl_p0i8p0s_swift.errorss({{.*}}, %T27distributed_actor_accessors11LargeStructV* [[INDIRECT_RESULT_BUFF]], %swift.context* {{.*}}, {{.*}} [[NATIVE_ARR_VAL]], %T27distributed_actor_accessors3ObjC* [[NATIVE_OBJ_VAL]], i32 [[NATIVE_OPT_VAL_0]], i32 [[NATIVE_OPT_VAL_1]], i32 [[NATIVE_OPT_VAL_2]], %T27distributed_actor_accessors11LargeStructV* [[STRUCT_PTR]], %T27distributed_actor_accessors7MyActorC* {{.*}}) + +/// RESULT is returned indirectly so there is nothing to pass to `end` + +// CHECK: {{.*}} = call i1 (i8*, i1, ...) @llvm.coro.end.async({{.*}}, %swift.context* {{.*}}, %swift.error* {{.*}}) + +/// ---> Thunk and distributed method for `MyOtherActor.empty` + +/// Let's check that there is no offset allocation here since parameter list is empty + +// CHECK: define internal swifttailcc void @"$s27distributed_actor_accessors12MyOtherActorC5emptyyyFTETF"(%swift.context* swiftasync {{.*}}, i8* [[ARG_BUFF:%.*]], i8* [[RESULT_BUFF:%.*]], %swift.refcounted* swiftself {{.*}}) +// CHECK-NEXT: entry: +// CHECK-NEXT: {{.*}} = alloca %swift.context* +// CHECK-NEXT: %swifterror = alloca %swift.error* +// CHECK-NEXT: {{.*}} = call token @llvm.coro.id.async(i32 12, i32 16, i32 0, i8* bitcast (%swift.async_func_pointer* @"$s27distributed_actor_accessors12MyOtherActorC5emptyyyFTETFTu" to i8*)) +// CHECK-NEXT: {{.*}} = call i8* @llvm.coro.begin(token {{%.*}}, i8* null) +// CHECK-NEXT: store %swift.context* {{.*}}, %swift.context** {{.*}} +// CHECK-NEXT: store %swift.error* null, %swift.error** %swifterror +// CHECK-NEXT: {{.*}} = bitcast i8* [[RESULT_BUFF]] to %swift.opaque* +// CHECK-NEXT: {{.*}} = load i32, i32* getelementptr inbounds (%swift.async_func_pointer, %swift.async_func_pointer* bitcast (void (%swift.context*, %T27distributed_actor_accessors12MyOtherActorC*)* @"$s27distributed_actor_accessors12MyOtherActorC5emptyyyFTE" to %swift.async_func_pointer*), i32 0, i32 0) diff --git a/test/Distributed/distributed_actor_accessor_thunks_64bit.swift b/test/Distributed/distributed_actor_accessor_thunks_64bit.swift new file mode 100644 index 0000000000000..105e0fd91dcf7 --- /dev/null +++ b/test/Distributed/distributed_actor_accessor_thunks_64bit.swift @@ -0,0 +1,314 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend-emit-module -emit-module-path %t/FakeDistributedActorSystems.swiftmodule -module-name FakeDistributedActorSystems -disable-availability-checking %S/Inputs/FakeDistributedActorSystems.swift +// RUN: %target-swift-frontend -module-name distributed_actor_accessors -emit-irgen -enable-experimental-distributed -disable-availability-checking -I %t 2>&1 %s | %IRGenFileCheck %s + +// UNSUPPORTED: back_deploy_concurrency +// REQUIRES: concurrency +// REQUIRES: distributed + +// REQUIRES: CPU=x86_64 + +// UNSUPPORTED: OS=windows-msvc + +import _Distributed +import FakeDistributedActorSystems + +@available(SwiftStdlib 5.5, *) +typealias DefaultDistributedActorSystem = FakeActorSystem + +enum SimpleE : Codable { +case a +} + +enum E : Codable { +case a, b, c +} + +enum IndirectE : Codable { + case empty + indirect case test(_: Int) +} + +final class Obj : Codable, Sendable { + let x: Int + + init(x: Int) { + self.x = x + } +} + +struct LargeStruct : Codable { + var a: Int + var b: Int + var c: String + var d: Double +} + +@available(SwiftStdlib 5.6, *) +public distributed actor MyActor { + distributed func simple1(_: Int) { + } + + // `String` would be a direct result as a struct type + distributed func simple2(_: Int) -> String { + return "" + } + + // `String` is an object that gets exploded into two parameters + distributed func simple3(_: String) -> Int { + return 42 + } + + // Enum with a single case are special because they have an empty + // native schema so they are dropped from parameters/result. + distributed func single_case_enum(_ e: SimpleE) -> SimpleE { + return e + } + + distributed func with_indirect_enums(_: IndirectE, _: Int) -> IndirectE { + return .empty + } + + // Combination of multiple arguments, reference type and indirect result + // + // Note: Tuple types cannot be used here is either position because they + // cannot conform to protocols. + distributed func complex(_: [Int], _: Obj, _: String?, _: LargeStruct) -> LargeStruct { + fatalError() + } +} + +@available(SwiftStdlib 5.6, *) +public distributed actor MyOtherActor { + distributed func empty() { + } +} + +/// ---> Thunk and distributed method accessor for `simple1` + +/// Let's make sure that accessor loads the data from the buffer and calls expected accessor + +// CHECK: define hidden swifttailcc void @"$s27distributed_actor_accessors7MyActorC7simple1yySiFTE" + +// CHECK: define internal swifttailcc void @"$s27distributed_actor_accessors7MyActorC7simple1yySiFTETF"(%swift.context* swiftasync %0, i8* %1, i8* %2, %swift.refcounted* swiftself %3) + +/// Read the current offset and cast an element to `Int` + +// CHECK: store i8* %1, i8** %offset +// CHECK-NEXT: %elt_offset = load i8*, i8** %offset +// CHECK-NEXT: [[ELT_PTR:%.*]] = bitcast i8* %elt_offset to %TSi* +// CHECK-NEXT: [[NATIVE_VAL_LOC:%.*]] = getelementptr inbounds %TSi, %TSi* [[ELT_PTR]], i32 0, i32 0 +// CHECK-NEXT: [[ARG_VAL:%.*]] = load i64, i64* [[NATIVE_VAL_LOC]] + +/// Retrieve an async pointer to the distributed thunk for `simple1` + +// CHECK: [[THUNK_LOC:%.*]] = add i64 ptrtoint (void (%swift.context*, i64, %T27distributed_actor_accessors7MyActorC*)* @"$s27distributed_actor_accessors7MyActorC7simple1yySiFTE" to i64), {{.*}} +// CHECK-NEXT: [[OPAQUE_THUNK_PTR:%.*]] = inttoptr i64 [[THUNK_LOC]] to i8* +// CHECK-NEXT: [[THUNK_PTR:%.*]] = bitcast i8* [[OPAQUE_THUNK_PTR]] to void (%swift.context*, i64, %T27distributed_actor_accessors7MyActorC*)* + +/// Call distributed thunk for `simple1` and `end` async context without results + +// CHECK: [[THUNK_PTR_REF:%.*]] = bitcast void (%swift.context*, i64, %T27distributed_actor_accessors7MyActorC*)* [[THUNK_PTR]] to i8* +// CHECK-NEXT: [[THUNK_RESULT:%.*]] = call { i8*, %swift.error* } (i32, i8*, i8*, ...) @llvm.coro.suspend.async.sl_p0i8p0s_swift.errorss({{.*}}, i8* [[THUNK_PTR_REF]], %swift.context* {{.*}}, i64 [[ARG_VAL]], %T27distributed_actor_accessors7MyActorC* {{.*}}) +// CHECK-NEXT: [[TASK_REF:%.*]] = extractvalue { i8*, %swift.error* } [[THUNK_RESULT]], 0 +// CHECK-NEXT: {{.*}} = call i8* @__swift_async_resume_project_context(i8* [[TASK_REF]]) +// CHECK: {{.*}} = call i1 (i8*, i1, ...) @llvm.coro.end.async({{.*}}, %swift.context* {{.*}}, %swift.error* {{.*}}) + +/// ---> Thunk and distributed method accessor for `simple2` + +// CHECK: define hidden swifttailcc void @"$s27distributed_actor_accessors7MyActorC7simple2ySSSiFTE" + +// CHECK: define internal swifttailcc void @"$s27distributed_actor_accessors7MyActorC7simple2ySSSiFTETF" + +/// !!! - We are not going to double-check argument extraction here since it's the same as `simple1`. +// CHECK: [[NATIVE_ARG_VAL:%.*]] = load i64, i64* {{.*}} + +/// Load async pointer to distributed thunk for `simple2` + +// CHECK: [[THUNK_LOC:%.*]] = add i64 ptrtoint (void (%swift.context*, i64, %T27distributed_actor_accessors7MyActorC*)* @"$s27distributed_actor_accessors7MyActorC7simple2ySSSiFTE" to i64), {{.*}} +// CHECK-NEXT: [[OPAQUE_THUNK_PTR:%.*]] = inttoptr i64 [[THUNK_LOC]] to i8* +// CHECK-NEXT: [[THUNK_PTR:%.*]] = bitcast i8* [[OPAQUE_THUNK_PTR]] to void (%swift.context*, i64, %T27distributed_actor_accessors7MyActorC*)* + +/// Call the thunk with extracted argument value + +// CHECK: [[THUNK_PTR_REF:%.*]] = bitcast void (%swift.context*, i64, %T27distributed_actor_accessors7MyActorC*)* [[THUNK_PTR]] to i8* +// CHECK-NEXT: [[THUNK_RESULT:%.*]] = call { i8*, i64, %swift.bridge*, %swift.error* } (i32, i8*, i8*, ...) @llvm.coro.suspend.async.sl_p0i8i64p0s_swift.bridgesp0s_swift.errorss({{.*}}, i8* [[THUNK_PTR_REF]], %swift.context* {{.*}}, i64 [[NATIVE_ARG_VAL]], %T27distributed_actor_accessors7MyActorC* {{.*}}) +// CHECK-NEXT: [[TASK_REF:%.*]] = extractvalue { i8*, i64, %swift.bridge*, %swift.error* } [[THUNK_RESULT]], 0 +// CHECK-NEXT: {{.*}} = call i8* @__swift_async_resume_project_context(i8* [[TASK_REF]]) + +/// Extract information about `String` from result and call `end` + +// CHECK: [[STR_STRUCT:%.*]] = insertvalue { i64, %swift.bridge* } {{.*}}, %swift.bridge* {{.*}}, 1 +// CHECK: [[STR_SIZE:%.*]] = extractvalue { i64, %swift.bridge* } [[STR_STRUCT]], 0 +// CHECK-NEXT: [[STR_VAL:%.*]] = extractvalue { i64, %swift.bridge* } [[STR_STRUCT]], 1 + +/// Initialize the result buffer with values produced by the thunk + +// CHECK: store i64 [[STR_SIZE]], i64* %._guts._object._countAndFlagsBits._value +// CHECK: store %swift.bridge* [[STR_VAL]], %swift.bridge** %._guts._object._object + +// CHECK: {{.*}} = call i1 (i8*, i1, ...) @llvm.coro.end.async({{.*}}, %swift.context* {{.*}}, %swift.error* {{.*}}) + +/// ---> Thunk and distributed method accessor for `simple3` + +// CHECK: define hidden swifttailcc void @"$s27distributed_actor_accessors7MyActorC7simple3ySiSSFTE" + +/// !!! in `simple3` interesting bits are: argument value extraction (because string is exploded into N arguments) and call to distributed thunk +// CHECK: define internal swifttailcc void @"$s27distributed_actor_accessors7MyActorC7simple3ySiSSFTETF"(%swift.context* swiftasync {{.*}}, i8* [[ARG_BUFF:%.*]], i8* [[RESULT_BUFF:%.*]], %swift.refcounted* swiftself {{.*}}) + +// CHECK: [[TYPED_RESULT_BUFF:%.*]] = bitcast i8* [[RESULT_BUFF]] to %TSi* + +// CHECK: [[ELT_PTR:%.*]] = bitcast i8* %elt_offset to %TSS* +// CHECK-NEXT: %._guts = getelementptr inbounds %TSS, %TSS* [[ELT_PTR]], i32 0, i32 0 + +// CHECK: [[STR_SIZE:%.*]] = load i64, i64* %._guts._object._countAndFlagsBits._value +// CHECK: [[STR_VAL:%.*]] = load %swift.bridge*, %swift.bridge** %._guts._object._object + +/// Load pointer to a distributed thunk for `simple3` + +// CHECK: [[THUNK_LOC:%.*]] = add i64 ptrtoint (void (%swift.context*, i64, %swift.bridge*, %T27distributed_actor_accessors7MyActorC*)* @"$s27distributed_actor_accessors7MyActorC7simple3ySiSSFTE" to i64), {{.*}} +// CHECK-NEXT: [[OPAQUE_THUNK_PTR:%.*]] = inttoptr i64 [[THUNK_LOC]] to i8* +// CHECK-NEXT: [[THUNK_PTR:%.*]] = bitcast i8* [[OPAQUE_THUNK_PTR]] to void (%swift.context*, i64, %swift.bridge*, %T27distributed_actor_accessors7MyActorC*)* + +/// Call distributed thunk with exploaded string value + +// CHECK: [[OPAQUE_THUNK_PTR:%.*]] = bitcast void (%swift.context*, i64, %swift.bridge*, %T27distributed_actor_accessors7MyActorC*)* [[THUNK_PTR]] to i8* +// CHECK-NEXT: [[THUNK_RESULT:%.*]] = call { i8*, i64, %swift.error* } (i32, i8*, i8*, ...) @llvm.coro.suspend.async.sl_p0i8i64p0s_swift.errorss({{.*}}, i8* [[OPAQUE_THUNK_PTR]], %swift.context* {{.*}}, i64 [[STR_SIZE]], %swift.bridge* [[STR_VAL]], %T27distributed_actor_accessors7MyActorC* {{.*}}) +// CHECK-NEXT: [[TASK_REF:%.*]] = extractvalue { i8*, i64, %swift.error* } [[THUNK_RESULT]], 0 +// CHECK-NEXT: {{.*}} = call i8* @__swift_async_resume_project_context(i8* [[TASK_REF]]) +// CHECK: [[INT_RES:%.*]] = extractvalue { i8*, i64, %swift.error* } [[THUNK_RESULT]], 1 +// CHECK: %._value = getelementptr inbounds %TSi, %TSi* [[TYPED_RESULT_BUFF]], i32 0, i32 0 +// CHECK: store i64 [[INT_RES]], i64* %._value +// CHECK: {{.*}} = call i1 (i8*, i1, ...) @llvm.coro.end.async({{.*}}, %swift.context* {{.*}}, %swift.error* {{.*}}) + +/// --> Thunk and distributed method accessor for `single_case_enum` + +// CHECK: define hidden swifttailcc void @"$s27distributed_actor_accessors7MyActorC16single_case_enumyAA7SimpleEOAFFTE" + +// CHECK: define internal swifttailcc void @"$s27distributed_actor_accessors7MyActorC16single_case_enumyAA7SimpleEOAFFTETF"(%swift.context* swiftasync %0, i8* [[BUFFER:%.*]], i8* [[RESULT_BUFF:%.*]], %swift.refcounted* swiftself {{.*}}) + +/// First, let's check that there were no loads from the argument buffer and no stores to "current offset". + +// CHECK: [[OFFSET:%.*]] = bitcast i8** %offset to i8* +// CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 8, i8* [[OFFSET]]) +// CHECK-NEXT: store i8* [[BUFFER]], i8** %offset +// CHECK-NEXT: %elt_offset = load i8*, i8** %offset +// CHECK-NEXT: [[ELT_PTR:.*]] = bitcast i8* %elt_offset to %T27distributed_actor_accessors7SimpleEO* +// CHECK-NEXT: [[OFFSET:%.*]] = bitcast i8** %offset to i8* +// CHECK-NEXT call void @llvm.lifetime.end.p0i8(i64 8, i8* [[OFFSET]]) + +/// Now, let's check that the call doesn't have any arguments and returns nothing. + +// CHECK: [[THUNK_REF:%.*]] = bitcast void (%swift.context*, %T27distributed_actor_accessors7MyActorC*)* {{.*}} to i8* +// CHECK: {{.*}} = call { i8*, %swift.error* } (i32, i8*, i8*, ...) @llvm.coro.suspend.async.sl_p0i8p0s_swift.errorss({{.*}}, i8* [[THUNK_REF]], %swift.context* {{.*}}, %T27distributed_actor_accessors7MyActorC* {{.*}}) +// CHECK: {{.*}} = call i1 (i8*, i1, ...) @llvm.coro.end.async({{.*}}, i8* {{.*}}, %swift.context* {{.*}}, %swift.error* {{.*}}) + +/// --> Thunk and distributed method accessor for `with_indirect_enums` + +// CHECK: define hidden swifttailcc void @"$s27distributed_actor_accessors7MyActorC19with_indirect_enumsyAA9IndirectEOAF_SitFTE" +// CHECK: define internal swifttailcc void @"$s27distributed_actor_accessors7MyActorC19with_indirect_enumsyAA9IndirectEOAF_SitFTETF" + +/// First, Load both arguments from the buffer. + +// CHECK: [[TYPED_RESULT_BUFF:%.*]] = bitcast i8* %2 to %T27distributed_actor_accessors9IndirectEO* +// CHECK: store i8* %1, i8** %offset +// CHECK-NEXT: %elt_offset = load i8*, i8** %offset + +// CHECK-NEXT: [[ENUM_PTR:%.*]] = bitcast i8* %elt_offset to %T27distributed_actor_accessors9IndirectEO* +// CHECK-NEXT: [[NATIVE_ENUM_PTR:%.*]] = bitcast %T27distributed_actor_accessors9IndirectEO* [[ENUM_PTR]] to i64* +// CHECK-NEXT: [[NATIVE_ENUM_VAL:%.*]] = load i64, i64* [[NATIVE_ENUM_PTR]] +// CHECK: [[ENUM_PTR_INT:%.*]] = ptrtoint %T27distributed_actor_accessors9IndirectEO* [[ENUM_PTR]] to i64 +// CHECK-NEXT: [[NEXT_ELT_LOC:%.*]] = add i64 [[ENUM_PTR_INT]], 8 +// CHECK-NEXT: [[NEXT_ELT_PTR:%.*]] = inttoptr i64 [[NEXT_ELT_LOC]] to i8* +// CHECK-NEXT: store i8* [[NEXT_ELT_PTR]], i8** %offset +// CHECK-NEXT: %elt_offset1 = load i8*, i8** %offset +// CHECK-NEXT: [[INT_PTR:%.*]] = bitcast i8* %elt_offset1 to %TSi* +// CHECK-NEXT: %._value = getelementptr inbounds %TSi, %TSi* [[INT_PTR]], i32 0, i32 0 +// CHECK-NEXT: [[NATIVE_INT_VAL:%.*]] = load i64, i64* %._value + +/// Call distributed thunk with extracted arguments. + +// CHECK: [[THUNK_RESULT:%.*]] = call { i8*, i64, %swift.error* } (i32, i8*, i8*, ...) @llvm.coro.suspend.async.sl_p0i8i64p0s_swift.errorss({{.*}}, %swift.context* {{.*}}, i64 [[NATIVE_ENUM_VAL]], i64 [[NATIVE_INT_VAL]], %T27distributed_actor_accessors7MyActorC* {{.*}}) +// CHECK-NEXT: [[TASK_REF:%.*]] = extractvalue { i8*, i64, %swift.error* } [[THUNK_RESULT]], 0 +// CHECK-NEXT: {{.*}} = call i8* @__swift_async_resume_project_context(i8* [[TASK_REF]]) +// CHECK: [[ENUM_RESULT:%.*]] = extractvalue { i8*, i64, %swift.error* } [[THUNK_RESULT]], 1 +// CHECK: [[NATIVE_RESULT_PTR:%.*]] = bitcast %T27distributed_actor_accessors9IndirectEO* [[TYPED_RESULT_BUFF]] to i64* +// CHECK-NEXT: store i64 [[ENUM_RESULT]], i64* [[NATIVE_RESULT_PTR]] + +// CHECK: {{.*}} = call i1 (i8*, i1, ...) @llvm.coro.end.async({{.*}}, %swift.context* {{.*}}, %swift.error* {{.*}}) + +/// ---> Thunk and distributed method for `complex` + +// CHECK: define hidden swifttailcc void @"$s27distributed_actor_accessors7MyActorC7complexyAA11LargeStructVSaySiG_AA3ObjCSSSgAFtFTE" + +// CHECK: define internal swifttailcc void @"$s27distributed_actor_accessors7MyActorC7complexyAA11LargeStructVSaySiG_AA3ObjCSSSgAFtFTETF"(%swift.context* swiftasync {{.*}}, i8* [[ARG_BUFF:%.*]], i8* [[RESULT_BUFF:%.*]], %swift.refcounted* swiftself {{.*}}) + +/// First, let's check that all of the different argument types here are loaded correctly. + +/// Cast result buffer to the expected result type (in this case its indirect opaque pointer) +// CHECK: [[TYPED_RESULT_BUFF:%.*]] = bitcast i8* [[RESULT_BUFF]] to %swift.opaque* + +/// -> [Int] + +// CHECK: %elt_offset = load i8*, i8** %offset +// CHECK-NEXT: [[ARR_PTR:%.*]] = bitcast i8* %elt_offset to %TSa* +// CHECK-NEXT: %._buffer = getelementptr inbounds %TSa, %TSa* [[ARR_PTR]], i32 0, i32 0 +// CHECK: [[NATIVE_ARR_VAL:%.*]] = load [[ARR_STORAGE_TYPE:%.*]], [[ARR_STORAGE_TYPE]]* %._buffer._storage +// CHECK: [[ARR_PTR_INT:%.*]] = ptrtoint %TSa* [[ARR_PTR]] to i64 +// CHECK-NEXT: [[NEXT_ELT:%.*]] = add i64 [[ARR_PTR_INT]], 8 +// CHECK-NEXT: [[OPAQUE_NEXT_ELT:%.*]] = inttoptr i64 [[NEXT_ELT]] to i8* +// CHECK-NEXT: store i8* [[OPAQUE_NEXT_ELT]], i8** %offset + +/// -> Obj + +// CHECK-NEXT: %elt_offset1 = load i8*, i8** %offset +// CHECK-NEXT: [[OBJ_PTR:%.*]] = bitcast i8* %elt_offset1 to %T27distributed_actor_accessors3ObjC** +// CHECK-NEXT: [[NATIVE_OBJ_VAL:%.*]] = load %T27distributed_actor_accessors3ObjC*, %T27distributed_actor_accessors3ObjC** [[OBJ_PTR]] +// CHECK: [[OBJ_PTR_INT:%.*]] = ptrtoint %T27distributed_actor_accessors3ObjC** [[OBJ_PTR]] to i64 +// CHECK-NEXT: [[NEXT_ELT:%.*]] = add i64 [[OBJ_PTR_INT]], 8 +// CHECK-NEXT: [[NEXT_ELT_PTR:%.*]] = inttoptr i64 [[NEXT_ELT]] to i8* +// CHECK-NEXT: store i8* [[NEXT_ELT_PTR]], i8** %offset + +/// -> String? + +// CHECK-NEXT: %elt_offset2 = load i8*, i8** %offset +// CHECK-NEXT: [[OPT_PTR:%.*]] = bitcast i8* %elt_offset2 to %TSSSg* +// CHECK-NEXT: [[NATIVE_OPT_PTR:%.*]] = bitcast %TSSSg* [[OPT_PTR]] to { i64, i64 }* +// CHECK-NEXT: [[NATIVE_OPT_VAL_0_PTR:%.*]] = getelementptr inbounds { i64, i64 }, { i64, i64 }* [[NATIVE_OPT_PTR]], i32 0, i32 0 +// CHECK-NEXT: [[NATIVE_OPT_VAL_0:%.*]] = load i64, i64* [[NATIVE_OPT_VAL_0_PTR]] +// CHECK-NEXT: [[NATIVE_OPT_VAL_1_PTR:%.*]] = getelementptr inbounds { i64, i64 }, { i64, i64 }* [[NATIVE_OPT_PTR]], i32 0, i32 1 +// CHECK-NEXT: [[NATIVE_OPT_VAL_1:%.*]] = load i64, i64* [[NATIVE_OPT_VAL_1_PTR]] +// CHECK-NEXT: [[OPT_PTR_INT:%.*]] = ptrtoint %TSSSg* [[OPT_PTR]] to i64 +// CHECK-NEXT: [[NEXT_ELT:%.*]] = add i64 [[OPT_PTR_INT]], 16 +// CHECK-NEXT: [[NEXT_ELT_PTR:%.*]] = inttoptr i64 [[NEXT_ELT]] to i8* +// CHECK-NEXT: store i8* [[NEXT_ELT_PTR]], i8** %offset + +/// -> LargeStruct (passed indirectly) + +// CHECK-NEXT: %elt_offset3 = load i8*, i8** %offset +// CHECK-NEXT: [[STRUCT_PTR:%.*]] = bitcast i8* %elt_offset3 to %T27distributed_actor_accessors11LargeStructV* + +// CHECK: [[INDIRECT_RESULT_BUFF:%.*]] = bitcast %swift.opaque* [[TYPED_RESULT_BUFF]] to %T27distributed_actor_accessors11LargeStructV* + +/// Now let's make sure that distributed thunk call uses the arguments correctly + +// CHECK: [[THUNK_RESULT:%.*]] = call { i8*, %swift.error* } (i32, i8*, i8*, ...) @llvm.coro.suspend.async.sl_p0i8p0s_swift.errorss({{.*}}, %T27distributed_actor_accessors11LargeStructV* [[INDIRECT_RESULT_BUFF]], %swift.context* {{.*}}, {{.*}} [[NATIVE_ARR_VAL]], %T27distributed_actor_accessors3ObjC* [[NATIVE_OBJ_VAL]], i64 [[NATIVE_OPT_VAL_0]], i64 [[NATIVE_OPT_VAL_1]], %T27distributed_actor_accessors11LargeStructV* [[STRUCT_PTR]], %T27distributed_actor_accessors7MyActorC* {{.*}}) + +/// RESULT is returned indirectly so there is nothing to pass to `end` + +// CHECK: {{.*}} = call i1 (i8*, i1, ...) @llvm.coro.end.async({{.*}}, %swift.context* {{.*}}, %swift.error* {{.*}}) + +/// ---> Thunk and distributed method for `MyOtherActor.empty` + +/// Let's check that there is no offset allocation here since parameter list is empty + +// CHECK: define internal swifttailcc void @"$s27distributed_actor_accessors12MyOtherActorC5emptyyyFTETF"(%swift.context* swiftasync {{.*}}, i8* [[ARG_BUFF:%.*]], i8* [[RESULT_BUFF:%.*]], %swift.refcounted* swiftself {{.*}}) +// CHECK-NEXT: entry: +// CHECK-NEXT: {{.*}} = alloca %swift.context* +// CHECK-NEXT: %swifterror = alloca swifterror %swift.error* +// CHECK-NEXT: {{.*}} = call token @llvm.coro.id.async(i32 20, i32 16, i32 0, i8* bitcast (%swift.async_func_pointer* @"$s27distributed_actor_accessors12MyOtherActorC5emptyyyFTETFTu" to i8*)) +// CHECK-NEXT: {{.*}} = call i8* @llvm.coro.begin(token {{%.*}}, i8* null) +// CHECK-NEXT: store %swift.context* {{.*}}, %swift.context** {{.*}} +// CHECK-NEXT: store %swift.error* null, %swift.error** %swifterror +// CHECK-NEXT: {{.*}} = bitcast i8* [[RESULT_BUFF]] to %swift.opaque* +// CHECK-NEXT: {{.*}} = load i32, i32* getelementptr inbounds (%swift.async_func_pointer, %swift.async_func_pointer* bitcast (void (%swift.context*, %T27distributed_actor_accessors12MyOtherActorC*)* @"$s27distributed_actor_accessors12MyOtherActorC5emptyyyFTE" to %swift.async_func_pointer*), i32 0, i32 0) diff --git a/test/SIL/Parser/basic.sil b/test/SIL/Parser/basic.sil index fe64079ecb632..801a1baa1f719 100644 --- a/test/SIL/Parser/basic.sil +++ b/test/SIL/Parser/basic.sil @@ -1725,6 +1725,13 @@ bb0: return %0 : $() } +// CHECK-LABEL: sil hidden [distributed] @test_distributed_method +sil hidden [distributed] @test_distributed_method : $@convention(thin) () -> () { +bb0: + %0 = tuple () + return %0 : $() +} + struct EmptyStruct {} sil @test_empty_destructure : $@convention(thin) () -> () { diff --git a/test/SILGen/distributed_thunk.swift b/test/SILGen/distributed_thunk.swift index 396d032de4ac9..7f58eac6f23c9 100644 --- a/test/SILGen/distributed_thunk.swift +++ b/test/SILGen/distributed_thunk.swift @@ -9,7 +9,7 @@ distributed actor DA { } extension DA { - // CHECK-LABEL: sil hidden [thunk] [ossa] @$s17distributed_thunk2DAC1fyyFTE : $@convention(method) @async (@guaranteed DA) -> @error Error + // CHECK-LABEL: sil hidden [thunk] [distributed] [ossa] @$s17distributed_thunk2DAC1fyyFTE : $@convention(method) @async (@guaranteed DA) -> @error Error // CHECK: function_ref @swift_distributed_actor_is_remote // Call the actor function