From 79ad9278b2a4825a3b025cb28a6287ec8ee69582 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Thu, 7 Apr 2022 17:19:55 +0900 Subject: [PATCH 1/8] [Distributed] Retain adhoc decodeNextArgument in distributed thunk --- include/swift/AST/Decl.h | 11 ++++ include/swift/AST/DistributedDecl.h | 4 ++ include/swift/SIL/SILDeclRef.h | 7 ++- include/swift/SIL/SILFunction.h | 61 ++++++++++++++---- lib/AST/Decl.cpp | 4 ++ lib/AST/DistributedDecl.cpp | 15 +++++ lib/SIL/IR/SILDeclRef.cpp | 8 +++ lib/SIL/IR/SILFunctionBuilder.cpp | 62 +++++++++++++------ lib/SIL/IR/SILModule.cpp | 2 + lib/SIL/IR/SILPrinter.cpp | 7 +++ lib/SIL/Parser/ParseSIL.cpp | 40 ++++++++++-- lib/Sema/TypeCheckDistributed.cpp | 9 ++- lib/Serialization/DeserializeSIL.cpp | 27 +++++--- lib/Serialization/ModuleFormat.h | 2 +- lib/Serialization/SILFormat.h | 1 + lib/Serialization/SerializeSIL.cpp | 17 +++-- .../Distributed/DistributedActorSystem.swift | 5 +- .../LocalTestingDistributedActorSystem.swift | 2 +- .../Inputs/FakeDistributedActorSystems.swift | 11 +++- ...adhoc_requirement_not_optimized_away.swift | 51 +++++++++++++++ 20 files changed, 290 insertions(+), 56 deletions(-) create mode 100644 test/Distributed/Runtime/distributed_actor_cross_module_final_class_adhoc_requirement_not_optimized_away.swift diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index ff2e73923df9d..0130ec9c1e19c 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -2258,6 +2258,9 @@ class ValueDecl : public Decl { /// Whether this declaration produces an implicitly unwrapped /// optional result. unsigned isIUO : 1; +// +// /// Whether this declaration is a witness to an ad-hoc requirement. +// unsigned isAdHocRequirementWitness : 1; } LazySemanticInfo = { }; friend class DynamicallyReplacedDeclRequest; @@ -2595,6 +2598,14 @@ class ValueDecl : public Decl { /// Returns true if this decl can be found by id-style dynamic lookup. bool canBeAccessedByDynamicLookup() const; +// /// Set whether this declaration is a witness to an ad-hoc requirement or not. +// void setIsAdHocRequirementWitness(bool value); +// +// /// Whether this declaration is a witness to an ad-hoc requirement. +// bool isAdHocRequirementWitness() const { +// return LazySemanticInfo.isAdHocRequirementWitness; +// } + /// Returns true if this declaration has an implicitly unwrapped optional /// result. The precise meaning depends on the declaration kind: /// - for properties, the value is IUO diff --git a/include/swift/AST/DistributedDecl.h b/include/swift/AST/DistributedDecl.h index 68bf4b34e6baa..e3eb91cfcefa3 100644 --- a/include/swift/AST/DistributedDecl.h +++ b/include/swift/AST/DistributedDecl.h @@ -57,6 +57,10 @@ Type getDistributedSerializationRequirementType( /// system. Type getDistributedActorSystemInvocationEncoderType(NominalTypeDecl *system); +/// Get the specific 'InvocationDecoder' type of a specific distributed actor +/// system. +Type getDistributedActorSystemInvocationDecoderType(NominalTypeDecl *system); + /// Get the specific 'ResultHandler' type of a specific distributed actor /// system. Type getDistributedActorSystemResultHandlerType(NominalTypeDecl *system); diff --git a/include/swift/SIL/SILDeclRef.h b/include/swift/SIL/SILDeclRef.h index 930037fefed90..e92ddac6d764c 100644 --- a/include/swift/SIL/SILDeclRef.h +++ b/include/swift/SIL/SILDeclRef.h @@ -555,7 +555,12 @@ struct SILDeclRef { bool canBeDynamicReplacement() const; - bool isAutoDiffDerivativeFunction() const { +// /// True if the function was a witness to an ad-hoc requirement. +// /// Useful in order to e.g. not optimize away the function even +// /// if it seems not-used to the optimizer based on information in SIL. +// bool isAdHocRequirementWitness() const; + + bool isAutoDiffDerivativeFunction() const { return pointer.is() && pointer.get() != nullptr; } diff --git a/include/swift/SIL/SILFunction.h b/include/swift/SIL/SILFunction.h index be958f54030fa..05e2aa1afd05b 100644 --- a/include/swift/SIL/SILFunction.h +++ b/include/swift/SIL/SILFunction.h @@ -55,6 +55,10 @@ enum IsDynamicallyReplaceable_t { IsNotDynamic, IsDynamic }; +//enum IsAdHocRequirementWitness_t { +// IsNotAdHocWitness, +// IsAdHocWitness +//}; enum IsExactSelfClass_t { IsNotExactSelfClass, IsExactSelfClass, @@ -212,6 +216,15 @@ class SILFunction /// @_dynamicReplacement(for:) function. SILFunction *ReplacedFunction = nullptr; + /// Ad-hoc requirements may need to be retained explicitly, such that they + /// do not get optimized away as it might seem tha they are un-used. + /// + /// Specifically, e.g. the DistributedTargetInvocationDecoder's + /// 'decodeNextArgument' must be retained, as it is only used from IRGen + /// and such, appears as-if unused in SIL and would get optimized away. + // TODO: rename: referenced ad hoc requirement witnesses + SILFunction *AdHocRequirementFunction = nullptr; + Identifier ObjCReplacementFor; /// The head of a single-linked list of currently alive BasicBlockBitfield. @@ -298,7 +311,7 @@ class SILFunction /// Whether the implementation can be dynamically replaced. unsigned IsDynamicReplaceable : 1; - + /// If true, this indicates that a class method implementation will always be /// invoked with a `self` argument of the exact base class type. unsigned ExactSelfClass : 1; @@ -350,6 +363,9 @@ class SILFunction /// The function is in a statically linked module. unsigned IsStaticallyLinked : 1; +// /// The function is a witness to an ad-hoc requirement. +// unsigned IsAdHocRequirementWitness : 1; + static void validateSubclassScope(SubclassScope scope, IsThunk_t isThunk, const GenericSpecializationInformation *genericInfo) { @@ -466,16 +482,6 @@ class SILFunction ReplacedFunction = f; ReplacedFunction->incrementRefCount(); } - - SILFunction *getDistributedRecordArgumentFunction() const { - return ReplacedFunction; - } - void setDistributedRecordArgumentFunction(SILFunction *f) { - if (f == nullptr) - return; - f->incrementRefCount(); - } - /// This function should only be called when SILFunctions are bulk deleted. void dropDynamicallyReplacedFunction() { if (!ReplacedFunction) @@ -484,6 +490,30 @@ class SILFunction ReplacedFunction = nullptr; } + SILFunction *getUsedAdHocRequirementWitnessFunction() const { + return AdHocRequirementFunction; + } + // Marks that this `SILFunction` uses the passed in ad-hoc protocol + // requirement witness `f` and therefore must retain it explicitly, + // otherwise we might not be able to get a reference to it. + void setUsedAdHocRequirementWitnessFunction(SILFunction *f) { + assert(AdHocRequirementFunction == nullptr && "already set"); + + fprintf(stderr, "[%s:%d] (%s) SET AD HOC WITNESS [%s] ON [%s]\n", __FILE__, __LINE__, __FUNCTION__, f->getName().str().c_str(), this->getName().str().c_str()); + f->dump(); + + if (f == nullptr) + return; + AdHocRequirementFunction = f; + AdHocRequirementFunction->incrementRefCount(); + } + void dropAdHocRequirementFunction() { + if (!AdHocRequirementFunction) + return; + AdHocRequirementFunction->decrementRefCount(); + AdHocRequirementFunction = nullptr; + } + bool hasObjCReplacement() const { return !ObjCReplacementFor.empty(); } @@ -758,6 +788,15 @@ class SILFunction IsDynamicReplaceable = value; assert(!Transparent || !IsDynamicReplaceable); } + + /// Returns whether this function implementation can be dynamically replaced. +// IsAdHocRequirementWitness_t isAdHocRequirementWitness() const { +// return IsAdHocRequirementWitness_t(IsAdHocRequirementWitness); +// } +// void setIsAdHocRequirementWitness(IsAdHocRequirementWitness_t value = IsAdHocWitness) { +// IsAdHocRequirementWitness = value; +// assert(!IsDynamicReplaceable); +// } IsExactSelfClass_t isExactSelfClass() const { return IsExactSelfClass_t(ExactSelfClass); diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index d311173ffa2ed..af6bcb9ca9449 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -3167,6 +3167,10 @@ void ValueDecl::setIsDynamic(bool value) { LazySemanticInfo.isDynamic = value; } +//void ValueDecl::setIsAdHocRequirementWitness(bool value) { +// LazySemanticInfo.isAdHocRequirementWitness = true; +//} + ValueDecl *ValueDecl::getDynamicallyReplacedDecl() const { return evaluateOrDefault(getASTContext().evaluator, DynamicallyReplacedDeclRequest{ diff --git a/lib/AST/DistributedDecl.cpp b/lib/AST/DistributedDecl.cpp index 1b3e172b7f060..84b88306879d8 100644 --- a/lib/AST/DistributedDecl.cpp +++ b/lib/AST/DistributedDecl.cpp @@ -168,6 +168,21 @@ Type swift::getDistributedActorSystemInvocationEncoderType(NominalTypeDecl *syst return conformance.getTypeWitnessByName(selfType, ctx.Id_InvocationEncoder); } +Type swift::getDistributedActorSystemInvocationDecoderType(NominalTypeDecl *system) { + assert(!system->isDistributedActor()); + auto &ctx = system->getASTContext(); + + auto DAS = ctx.getDistributedActorSystemDecl(); + if (!DAS) + return Type(); + + // Dig out the serialization requirement type. + auto module = system->getParentModule(); + Type selfType = system->getSelfInterfaceType(); + auto conformance = module->lookupConformance(selfType, DAS); + return conformance.getTypeWitnessByName(selfType, ctx.Id_InvocationDecoder); +} + Type swift::getDistributedSerializationRequirementType( NominalTypeDecl *nominal, ProtocolDecl *protocol) { assert(nominal); diff --git a/lib/SIL/IR/SILDeclRef.cpp b/lib/SIL/IR/SILDeclRef.cpp index c99ac31751aac..cd66123b0be42 100644 --- a/lib/SIL/IR/SILDeclRef.cpp +++ b/lib/SIL/IR/SILDeclRef.cpp @@ -1382,6 +1382,14 @@ bool SILDeclRef::canBeDynamicReplacement() const { return true; } +//bool SILDeclRef::isAdHocRequirementWitness() const { +// auto decl = getDecl(); +// if (!decl) +// return false; +// +// return decl->isAdHocRequirementWitness(); +//} + bool SILDeclRef::isDynamicallyReplaceable() const { // The non-foreign entry of a @dynamicReplacement(for:) of @objc method in a // generic class can't be a dynamically replaced. diff --git a/lib/SIL/IR/SILFunctionBuilder.cpp b/lib/SIL/IR/SILFunctionBuilder.cpp index 84dd94b8a1b07..94d420bd3fc1f 100644 --- a/lib/SIL/IR/SILFunctionBuilder.cpp +++ b/lib/SIL/IR/SILFunctionBuilder.cpp @@ -14,6 +14,7 @@ #include "swift/AST/AttrKind.h" #include "swift/AST/Availability.h" #include "swift/AST/DiagnosticsParse.h" +#include "swift/AST/DistributedDecl.h" #include "swift/AST/Decl.h" #include "swift/AST/ParameterList.h" #include "swift/AST/SemanticAttrs.h" @@ -200,29 +201,52 @@ void SILFunctionBuilder::addFunctionAttributes( // Only assign replacements when the thing being replaced is function-like and // explicitly declared. auto *origDecl = decl->getDynamicallyReplacedDecl(); - auto *replacedDecl = dyn_cast_or_null(origDecl); - if (!replacedDecl) - return; - - // For @objc method replacement we normally use categories to perform the - // replacement. Except for methods in generic class where we can't. Instead, - // we special case this and use the native swift replacement mechanism. - if (decl->isObjC() && !decl->isNativeMethodReplacement()) { - F->setObjCReplacement(replacedDecl); - return; - } + if (auto *replacedDecl = dyn_cast_or_null(origDecl)) { + // For @objc method replacement we normally use categories to perform the + // replacement. Except for methods in generic class where we can't. Instead, + // we special case this and use the native swift replacement mechanism. + if (decl->isObjC() && !decl->isNativeMethodReplacement()) { + F->setObjCReplacement(replacedDecl); + return; + } - if (!constant.canBeDynamicReplacement()) - return; + if (constant.canBeDynamicReplacement()) { + SILDeclRef declRef(replacedDecl, constant.kind, false); + auto *replacedFunc = getOrCreateDeclaration(replacedDecl, declRef); - SILDeclRef declRef(replacedDecl, constant.kind, false); - auto *replacedFunc = getOrCreateDeclaration(replacedDecl, declRef); + assert(replacedFunc->getLoweredFunctionType() == + F->getLoweredFunctionType() || + replacedFunc->getLoweredFunctionType()->hasOpaqueArchetype()); - assert(replacedFunc->getLoweredFunctionType() == - F->getLoweredFunctionType() || - replacedFunc->getLoweredFunctionType()->hasOpaqueArchetype()); + F->setDynamicallyReplacedFunction(replacedFunc); + } + } - F->setDynamicallyReplacedFunction(replacedFunc); + if (constant.isDistributedThunk()) { + fprintf(stderr, "[%s:%d] (%s) IS DIST THUNK!\n", __FILE__, __LINE__, __FUNCTION__); + constant.dump(); + + // TODO: also handle protocol / extension + auto actor = dyn_cast(decl->getDeclContext()); + if (actor && actor->isDistributedActor()) { + auto &C = decl->getASTContext(); + auto systemTy = getDistributedActorSystemType(actor); + assert(systemTy); + + auto decoderTy = + getDistributedActorSystemInvocationDecoderType( + systemTy->getAnyNominal()); + assert(decoderTy); + + auto decodeFunc = C.getDecodeNextArgumentOnDistributedInvocationDecoder( + decoderTy->getAnyNominal()); + auto decodeRef = SILDeclRef(decodeFunc); + auto *adHocWitness = getOrCreateDeclaration(decodeFunc, decodeRef); + F->setUsedAdHocRequirementWitnessFunction(adHocWitness); + + F->dump(); + } + } } SILFunction *SILFunctionBuilder::getOrCreateFunction( diff --git a/lib/SIL/IR/SILModule.cpp b/lib/SIL/IR/SILModule.cpp index 8bd442a03ddd8..631b15f21b77e 100644 --- a/lib/SIL/IR/SILModule.cpp +++ b/lib/SIL/IR/SILModule.cpp @@ -138,6 +138,7 @@ SILModule::~SILModule() { for (SILFunction &F : *this) { F.dropAllReferences(); F.dropDynamicallyReplacedFunction(); + F.dropAdHocRequirementFunction(); F.clearSpecializeAttrs(); } @@ -467,6 +468,7 @@ void SILModule::eraseFunction(SILFunction *F) { // (References are not needed anymore.) F->clear(); F->dropDynamicallyReplacedFunction(); + F->dropAdHocRequirementFunction(); // Drop references for any _specialize(target:) functions. F->clearSpecializeAttrs(); } diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index bb7672342628d..1eb57a21f35ed 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -2983,6 +2983,13 @@ void SILFunction::print(SILPrintContext &PrintCtx) const { OS << "\"] "; } + if (auto *usedFunc = getUsedAdHocRequirementWitnessFunction()) { + OS << "[used_adhoc_requirement_witness \""; + OS << usedFunc->getName(); + OS << "\"] "; + fprintf(stderr, "[%s:%d] (%s) PRINTED THE used_adhoc_requirement_witness...\n", __FILE__, __LINE__, __FUNCTION__); + } + if (hasObjCReplacement()) { OS << "[objc_replacement_for \""; OS << getObjCReplacement().str(); diff --git a/lib/SIL/Parser/ParseSIL.cpp b/lib/SIL/Parser/ParseSIL.cpp index 3f8a75c19534f..fcf79b46f6355 100644 --- a/lib/SIL/Parser/ParseSIL.cpp +++ b/lib/SIL/Parser/ParseSIL.cpp @@ -973,6 +973,7 @@ static bool parseDeclSILOptional(bool *isTransparent, IsDistributed_t *isDistributed, IsExactSelfClass_t *isExactSelfClass, SILFunction **dynamicallyReplacedFunction, + SILFunction **usedAdHocRequirementWitness, Identifier *objCReplacementFor, SILFunction::Purpose *specialPurpose, Inline_t *inlineStrategy, @@ -1069,7 +1070,7 @@ static bool parseDeclSILOptional(bool *isTransparent, *MRK = EffectsKind::ReadWrite; else if (MRK && SP.P.Tok.getText() == "releasenone") *MRK = EffectsKind::ReleaseNone; - else if (dynamicallyReplacedFunction && SP.P.Tok.getText() == "dynamic_replacement_for") { + else if (dynamicallyReplacedFunction && SP.P.Tok.getText() == "dynamic_replacement_for") { SP.P.consumeToken(tok::identifier); if (SP.P.Tok.getKind() != tok::string_literal) { SP.P.diagnose(SP.P.Tok, diag::expected_in_attribute_list); @@ -1087,6 +1088,27 @@ static bool parseDeclSILOptional(bool *isTransparent, *dynamicallyReplacedFunction = Func; SP.P.consumeToken(tok::string_literal); + SP.P.parseToken(tok::r_square, diag::expected_in_attribute_list); + continue; + } else if (usedAdHocRequirementWitness && SP.P.Tok.getText() == "used_adhoc_requirement_witness") { + fprintf(stderr, "[%s:%d] (%s) PARSING THE... used_adhoc_requirement_witness\n", __FILE__, __LINE__, __FUNCTION__); + SP.P.consumeToken(tok::identifier); + if (SP.P.Tok.getKind() != tok::string_literal) { + SP.P.diagnose(SP.P.Tok, diag::expected_in_attribute_list); + return true; + } + // Drop the double quotes. + StringRef witnessFunc = SP.P.Tok.getText().drop_front().drop_back(); + SILFunction *Func = M.lookUpFunction(witnessFunc.str()); + if (!Func) { + Identifier Id = SP.P.Context.getIdentifier(witnessFunc); + SP.P.diagnose(SP.P.Tok, diag::sil_dynamically_replaced_func_not_found, // FIXME: bettererror + Id); + return true; + } + *usedAdHocRequirementWitness = Func; + SP.P.consumeToken(tok::string_literal); + SP.P.parseToken(tok::r_square, diag::expected_in_attribute_list); continue; } else if (objCReplacementFor && @@ -6335,12 +6357,13 @@ bool SILParserState::parseDeclSIL(Parser &P) { EffectsKind MRK = EffectsKind::Unspecified; llvm::SmallVector argEffectLocs; SILFunction *DynamicallyReplacedFunction = nullptr; + SILFunction *AdHocWitnessFunction = nullptr; Identifier objCReplacementFor; if (parseSILLinkage(FnLinkage, P) || parseDeclSILOptional( &isTransparent, &isSerialized, &isCanonical, &hasOwnershipSSA, &isThunk, &isDynamic, &isDistributed, &isExactSelfClass, - &DynamicallyReplacedFunction, &objCReplacementFor, &specialPurpose, + &DynamicallyReplacedFunction, &AdHocWitnessFunction, &objCReplacementFor, &specialPurpose, &inlineStrategy, &optimizationMode, &perfConstr, nullptr, &isWeakImported, &availability, &isWithoutActuallyEscapingThunk, &Semantics, @@ -6378,6 +6401,11 @@ bool SILParserState::parseDeclSIL(Parser &P) { FunctionState.F->setIsExactSelfClass(isExactSelfClass); FunctionState.F->setDynamicallyReplacedFunction( DynamicallyReplacedFunction); + if (AdHocWitnessFunction) { + fprintf(stderr, "[%s:%d] (%s) PARSED AdHocWitnessFunction\n", __FILE__, __LINE__, __FUNCTION__); + } + FunctionState.F->setUsedAdHocRequirementWitnessFunction( + AdHocWitnessFunction); if (!objCReplacementFor.empty()) FunctionState.F->setObjCReplacement(objCReplacementFor); FunctionState.F->setSpecialPurpose(specialPurpose); @@ -6571,7 +6599,7 @@ bool SILParserState::parseSILGlobal(Parser &P) { if (parseSILLinkage(GlobalLinkage, P) || parseDeclSILOptional(nullptr, &isSerialized, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, &isLet, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, State, M) || P.parseToken(tok::at_sign, diag::expected_sil_value_name) || @@ -6624,7 +6652,7 @@ bool SILParserState::parseSILProperty(Parser &P) { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - SP, M)) + nullptr, SP, M)) return true; ValueDecl *VD; @@ -6694,7 +6722,7 @@ bool SILParserState::parseSILVTable(Parser &P) { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, VTableState, M)) + nullptr, nullptr, VTableState, M)) return true; // Parse the class name. @@ -7211,7 +7239,7 @@ bool SILParserState::parseSILWitnessTable(Parser &P) { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, WitnessState, M)) + nullptr, nullptr, WitnessState, M)) return true; // Parse the protocol conformance. diff --git a/lib/Sema/TypeCheckDistributed.cpp b/lib/Sema/TypeCheckDistributed.cpp index 063700e0956a6..d767deb65714c 100644 --- a/lib/Sema/TypeCheckDistributed.cpp +++ b/lib/Sema/TypeCheckDistributed.cpp @@ -72,8 +72,15 @@ static AbstractFunctionDecl *findDistributedAdHocRequirement( for (auto value : decl->lookupDirect(identifier)) { auto func = dyn_cast(value); - if (func && matchFn(func)) + if (func && matchFn(func)) { + // In order to prevent an "unused" (from SIL's perspective) function + // from being optimized away, we mark it as an ad-hoc requirement witness + // which is then picked up in SIL and causes the function to be retained. +// fprintf(stderr, "[%s:%d] (%s) FOUND ADHOC WITNESS: %s\n", __FILE__, __LINE__, __FUNCTION__, func->getNameStr().str().c_str()); +// +// func->setIsAdHocRequirementWitness(true); return func; + } } return nullptr; diff --git a/lib/Serialization/DeserializeSIL.cpp b/lib/Serialization/DeserializeSIL.cpp index 47010bce0d5d4..45a3b5dcc662e 100644 --- a/lib/Serialization/DeserializeSIL.cpp +++ b/lib/Serialization/DeserializeSIL.cpp @@ -521,9 +521,10 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, DeclID clangNodeOwnerID; TypeID funcTyID; IdentifierID replacedFunctionID; + IdentifierID usedAdHocWitnessFunctionID; GenericSignatureID genericSigID; unsigned rawLinkage, isTransparent, isSerialized, isThunk, - isWithoutactuallyEscapingThunk, specialPurpose, inlineStrategy, + isWithoutActuallyEscapingThunk, specialPurpose, inlineStrategy, optimizationMode, perfConstr, subclassScope, hasCReferences, effect, numAttrs, hasQualifiedOwnership, isWeakImported, LIST_VER_TUPLE_PIECES(available), @@ -531,11 +532,12 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, ArrayRef SemanticsIDs; SILFunctionLayout::readRecord( scratch, rawLinkage, isTransparent, isSerialized, isThunk, - isWithoutactuallyEscapingThunk, specialPurpose, inlineStrategy, + isWithoutActuallyEscapingThunk, specialPurpose, inlineStrategy, optimizationMode, perfConstr, subclassScope, hasCReferences, effect, numAttrs, hasQualifiedOwnership, isWeakImported, LIST_VER_TUPLE_PIECES(available), - isDynamic, isExactSelfClass, isDistributed, funcTyID, replacedFunctionID, + isDynamic, isExactSelfClass, isDistributed, funcTyID, + replacedFunctionID, usedAdHocWitnessFunctionID, genericSigID, clangNodeOwnerID, SemanticsIDs); if (funcTyID == 0) { @@ -566,6 +568,13 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, replacedObjectiveCFunc = MF->getIdentifier(replacedFunctionID); } + SILFunction *usedAdHocWitnessFunction = nullptr; + if (usedAdHocWitnessFunctionID) { + auto usedAdHocWitnessFunctionStr = + MF->getIdentifier(usedAdHocWitnessFunctionID).str(); + usedAdHocWitnessFunction = getFuncForReference(usedAdHocWitnessFunctionStr); + } + auto linkageOpt = fromStableSILLinkage(rawLinkage); if (!linkageOpt) { LLVM_DEBUG(llvm::dbgs() << "invalid linkage code " << rawLinkage @@ -646,7 +655,7 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, fn->setTransparent(IsTransparent_t(isTransparent == 1)); fn->setSerialized(IsSerialized_t(isSerialized)); fn->setThunk(IsThunk_t(isThunk)); - fn->setWithoutActuallyEscapingThunk(bool(isWithoutactuallyEscapingThunk)); + fn->setWithoutActuallyEscapingThunk(bool(isWithoutActuallyEscapingThunk)); fn->setInlineStrategy(Inline_t(inlineStrategy)); fn->setSpecialPurpose(SILFunction::Purpose(specialPurpose)); fn->setEffectsKind(EffectsKind(effect)); @@ -670,6 +679,8 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, fn->setDynamicallyReplacedFunction(replacedFunction); if (!replacedObjectiveCFunc.empty()) fn->setObjCReplacement(replacedObjectiveCFunc); + if (usedAdHocWitnessFunction) + fn->setUsedAdHocRequirementWitnessFunction(usedAdHocWitnessFunction); if (clangNodeOwner) fn->setClangNodeOwner(clangNodeOwner); for (auto ID : SemanticsIDs) { @@ -2954,9 +2965,10 @@ bool SILDeserializer::hasSILFunction(StringRef Name, DeclID clangOwnerID; TypeID funcTyID; IdentifierID replacedFunctionID; + IdentifierID usedAdHocWitnessFunctionID; GenericSignatureID genericSigID; unsigned rawLinkage, isTransparent, isSerialized, isThunk, - isWithoutactuallyEscapingThunk, isGlobal, inlineStrategy, + isWithoutActuallyEscapingThunk, isGlobal, inlineStrategy, optimizationMode, perfConstr, subclassScope, hasCReferences, effect, numSpecAttrs, hasQualifiedOwnership, isWeakImported, LIST_VER_TUPLE_PIECES(available), @@ -2964,11 +2976,12 @@ bool SILDeserializer::hasSILFunction(StringRef Name, ArrayRef SemanticsIDs; SILFunctionLayout::readRecord( scratch, rawLinkage, isTransparent, isSerialized, isThunk, - isWithoutactuallyEscapingThunk, isGlobal, inlineStrategy, + isWithoutActuallyEscapingThunk, isGlobal, inlineStrategy, optimizationMode, perfConstr, subclassScope, hasCReferences, effect, numSpecAttrs, hasQualifiedOwnership, isWeakImported, LIST_VER_TUPLE_PIECES(available), - isDynamic, isExactSelfClass, isDistributed, funcTyID, replacedFunctionID, + isDynamic, isExactSelfClass, isDistributed, funcTyID, + replacedFunctionID, usedAdHocWitnessFunctionID, genericSigID, clangOwnerID, SemanticsIDs); auto linkage = fromStableSILLinkage(rawLinkage); if (!linkage) { diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 2775a4a0679ae..26d81deeb994f 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 = 686; // async let bit encoded in any_pattern +const uint16_t SWIFTMODULE_VERSION_MINOR = 687; // AdHocRequirementWitness info in SILFunctionLayout /// 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 c09024e80c982..762d55960c868 100644 --- a/lib/Serialization/SILFormat.h +++ b/lib/Serialization/SILFormat.h @@ -284,6 +284,7 @@ namespace sil_block { BCFixed<1>, // is distributed TypeIDField, // SILFunctionType DeclIDField, // SILFunction name or 0 (replaced function) + DeclIDField, // SILFunction name or 0 (used ad-hoc requirement witness function) // NEW GenericSignatureIDField, DeclIDField, // ClangNode owner BCArray // Semantics Attribute diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp index e816999337443..71b508e59644d 100644 --- a/lib/Serialization/SerializeSIL.cpp +++ b/lib/Serialization/SerializeSIL.cpp @@ -462,12 +462,17 @@ void SILSerializer::writeSILFunction(const SILFunction &F, bool DeclOnly) { if (auto *fun = F.getDynamicallyReplacedFunction()) { addReferencedSILFunction(fun, true); replacedFunctionID = S.addUniquedStringRef(fun->getName()); - } - else if (F.hasObjCReplacement()) { + } else if (F.hasObjCReplacement()) { replacedFunctionID = S.addUniquedStringRef(F.getObjCReplacement().str()); } + IdentifierID usedAdHocWitnessFunctionID = 0; + if (auto *fun = F.getUsedAdHocRequirementWitnessFunction()) { + addReferencedSILFunction(fun, true); + usedAdHocWitnessFunctionID = S.addUniquedStringRef(fun->getName()); + } + unsigned numAttrs = NoBody ? 0 : F.getSpecializeAttrs().size(); auto resilience = F.getModule().getSwiftModule()->getResilienceStrategy(); @@ -495,9 +500,11 @@ void SILSerializer::writeSILFunction(const SILFunction &F, bool DeclOnly) { (unsigned)F.hasCReferences(), (unsigned)F.getEffectsKind(), (unsigned)numAttrs, (unsigned)F.hasOwnership(), F.isAlwaysWeakImported(), LIST_VER_TUPLE_PIECES(available), - (unsigned)F.isDynamicallyReplaceable(), (unsigned)F.isExactSelfClass(), - (unsigned)F.isDistributed(), FnID, replacedFunctionID, genericSigID, - clangNodeOwnerID, SemanticsIDs); + (unsigned)F.isDynamicallyReplaceable(), + (unsigned)F.isExactSelfClass(), + (unsigned)F.isDistributed(), + FnID, replacedFunctionID, usedAdHocWitnessFunctionID, + genericSigID, clangNodeOwnerID, SemanticsIDs); F.visitArgEffects( [&](int effectIdx, bool isDerived, SILFunction::ArgEffectKind) { diff --git a/stdlib/public/Distributed/DistributedActorSystem.swift b/stdlib/public/Distributed/DistributedActorSystem.swift index 65fcf9c302a77..422a2f76144d3 100644 --- a/stdlib/public/Distributed/DistributedActorSystem.swift +++ b/stdlib/public/Distributed/DistributedActorSystem.swift @@ -27,10 +27,11 @@ public protocol DistributedActorSystem: Sendable { associatedtype ResultHandler: DistributedTargetInvocationResultHandler /// The serialization requirement that will be applied to all distributed targets used with this system. + // TODO: constrain SerializationRequirement in typesystem to only be ok with protocol or class here associatedtype SerializationRequirement where SerializationRequirement == InvocationEncoder.SerializationRequirement, - SerializationRequirement == InvocationDecoder.SerializationRequirement, - SerializationRequirement == ResultHandler.SerializationRequirement + SerializationRequirement == InvocationDecoder.SerializationRequirement, + SerializationRequirement == ResultHandler.SerializationRequirement // ==== --------------------------------------------------------------------- // - MARK: Resolving actors by identity diff --git a/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift b/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift index 094a9e06f1807..90c108c1f38f5 100644 --- a/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift +++ b/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift @@ -170,7 +170,7 @@ public struct LocalTestingInvocationEncoder: DistributedTargetInvocationEncoder } @available(SwiftStdlib 5.7, *) -public class LocalTestingInvocationDecoder : DistributedTargetInvocationDecoder { +public final class LocalTestingInvocationDecoder: DistributedTargetInvocationDecoder { public typealias SerializationRequirement = Codable public func decodeGenericSubstitutions() throws -> [Any.Type] { diff --git a/test/Distributed/Inputs/FakeDistributedActorSystems.swift b/test/Distributed/Inputs/FakeDistributedActorSystems.swift index f864ac3929f84..b37293e2f6248 100644 --- a/test/Distributed/Inputs/FakeDistributedActorSystems.swift +++ b/test/Distributed/Inputs/FakeDistributedActorSystems.swift @@ -36,6 +36,7 @@ public struct ActorAddress: Hashable, Sendable, Codable { // ==== Noop Transport --------------------------------------------------------- +@available(SwiftStdlib 5.7, *) public struct FakeActorSystem: DistributedActorSystem, CustomStringConvertible { public typealias ActorID = ActorAddress public typealias InvocationDecoder = FakeInvocationDecoder @@ -111,6 +112,7 @@ public struct FakeActorSystem: DistributedActorSystem, CustomStringConvertible { // ==== Fake Roundtrip Transport ----------------------------------------------- // TODO(distributed): not thread safe... +@available(SwiftStdlib 5.7, *) public final class FakeRoundtripActorSystem: DistributedActorSystem, @unchecked Sendable { public typealias ActorID = ActorAddress public typealias InvocationEncoder = FakeInvocationEncoder @@ -256,6 +258,7 @@ public final class FakeRoundtripActorSystem: DistributedActorSystem, @unchecked } +@available(SwiftStdlib 5.7, *) public struct FakeInvocationEncoder : DistributedTargetInvocationEncoder { public typealias SerializationRequirement = Codable @@ -300,7 +303,9 @@ public struct FakeInvocationEncoder : DistributedTargetInvocationEncoder { } // === decoding -------------------------------------------------------------- -public class FakeInvocationDecoder : DistributedTargetInvocationDecoder { + +@available(SwiftStdlib 5.7, *) +public final class FakeInvocationDecoder: DistributedTargetInvocationDecoder { public typealias SerializationRequirement = Codable var genericSubs: [Any.Type] = [] @@ -353,7 +358,7 @@ public class FakeInvocationDecoder : DistributedTargetInvocationDecoder { } } -@available(SwiftStdlib 5.5, *) +@available(SwiftStdlib 5.7, *) public struct FakeRoundtripResultHandler: DistributedTargetInvocationResultHandler { public typealias SerializationRequirement = Codable @@ -382,9 +387,11 @@ public struct FakeRoundtripResultHandler: DistributedTargetInvocationResultHandl // ==== Helpers ---------------------------------------------------------------- +@available(SwiftStdlib 5.7, *) @_silgen_name("swift_distributed_actor_is_remote") func __isRemoteActor(_ actor: AnyObject) -> Bool +@available(SwiftStdlib 5.7, *) func __isLocalActor(_ actor: AnyObject) -> Bool { return !__isRemoteActor(actor) } diff --git a/test/Distributed/Runtime/distributed_actor_cross_module_final_class_adhoc_requirement_not_optimized_away.swift b/test/Distributed/Runtime/distributed_actor_cross_module_final_class_adhoc_requirement_not_optimized_away.swift new file mode 100644 index 0000000000000..03767a199b30d --- /dev/null +++ b/test/Distributed/Runtime/distributed_actor_cross_module_final_class_adhoc_requirement_not_optimized_away.swift @@ -0,0 +1,51 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -target %target-cpu-apple-macosx10.15 -parse-as-library -emit-library -emit-module-path %t/FakeDistributedActorSystems.swiftmodule -module-name FakeDistributedActorSystems %S/../Inputs/FakeDistributedActorSystems.swift -o %t/%target-library-name(FakeDistributedActorSystems) +// RUN: %target-build-swift -target %target-cpu-apple-macosx10.15 -parse-as-library -lFakeDistributedActorSystems -module-name main -I %t -L %t %s -o %t/a.out +// RUN: %target-run %t/a.out | %FileCheck %s --color + +// REQUIRES: OS=macosx && (CPU=x86_64 || CPU=arm64) +// REQUIRES: executable_test +// REQUIRES: concurrency +// REQUIRES: distributed + +// rdar://76038845 +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: back_deployment_runtime + +// FIXME(distributed): Distributed actors currently have some issues on windows, isRemote always returns false. rdar://82593574 +// UNSUPPORTED: windows + +import Distributed +import FakeDistributedActorSystems + +@available(SwiftStdlib 5.7, *) +typealias DefaultDistributedActorSystem = FakeRoundtripActorSystem + +@available(SwiftStdlib 5.7, *) +distributed actor Greeter { + distributed func hello() -> String { + return "Hello, World!" + } +} + +@available(SwiftStdlib 5.7, *) +func test() async throws { + let system = DefaultDistributedActorSystem() + + let local = Greeter(actorSystem: system) + let ref = try Greeter.resolve(id: local.id, using: system) + + let response = try await ref.hello() + // CHECK: >> remoteCall: on:main.Greeter, target:main.Greeter.hello(), invocation:FakeInvocationEncoder(genericSubs: [], arguments: [], returnType: Optional(Swift.String), errorType: nil), throwing:Swift.Never, returning:Swift.String + + print("response: \(response)") + // CHECK: response: Hello, World! + +} + +@available(SwiftStdlib 5.7, *) +@main struct Main { + static func main() async { + try! await test() + } +} From 870c712bd0ee66ef85e623c4a577c2a9871c21c2 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Wed, 13 Apr 2022 16:34:34 +0900 Subject: [PATCH 2/8] fix protocols --- lib/AST/DistributedDecl.cpp | 4 ++++ lib/SIL/IR/SILFunctionBuilder.cpp | 5 ++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/AST/DistributedDecl.cpp b/lib/AST/DistributedDecl.cpp index 84b88306879d8..26d9e6b308612 100644 --- a/lib/AST/DistributedDecl.cpp +++ b/lib/AST/DistributedDecl.cpp @@ -97,6 +97,10 @@ Type swift::getConcreteReplacementForProtocolActorSystemType(ValueDecl *member) } Type swift::getDistributedActorSystemType(NominalTypeDecl *actor) { + assert(!dyn_cast(actor) && + "Use getConcreteReplacementForProtocolActorSystemType instead to get" + "the concrete ActorSystem, if bound, for this DistributedActor " + "constrained ProtocolDecl!"); assert(actor->isDistributedActor()); auto &C = actor->getASTContext(); diff --git a/lib/SIL/IR/SILFunctionBuilder.cpp b/lib/SIL/IR/SILFunctionBuilder.cpp index 94d420bd3fc1f..0968e28526a5c 100644 --- a/lib/SIL/IR/SILFunctionBuilder.cpp +++ b/lib/SIL/IR/SILFunctionBuilder.cpp @@ -226,11 +226,10 @@ void SILFunctionBuilder::addFunctionAttributes( fprintf(stderr, "[%s:%d] (%s) IS DIST THUNK!\n", __FILE__, __LINE__, __FUNCTION__); constant.dump(); - // TODO: also handle protocol / extension - auto actor = dyn_cast(decl->getDeclContext()); + auto *actor = decl->getDeclContext()->getSelfNominalTypeDecl(); if (actor && actor->isDistributedActor()) { auto &C = decl->getASTContext(); - auto systemTy = getDistributedActorSystemType(actor); + auto systemTy = getConcreteReplacementForProtocolActorSystemType(decl); assert(systemTy); auto decoderTy = From dadf3011f98c182b26a20c2e5f4f370659f17d6f Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Wed, 13 Apr 2022 17:03:33 +0900 Subject: [PATCH 3/8] cleanups --- include/swift/AST/Decl.h | 11 ---- include/swift/AST/DiagnosticsParse.def | 2 + include/swift/SIL/SILDeclRef.h | 7 +-- include/swift/SIL/SILFunction.h | 53 +++++++------------ lib/AST/Decl.cpp | 4 -- lib/SIL/IR/SILDeclRef.cpp | 8 --- lib/SIL/IR/SILFunctionBuilder.cpp | 7 +-- lib/SIL/IR/SILModule.cpp | 4 +- lib/SIL/IR/SILPrinter.cpp | 5 +- lib/SIL/Parser/ParseSIL.cpp | 10 ++-- lib/Sema/TypeCheckDistributed.cpp | 9 +--- lib/Serialization/DeserializeSIL.cpp | 2 +- lib/Serialization/ModuleFormat.h | 2 +- lib/Serialization/SILFormat.h | 2 +- lib/Serialization/SerializeSIL.cpp | 2 +- .../Inputs/FakeDistributedActorSystems.swift | 3 ++ ...tributed_actor_accessor_thunks_64bit.swift | 2 +- test/SILGen/distributed_thunk.swift | 4 +- 18 files changed, 40 insertions(+), 97 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 0130ec9c1e19c..ff2e73923df9d 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -2258,9 +2258,6 @@ class ValueDecl : public Decl { /// Whether this declaration produces an implicitly unwrapped /// optional result. unsigned isIUO : 1; -// -// /// Whether this declaration is a witness to an ad-hoc requirement. -// unsigned isAdHocRequirementWitness : 1; } LazySemanticInfo = { }; friend class DynamicallyReplacedDeclRequest; @@ -2598,14 +2595,6 @@ class ValueDecl : public Decl { /// Returns true if this decl can be found by id-style dynamic lookup. bool canBeAccessedByDynamicLookup() const; -// /// Set whether this declaration is a witness to an ad-hoc requirement or not. -// void setIsAdHocRequirementWitness(bool value); -// -// /// Whether this declaration is a witness to an ad-hoc requirement. -// bool isAdHocRequirementWitness() const { -// return LazySemanticInfo.isAdHocRequirementWitness; -// } - /// Returns true if this declaration has an implicitly unwrapped optional /// result. The precise meaning depends on the declaration kind: /// - for properties, the value is IUO diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index fe02f3f491ae2..2267f88752ee2 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -680,6 +680,8 @@ ERROR(expected_sil_function_type, none, "sil function expected to have SIL function type", ()) ERROR(sil_dynamically_replaced_func_not_found,none, "dynamically replaced function not found %0", (Identifier)) +ERROR(sil_adhoc_requirement_witness_func_not_found,none, + "ad-hoc requirement witness function not found %0", (Identifier)) ERROR(sil_specialize_target_func_not_found,none, "_specialize target function not found %0", (Identifier)) ERROR(sil_availability_expected_version,none, diff --git a/include/swift/SIL/SILDeclRef.h b/include/swift/SIL/SILDeclRef.h index e92ddac6d764c..930037fefed90 100644 --- a/include/swift/SIL/SILDeclRef.h +++ b/include/swift/SIL/SILDeclRef.h @@ -555,12 +555,7 @@ struct SILDeclRef { bool canBeDynamicReplacement() const; -// /// True if the function was a witness to an ad-hoc requirement. -// /// Useful in order to e.g. not optimize away the function even -// /// if it seems not-used to the optimizer based on information in SIL. -// bool isAdHocRequirementWitness() const; - - bool isAutoDiffDerivativeFunction() const { + bool isAutoDiffDerivativeFunction() const { return pointer.is() && pointer.get() != nullptr; } diff --git a/include/swift/SIL/SILFunction.h b/include/swift/SIL/SILFunction.h index 05e2aa1afd05b..91bd00948a1f1 100644 --- a/include/swift/SIL/SILFunction.h +++ b/include/swift/SIL/SILFunction.h @@ -55,10 +55,6 @@ enum IsDynamicallyReplaceable_t { IsNotDynamic, IsDynamic }; -//enum IsAdHocRequirementWitness_t { -// IsNotAdHocWitness, -// IsAdHocWitness -//}; enum IsExactSelfClass_t { IsNotExactSelfClass, IsExactSelfClass, @@ -216,14 +212,16 @@ class SILFunction /// @_dynamicReplacement(for:) function. SILFunction *ReplacedFunction = nullptr; - /// Ad-hoc requirements may need to be retained explicitly, such that they - /// do not get optimized away as it might seem tha they are un-used. + /// This SILFunction REFerences an ad-hoc protocol requirement witness in + /// order to keep it alive, such that it main be obtained in IRGen. Without + /// this explicit reference, the witness would seem not-used, and not be + /// accessible for IRGen. /// - /// Specifically, e.g. the DistributedTargetInvocationDecoder's - /// 'decodeNextArgument' must be retained, as it is only used from IRGen + /// Specifically, one such case is the DistributedTargetInvocationDecoder's + /// 'decodeNextArgument' which must be retained, as it is only used from IRGen /// and such, appears as-if unused in SIL and would get optimized away. - // TODO: rename: referenced ad hoc requirement witnesses - SILFunction *AdHocRequirementFunction = nullptr; + // TODO: Consider making this a general "references adhoc functions" and make it an array? + SILFunction *RefAdHocRequirementFunction = nullptr; Identifier ObjCReplacementFor; @@ -363,9 +361,6 @@ class SILFunction /// The function is in a statically linked module. unsigned IsStaticallyLinked : 1; -// /// The function is a witness to an ad-hoc requirement. -// unsigned IsAdHocRequirementWitness : 1; - static void validateSubclassScope(SubclassScope scope, IsThunk_t isThunk, const GenericSpecializationInformation *genericInfo) { @@ -490,28 +485,25 @@ class SILFunction ReplacedFunction = nullptr; } - SILFunction *getUsedAdHocRequirementWitnessFunction() const { - return AdHocRequirementFunction; + SILFunction *getReferencedAdHocRequirementWitnessFunction() const { + return RefAdHocRequirementFunction; } // Marks that this `SILFunction` uses the passed in ad-hoc protocol // requirement witness `f` and therefore must retain it explicitly, // otherwise we might not be able to get a reference to it. - void setUsedAdHocRequirementWitnessFunction(SILFunction *f) { - assert(AdHocRequirementFunction == nullptr && "already set"); - - fprintf(stderr, "[%s:%d] (%s) SET AD HOC WITNESS [%s] ON [%s]\n", __FILE__, __LINE__, __FUNCTION__, f->getName().str().c_str(), this->getName().str().c_str()); - f->dump(); + void setReferencedAdHocRequirementWitnessFunction(SILFunction *f) { + assert(RefAdHocRequirementFunction == nullptr && "already set"); if (f == nullptr) return; - AdHocRequirementFunction = f; - AdHocRequirementFunction->incrementRefCount(); + RefAdHocRequirementFunction = f; + RefAdHocRequirementFunction->incrementRefCount(); } - void dropAdHocRequirementFunction() { - if (!AdHocRequirementFunction) + void dropReferencedAdHocRequirementWitnessFunction() { + if (!RefAdHocRequirementFunction) return; - AdHocRequirementFunction->decrementRefCount(); - AdHocRequirementFunction = nullptr; + RefAdHocRequirementFunction->decrementRefCount(); + RefAdHocRequirementFunction = nullptr; } bool hasObjCReplacement() const { @@ -789,15 +781,6 @@ class SILFunction assert(!Transparent || !IsDynamicReplaceable); } - /// Returns whether this function implementation can be dynamically replaced. -// IsAdHocRequirementWitness_t isAdHocRequirementWitness() const { -// return IsAdHocRequirementWitness_t(IsAdHocRequirementWitness); -// } -// void setIsAdHocRequirementWitness(IsAdHocRequirementWitness_t value = IsAdHocWitness) { -// IsAdHocRequirementWitness = value; -// assert(!IsDynamicReplaceable); -// } - IsExactSelfClass_t isExactSelfClass() const { return IsExactSelfClass_t(ExactSelfClass); } diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index af6bcb9ca9449..d311173ffa2ed 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -3167,10 +3167,6 @@ void ValueDecl::setIsDynamic(bool value) { LazySemanticInfo.isDynamic = value; } -//void ValueDecl::setIsAdHocRequirementWitness(bool value) { -// LazySemanticInfo.isAdHocRequirementWitness = true; -//} - ValueDecl *ValueDecl::getDynamicallyReplacedDecl() const { return evaluateOrDefault(getASTContext().evaluator, DynamicallyReplacedDeclRequest{ diff --git a/lib/SIL/IR/SILDeclRef.cpp b/lib/SIL/IR/SILDeclRef.cpp index cd66123b0be42..c99ac31751aac 100644 --- a/lib/SIL/IR/SILDeclRef.cpp +++ b/lib/SIL/IR/SILDeclRef.cpp @@ -1382,14 +1382,6 @@ bool SILDeclRef::canBeDynamicReplacement() const { return true; } -//bool SILDeclRef::isAdHocRequirementWitness() const { -// auto decl = getDecl(); -// if (!decl) -// return false; -// -// return decl->isAdHocRequirementWitness(); -//} - bool SILDeclRef::isDynamicallyReplaceable() const { // The non-foreign entry of a @dynamicReplacement(for:) of @objc method in a // generic class can't be a dynamically replaced. diff --git a/lib/SIL/IR/SILFunctionBuilder.cpp b/lib/SIL/IR/SILFunctionBuilder.cpp index 0968e28526a5c..22b8faa0ed539 100644 --- a/lib/SIL/IR/SILFunctionBuilder.cpp +++ b/lib/SIL/IR/SILFunctionBuilder.cpp @@ -223,9 +223,6 @@ void SILFunctionBuilder::addFunctionAttributes( } if (constant.isDistributedThunk()) { - fprintf(stderr, "[%s:%d] (%s) IS DIST THUNK!\n", __FILE__, __LINE__, __FUNCTION__); - constant.dump(); - auto *actor = decl->getDeclContext()->getSelfNominalTypeDecl(); if (actor && actor->isDistributedActor()) { auto &C = decl->getASTContext(); @@ -241,9 +238,7 @@ void SILFunctionBuilder::addFunctionAttributes( decoderTy->getAnyNominal()); auto decodeRef = SILDeclRef(decodeFunc); auto *adHocWitness = getOrCreateDeclaration(decodeFunc, decodeRef); - F->setUsedAdHocRequirementWitnessFunction(adHocWitness); - - F->dump(); + F->setReferencedAdHocRequirementWitnessFunction(adHocWitness); } } } diff --git a/lib/SIL/IR/SILModule.cpp b/lib/SIL/IR/SILModule.cpp index 631b15f21b77e..7f4d95f57d099 100644 --- a/lib/SIL/IR/SILModule.cpp +++ b/lib/SIL/IR/SILModule.cpp @@ -138,7 +138,7 @@ SILModule::~SILModule() { for (SILFunction &F : *this) { F.dropAllReferences(); F.dropDynamicallyReplacedFunction(); - F.dropAdHocRequirementFunction(); + F.dropReferencedAdHocRequirementWitnessFunction(); F.clearSpecializeAttrs(); } @@ -468,7 +468,7 @@ void SILModule::eraseFunction(SILFunction *F) { // (References are not needed anymore.) F->clear(); F->dropDynamicallyReplacedFunction(); - F->dropAdHocRequirementFunction(); + F->dropReferencedAdHocRequirementWitnessFunction(); // Drop references for any _specialize(target:) functions. F->clearSpecializeAttrs(); } diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index 1eb57a21f35ed..06868d3fd0e9f 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -2983,11 +2983,10 @@ void SILFunction::print(SILPrintContext &PrintCtx) const { OS << "\"] "; } - if (auto *usedFunc = getUsedAdHocRequirementWitnessFunction()) { - OS << "[used_adhoc_requirement_witness \""; + if (auto *usedFunc = getReferencedAdHocRequirementWitnessFunction()) { + OS << "[ref_adhoc_requirement_witness \""; OS << usedFunc->getName(); OS << "\"] "; - fprintf(stderr, "[%s:%d] (%s) PRINTED THE used_adhoc_requirement_witness...\n", __FILE__, __LINE__, __FUNCTION__); } if (hasObjCReplacement()) { diff --git a/lib/SIL/Parser/ParseSIL.cpp b/lib/SIL/Parser/ParseSIL.cpp index fcf79b46f6355..0396343828c55 100644 --- a/lib/SIL/Parser/ParseSIL.cpp +++ b/lib/SIL/Parser/ParseSIL.cpp @@ -1090,8 +1090,7 @@ static bool parseDeclSILOptional(bool *isTransparent, SP.P.parseToken(tok::r_square, diag::expected_in_attribute_list); continue; - } else if (usedAdHocRequirementWitness && SP.P.Tok.getText() == "used_adhoc_requirement_witness") { - fprintf(stderr, "[%s:%d] (%s) PARSING THE... used_adhoc_requirement_witness\n", __FILE__, __LINE__, __FUNCTION__); + } else if (usedAdHocRequirementWitness && SP.P.Tok.getText() == "ref_adhoc_requirement_witness") { SP.P.consumeToken(tok::identifier); if (SP.P.Tok.getKind() != tok::string_literal) { SP.P.diagnose(SP.P.Tok, diag::expected_in_attribute_list); @@ -1102,7 +1101,7 @@ static bool parseDeclSILOptional(bool *isTransparent, SILFunction *Func = M.lookUpFunction(witnessFunc.str()); if (!Func) { Identifier Id = SP.P.Context.getIdentifier(witnessFunc); - SP.P.diagnose(SP.P.Tok, diag::sil_dynamically_replaced_func_not_found, // FIXME: bettererror + SP.P.diagnose(SP.P.Tok, diag::sil_adhoc_requirement_witness_func_not_found, Id); return true; } @@ -6401,10 +6400,7 @@ bool SILParserState::parseDeclSIL(Parser &P) { FunctionState.F->setIsExactSelfClass(isExactSelfClass); FunctionState.F->setDynamicallyReplacedFunction( DynamicallyReplacedFunction); - if (AdHocWitnessFunction) { - fprintf(stderr, "[%s:%d] (%s) PARSED AdHocWitnessFunction\n", __FILE__, __LINE__, __FUNCTION__); - } - FunctionState.F->setUsedAdHocRequirementWitnessFunction( + FunctionState.F->setReferencedAdHocRequirementWitnessFunction( AdHocWitnessFunction); if (!objCReplacementFor.empty()) FunctionState.F->setObjCReplacement(objCReplacementFor); diff --git a/lib/Sema/TypeCheckDistributed.cpp b/lib/Sema/TypeCheckDistributed.cpp index d767deb65714c..063700e0956a6 100644 --- a/lib/Sema/TypeCheckDistributed.cpp +++ b/lib/Sema/TypeCheckDistributed.cpp @@ -72,15 +72,8 @@ static AbstractFunctionDecl *findDistributedAdHocRequirement( for (auto value : decl->lookupDirect(identifier)) { auto func = dyn_cast(value); - if (func && matchFn(func)) { - // In order to prevent an "unused" (from SIL's perspective) function - // from being optimized away, we mark it as an ad-hoc requirement witness - // which is then picked up in SIL and causes the function to be retained. -// fprintf(stderr, "[%s:%d] (%s) FOUND ADHOC WITNESS: %s\n", __FILE__, __LINE__, __FUNCTION__, func->getNameStr().str().c_str()); -// -// func->setIsAdHocRequirementWitness(true); + if (func && matchFn(func)) return func; - } } return nullptr; diff --git a/lib/Serialization/DeserializeSIL.cpp b/lib/Serialization/DeserializeSIL.cpp index 45a3b5dcc662e..833bec1f14c17 100644 --- a/lib/Serialization/DeserializeSIL.cpp +++ b/lib/Serialization/DeserializeSIL.cpp @@ -680,7 +680,7 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, if (!replacedObjectiveCFunc.empty()) fn->setObjCReplacement(replacedObjectiveCFunc); if (usedAdHocWitnessFunction) - fn->setUsedAdHocRequirementWitnessFunction(usedAdHocWitnessFunction); + fn->setReferencedAdHocRequirementWitnessFunction(usedAdHocWitnessFunction); if (clangNodeOwner) fn->setClangNodeOwner(clangNodeOwner); for (auto ID : SemanticsIDs) { diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 26d81deeb994f..4a7dde336c627 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 = 687; // AdHocRequirementWitness info in SILFunctionLayout +const uint16_t SWIFTMODULE_VERSION_MINOR = 687; // RefAdHocRequirementFunction info in SILFunctionLayout /// 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 762d55960c868..2af2716d589d6 100644 --- a/lib/Serialization/SILFormat.h +++ b/lib/Serialization/SILFormat.h @@ -284,7 +284,7 @@ namespace sil_block { BCFixed<1>, // is distributed TypeIDField, // SILFunctionType DeclIDField, // SILFunction name or 0 (replaced function) - DeclIDField, // SILFunction name or 0 (used ad-hoc requirement witness function) // NEW + DeclIDField, // SILFunction name or 0 (used ad-hoc requirement witness function) GenericSignatureIDField, DeclIDField, // ClangNode owner BCArray // Semantics Attribute diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp index 71b508e59644d..07d26c8e88d85 100644 --- a/lib/Serialization/SerializeSIL.cpp +++ b/lib/Serialization/SerializeSIL.cpp @@ -468,7 +468,7 @@ void SILSerializer::writeSILFunction(const SILFunction &F, bool DeclOnly) { } IdentifierID usedAdHocWitnessFunctionID = 0; - if (auto *fun = F.getUsedAdHocRequirementWitnessFunction()) { + if (auto *fun = F.getReferencedAdHocRequirementWitnessFunction()) { addReferencedSILFunction(fun, true); usedAdHocWitnessFunctionID = S.addUniquedStringRef(fun->getName()); } diff --git a/test/Distributed/Inputs/FakeDistributedActorSystems.swift b/test/Distributed/Inputs/FakeDistributedActorSystems.swift index b37293e2f6248..8ee09b9a1bae9 100644 --- a/test/Distributed/Inputs/FakeDistributedActorSystems.swift +++ b/test/Distributed/Inputs/FakeDistributedActorSystems.swift @@ -304,6 +304,9 @@ public struct FakeInvocationEncoder : DistributedTargetInvocationEncoder { // === decoding -------------------------------------------------------------- +// !!! WARNING !!! +// This is a 'final class' on purpose, to see that we retain the ad-hoc witness +// for 'decodeNextArgument'; Do not change it to just a class! @available(SwiftStdlib 5.7, *) public final class FakeInvocationDecoder: DistributedTargetInvocationDecoder { public typealias SerializationRequirement = Codable diff --git a/test/Distributed/distributed_actor_accessor_thunks_64bit.swift b/test/Distributed/distributed_actor_accessor_thunks_64bit.swift index 0902003307832..7395a9f74a1fb 100644 --- a/test/Distributed/distributed_actor_accessor_thunks_64bit.swift +++ b/test/Distributed/distributed_actor_accessor_thunks_64bit.swift @@ -1,6 +1,6 @@ // 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 -disable-availability-checking -I %t 2>&1 %s | %IRGenFileCheck %s -check-prefix CHECK-%target-import-type +// RUN: %target-swift-frontend -module-name distributed_actor_accessors -emit-irgen -disable-availability-checking -I %t 2>&1 %s | %IRGenFileCheck %s -check-prefix CHECK-%target-import-type --dump-input=always // UNSUPPORTED: back_deploy_concurrency // REQUIRES: concurrency diff --git a/test/SILGen/distributed_thunk.swift b/test/SILGen/distributed_thunk.swift index 74185c37817e9..e0b0dd8f26e5f 100644 --- a/test/SILGen/distributed_thunk.swift +++ b/test/SILGen/distributed_thunk.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-emit-silgen %s -enable-experimental-distributed -disable-availability-checking | %FileCheck %s --dump-input=fail +// RUN: %target-swift-emit-silgen %s -enable-experimental-distributed -disable-availability-checking | %FileCheck %s // REQUIRES: concurrency // REQUIRES: distributed @@ -9,7 +9,7 @@ distributed actor DA { } extension DA { - // CHECK-LABEL: sil hidden [thunk] [distributed] [ossa] @$s17distributed_thunk2DAC1fyyYaKFTE : $@convention(method) @async (@guaranteed DA) -> @error Error + // CHECK-LABEL: sil hidden [thunk] [distributed] [ref_adhoc_requirement_witness "$s11Distributed29LocalTestingInvocationDecoderC18decodeNextArgumentxyKSeRzSERzlF"] [ossa] @$s17distributed_thunk2DAC1fyyYaKFTE : $@convention(method) @async (@guaranteed DA) -> @error Error { // CHECK: function_ref @swift_distributed_actor_is_remote // Call the actor function From 4e2c059e209eb351779e5c89258f19155badf7c2 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Thu, 14 Apr 2022 11:55:21 +0900 Subject: [PATCH 4/8] review feedback, cleanup getting decode func --- include/swift/AST/Decl.h | 4 ++-- include/swift/AST/DistributedDecl.h | 7 +++++++ lib/AST/DistributedDecl.cpp | 26 ++++++++++++++++++++++++++ lib/IRGen/GenDistributed.cpp | 1 + lib/SIL/IR/SILFunctionBuilder.cpp | 27 ++++++++------------------- 5 files changed, 44 insertions(+), 21 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index ff2e73923df9d..95359aceee559 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -3564,10 +3564,10 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext { VarDecl *getDistributedActorIDProperty() const; /// Find the 'RemoteCallTarget.init(_:)' initializer function. - ConstructorDecl* getDistributedRemoteCallTargetInitFunction() const; + ConstructorDecl *getDistributedRemoteCallTargetInitFunction() const; /// Find the 'RemoteCallArgument(label:name:value:)' initializer function. - ConstructorDecl* getDistributedRemoteCallArgumentInitFunction() const; + ConstructorDecl *getDistributedRemoteCallArgumentInitFunction() const; /// Collect the set of protocols to which this type should implicitly /// conform, such as AnyObject (for classes). diff --git a/include/swift/AST/DistributedDecl.h b/include/swift/AST/DistributedDecl.h index e3eb91cfcefa3..fe4ac4fdfb0d1 100644 --- a/include/swift/AST/DistributedDecl.h +++ b/include/swift/AST/DistributedDecl.h @@ -53,6 +53,13 @@ Type getDistributedActorIDType(NominalTypeDecl *actor); Type getDistributedSerializationRequirementType( NominalTypeDecl *nominal, ProtocolDecl *protocol); +/// Given a distributed thunk declaration, inside a 'distributed actor', +/// finds the ad-hoc witness for 'decodeNextArgument' on the associated +/// 'ActorSystem.InvocationDecoder' of the actor, or null. +AbstractFunctionDecl * +getAssociatedDistributedInvocationDecoderDecodeNextArgumentFunction( + ValueDecl *thunk); + /// Get the specific 'InvocationEncoder' type of a specific distributed actor /// system. Type getDistributedActorSystemInvocationEncoderType(NominalTypeDecl *system); diff --git a/lib/AST/DistributedDecl.cpp b/lib/AST/DistributedDecl.cpp index 26d9e6b308612..854ad41e608a4 100644 --- a/lib/AST/DistributedDecl.cpp +++ b/lib/AST/DistributedDecl.cpp @@ -203,6 +203,32 @@ Type swift::getDistributedSerializationRequirementType( return conformance.getTypeWitnessByName(selfType, ctx.Id_SerializationRequirement); } +AbstractFunctionDecl * +swift::getAssociatedDistributedInvocationDecoderDecodeNextArgumentFunction( + ValueDecl *thunk) { + assert(thunk); + auto &C = thunk->getASTContext(); + + auto *actor = thunk->getDeclContext()->getSelfNominalTypeDecl(); + if (!actor) + return nullptr; + if (!actor->isDistributedActor()) + return nullptr; + + auto systemTy = getConcreteReplacementForProtocolActorSystemType(thunk); + if (!systemTy) + return nullptr; + + auto decoderTy = + getDistributedActorSystemInvocationDecoderType( + systemTy->getAnyNominal()); + if (!decoderTy) + return nullptr; + + return C.getDecodeNextArgumentOnDistributedInvocationDecoder( + decoderTy->getAnyNominal()); +} + Type ASTContext::getAssociatedTypeOfDistributedSystemOfActor( NominalTypeDecl *actor, Identifier member) { auto &ctx = actor->getASTContext(); diff --git a/lib/IRGen/GenDistributed.cpp b/lib/IRGen/GenDistributed.cpp index b8af5db569682..9d6586381f4c7 100644 --- a/lib/IRGen/GenDistributed.cpp +++ b/lib/IRGen/GenDistributed.cpp @@ -813,6 +813,7 @@ ArgumentDecoderInfo DistributedAccessor::findArgumentDecoder( decoder = instance.claimNext(); } + // TODO(distributed): this can be removed most likely now if (isa(decoderDecl) || isa(decoderDecl) || decoderDecl->isFinal()) { auto *decodeSIL = IGM.getSILModule().lookUpFunction(SILDeclRef(decodeFn)); diff --git a/lib/SIL/IR/SILFunctionBuilder.cpp b/lib/SIL/IR/SILFunctionBuilder.cpp index 22b8faa0ed539..f48eec097b633 100644 --- a/lib/SIL/IR/SILFunctionBuilder.cpp +++ b/lib/SIL/IR/SILFunctionBuilder.cpp @@ -220,25 +220,14 @@ void SILFunctionBuilder::addFunctionAttributes( F->setDynamicallyReplacedFunction(replacedFunc); } - } - - if (constant.isDistributedThunk()) { - auto *actor = decl->getDeclContext()->getSelfNominalTypeDecl(); - if (actor && actor->isDistributedActor()) { - auto &C = decl->getASTContext(); - auto systemTy = getConcreteReplacementForProtocolActorSystemType(decl); - assert(systemTy); - - auto decoderTy = - getDistributedActorSystemInvocationDecoderType( - systemTy->getAnyNominal()); - assert(decoderTy); - - auto decodeFunc = C.getDecodeNextArgumentOnDistributedInvocationDecoder( - decoderTy->getAnyNominal()); - auto decodeRef = SILDeclRef(decodeFunc); - auto *adHocWitness = getOrCreateDeclaration(decodeFunc, decodeRef); - F->setReferencedAdHocRequirementWitnessFunction(adHocWitness); + } else if (constant.isDistributedThunk()) { + if (auto decodeFuncDecl = + getAssociatedDistributedInvocationDecoderDecodeNextArgumentFunction( + decl)) { + + auto decodeRef = SILDeclRef(decodeFuncDecl); + auto *adHocFunc = getOrCreateDeclaration(decodeFuncDecl, decodeRef); + F->setReferencedAdHocRequirementWitnessFunction(adHocFunc); } } } From 560e96fcbe9a1af31f97fdc92d10462b3ebd2a73 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Sat, 16 Apr 2022 11:40:14 +0900 Subject: [PATCH 5/8] [Distributed] Temporarily simplify SIL tests; avoid a few checks --- lib/SIL/IR/SILFunctionBuilder.cpp | 12 ++++++------ .../distributed_actor_accessor_thunks_32bit.swift | 11 ++--------- .../distributed_actor_accessor_thunks_64bit.swift | 12 +++--------- 3 files changed, 11 insertions(+), 24 deletions(-) diff --git a/lib/SIL/IR/SILFunctionBuilder.cpp b/lib/SIL/IR/SILFunctionBuilder.cpp index f48eec097b633..b53ccec81e574 100644 --- a/lib/SIL/IR/SILFunctionBuilder.cpp +++ b/lib/SIL/IR/SILFunctionBuilder.cpp @@ -221,14 +221,14 @@ void SILFunctionBuilder::addFunctionAttributes( F->setDynamicallyReplacedFunction(replacedFunc); } } else if (constant.isDistributedThunk()) { - if (auto decodeFuncDecl = + auto decodeFuncDecl = getAssociatedDistributedInvocationDecoderDecodeNextArgumentFunction( - decl)) { + decl); + assert(decodeFuncDecl && "decodeNextArgument function not found!"); - auto decodeRef = SILDeclRef(decodeFuncDecl); - auto *adHocFunc = getOrCreateDeclaration(decodeFuncDecl, decodeRef); - F->setReferencedAdHocRequirementWitnessFunction(adHocFunc); - } + auto decodeRef = SILDeclRef(decodeFuncDecl); + auto *adHocFunc = getOrCreateDeclaration(decodeFuncDecl, decodeRef); + F->setReferencedAdHocRequirementWitnessFunction(adHocFunc); } } diff --git a/test/Distributed/distributed_actor_accessor_thunks_32bit.swift b/test/Distributed/distributed_actor_accessor_thunks_32bit.swift index b5c353f68670c..b660ef727782c 100644 --- a/test/Distributed/distributed_actor_accessor_thunks_32bit.swift +++ b/test/Distributed/distributed_actor_accessor_thunks_32bit.swift @@ -100,16 +100,9 @@ public distributed actor MyOtherActor { // CHECK: [[DECODER_PTR:%*]] = bitcast %swift.opaque* %1 to %T27FakeDistributedActorSystems0A17InvocationDecoderC** // CHECK-NEXT: [[DECODER:%.*]] = load %T27FakeDistributedActorSystems0A17InvocationDecoderC*, %T27FakeDistributedActorSystems0A17InvocationDecoderC** [[DECODER_PTR]] -// CHECK-NEXT: [[DECODER_METADATA:%.*]] = bitcast %swift.type* [[DECODER_TYPE]] to void (%swift.opaque*, %swift.type*, i8**, i8**, %T27FakeDistributedActorSystems0A17InvocationDecoderC*, %swift.error**)** -// CHECK-NEXT: [[DECODE_NEXT_ARG_REF:%.*]] = getelementptr inbounds void (%swift.opaque*, %swift.type*, i8**, i8**, %T27FakeDistributedActorSystems0A17InvocationDecoderC*, %swift.error**)*, void (%swift.opaque*, %swift.type*, i8**, i8**, %T27FakeDistributedActorSystems0A17InvocationDecoderC*, %swift.error**)** [[DECODER_METADATA]], i32 35 -// CHECK-NEXT: [[DECODE_NEXT_ARG:%.*]] = load void (%swift.opaque*, %swift.type*, i8**, i8**, %T27FakeDistributedActorSystems0A17InvocationDecoderC*, %swift.error**)*, void (%swift.opaque*, %swift.type*, i8**, i8**, %T27FakeDistributedActorSystems0A17InvocationDecoderC*, %swift.error**)** [[DECODE_NEXT_ARG_REF]] +// CHECK-NEXT: [[DECODER_METADATA:%.*]] = bitcast i8* %2 to %swift.type* +// CHECK-NEXT: [[DECODE_NEXT_ARG_REF:%.*]] = getelementptr inbounds %swift.type*, %swift.type** [[DECODER_METADATA]], i32 - -// CHECK: [[ARG_TYPES:%.*]] = bitcast i8* %2 to %swift.type** - -// CHECK: [[ARG_0_TYPE_LOC:%.*]] = getelementptr inbounds %swift.type*, %swift.type** [[ARG_TYPES]], i32 0 -// CHECK-NEXT: %arg_type = load %swift.type*, %swift.type** [[ARG_0_TYPE_LOC]] -// CHECK: %size = load i32, i32* {{.*}} // CHECK: [[ARG_0_SIZE_ADJ:%.*]] = add i32 %size, 15 // CHECK-NEXT: [[ARG_0_SIZE:%.*]] = and i32 [[ARG_0_SIZE_ADJ]], -16 // CHECK-NEXT: [[ARG_0_VALUE_BUF:%.*]] = call swiftcc i8* @swift_task_alloc(i32 [[ARG_0_SIZE]]) diff --git a/test/Distributed/distributed_actor_accessor_thunks_64bit.swift b/test/Distributed/distributed_actor_accessor_thunks_64bit.swift index 7395a9f74a1fb..d6284a3748ae0 100644 --- a/test/Distributed/distributed_actor_accessor_thunks_64bit.swift +++ b/test/Distributed/distributed_actor_accessor_thunks_64bit.swift @@ -100,15 +100,9 @@ public distributed actor MyOtherActor { // CHECK: [[DECODER_PTR:%*]] = bitcast %swift.opaque* %1 to %T27FakeDistributedActorSystems0A17InvocationDecoderC** // CHECK-NEXT: [[DECODER:%.*]] = load %T27FakeDistributedActorSystems0A17InvocationDecoderC*, %T27FakeDistributedActorSystems0A17InvocationDecoderC** [[DECODER_PTR]] -// CHECK-NEXT: [[DECODER_METADATA:%.*]] = bitcast %swift.type* [[DECODER_TYPE]] to void (%swift.opaque*, %swift.type*, i8**, i8**, %T27FakeDistributedActorSystems0A17InvocationDecoderC*, %swift.error**)** -// CHECK-NEXT: [[DECODE_NEXT_ARG_REF:%.*]] = getelementptr inbounds void (%swift.opaque*, %swift.type*, i8**, i8**, %T27FakeDistributedActorSystems0A17InvocationDecoderC*, %swift.error**)*, void (%swift.opaque*, %swift.type*, i8**, i8**, %T27FakeDistributedActorSystems0A17InvocationDecoderC*, %swift.error**)** [[DECODER_METADATA]], i64 {{29|32}} -// CHECK-NEXT: [[DECODE_NEXT_ARG:%.*]] = load void (%swift.opaque*, %swift.type*, i8**, i8**, %T27FakeDistributedActorSystems0A17InvocationDecoderC*, %swift.error**)*, void (%swift.opaque*, %swift.type*, i8**, i8**, %T27FakeDistributedActorSystems0A17InvocationDecoderC*, %swift.error**)** [[DECODE_NEXT_ARG_REF]] +// CHECK-NEXT: [[DECODER_METADATA:%.*]] = bitcast i8* %2 to %swift.type* +// CHECK-NEXT: [[DECODE_NEXT_ARG_REF:%.*]] = getelementptr inbounds %swift.type*, %swift.type** [[DECODER_METADATA]], i64 -// CHECK-NEXT: [[ARG_TYPES:%.*]] = bitcast i8* %2 to %swift.type** - -// CHECK: [[ARG_0_TYPE_LOC:%.*]] = getelementptr inbounds %swift.type*, %swift.type** [[ARG_TYPES]], i64 0 -// CHECK-NEXT: %arg_type = load %swift.type*, %swift.type** [[ARG_0_TYPE_LOC]] -// CHECK: %size = load i64, i64* {{.*}} // CHECK: [[ARG_0_SIZE_ADJ:%.*]] = add i64 %size, 15 // CHECK-NEXT: [[ARG_0_SIZE:%.*]] = and i64 [[ARG_0_SIZE_ADJ]], -16 // CHECK-NEXT: [[ARG_0_VALUE_BUF:%.*]] = call swiftcc i8* @swift_task_alloc(i64 [[ARG_0_SIZE]]) @@ -125,7 +119,7 @@ public distributed actor MyOtherActor { // CHECK: missing-witness1: // CHECK-NEXT: call void @llvm.trap() // CHECK-NEXT: unreachable -// CHECK: call swiftcc void [[DECODE_NEXT_ARG]](%swift.opaque* noalias nocapture sret(%swift.opaque) [[ARG_0_RES_SLOT]], %swift.type* %arg_type, i8** [[ENCODABLE_WITNESS]], i8** [[DECODABLE_WITNESS]], %T27FakeDistributedActorSystems0A17InvocationDecoderC* swiftself [[DECODER]], %swift.error** noalias nocapture swifterror dereferenceable(8) %swifterror) +// CHECK: call swiftcc void @"$s27FakeDistributedActorSystems0A17InvocationDecoderC18decodeNextArgumentxyKSeRzSERzlF"(%swift.opaque* noalias nocapture sret(%swift.opaque) %25, %swift.type* %arg_type, i8** %26, i8** %29, %T27FakeDistributedActorSystems0A17InvocationDecoderC* swiftself %15, %swift.error** noalias nocapture swifterror dereferenceable(8) %swifterror) // CHECK: store %swift.error* null, %swift.error** %swifterror // CHECK-NEXT: [[ARG_0_VAL_ADDR:%.*]] = bitcast i8* [[ARG_0_VALUE_BUF]] to %TSi* From 41a4ceae153817f47c48b6abedec0476a7b038a7 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 18 Apr 2022 13:00:18 -0700 Subject: [PATCH 6/8] [Distributed] SILOpt: Make sure that ad-hoc function does not get eliminated as unused --- lib/SILOptimizer/Analysis/CallerAnalysis.cpp | 6 ++++-- .../FunctionSignatureTransforms/FunctionSignatureOpts.cpp | 1 + lib/SILOptimizer/IPO/DeadFunctionElimination.cpp | 6 ++++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/SILOptimizer/Analysis/CallerAnalysis.cpp b/lib/SILOptimizer/Analysis/CallerAnalysis.cpp index 738d08ab79226..28ffc297ece5c 100644 --- a/lib/SILOptimizer/Analysis/CallerAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/CallerAnalysis.cpp @@ -33,8 +33,10 @@ CallerAnalysis::FunctionInfo::FunctionInfo(SILFunction *f) : callerStates(), // TODO: Make this more aggressive by considering // final/visibility/etc. - mayHaveIndirectCallers(f->getDynamicallyReplacedFunction() || - canBeCalledIndirectly(f->getRepresentation())), + mayHaveIndirectCallers( + f->getDynamicallyReplacedFunction() || + f->getReferencedAdHocRequirementWitnessFunction() || + canBeCalledIndirectly(f->getRepresentation())), mayHaveExternalCallers(f->isPossiblyUsedExternally() || f->isAvailableExternally()) {} diff --git a/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.cpp b/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.cpp index 8857f2d8aac7c..695596b117d55 100644 --- a/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.cpp +++ b/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.cpp @@ -646,6 +646,7 @@ bool FunctionSignatureTransform::run(bool hasCaller) { hasCaller |= FSOOptimizeIfNotCalled; if (!hasCaller && (F->getDynamicallyReplacedFunction() || + F->getReferencedAdHocRequirementWitnessFunction() || canBeCalledIndirectly(F->getRepresentation()))) { LLVM_DEBUG(llvm::dbgs() << " function has no caller -> abort\n"); return false; diff --git a/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp b/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp index 3004339b6002e..9d9c642f2b95a 100644 --- a/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp +++ b/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp @@ -101,6 +101,9 @@ class DeadFunctionAndGlobalElimination { if (F->isDynamicallyReplaceable()) return true; + if (F->getReferencedAdHocRequirementWitnessFunction()) + return true; + // Don't remove pre-specialized functions. We need to preserver the // pre-specialization specifications from other modules. if (F->hasPrespecialization()) @@ -325,6 +328,9 @@ class DeadFunctionAndGlobalElimination { LLVM_DEBUG(llvm::dbgs() << " scan function " << F->getName() << '\n'); + if (auto *adHocWitness = F->getReferencedAdHocRequirementWitnessFunction()) + ensureAlive(adHocWitness); + // First scan all instructions of the function. for (SILBasicBlock &BB : *F) { for (SILInstruction &I : BB) { From 8c8360a6a1ddc97553b202e6ec7149ca1eb7551f Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 18 Apr 2022 13:02:59 -0700 Subject: [PATCH 7/8] [Distributed] IRGen: Remove a `decodeNextArgument` access workaround The symbol is always retained now, so the workaround to use witness table for non-final classes is no longer necessary. --- lib/IRGen/GenDistributed.cpp | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/lib/IRGen/GenDistributed.cpp b/lib/IRGen/GenDistributed.cpp index 9d6586381f4c7..481bd6b31218d 100644 --- a/lib/IRGen/GenDistributed.cpp +++ b/lib/IRGen/GenDistributed.cpp @@ -813,22 +813,13 @@ ArgumentDecoderInfo DistributedAccessor::findArgumentDecoder( decoder = instance.claimNext(); } - // TODO(distributed): this can be removed most likely now - if (isa(decoderDecl) || isa(decoderDecl) || - decoderDecl->isFinal()) { - auto *decodeSIL = IGM.getSILModule().lookUpFunction(SILDeclRef(decodeFn)); - auto *fnPtr = IGM.getAddrOfSILFunction(decodeSIL, NotForDefinition, - /*isDynamicallyReplacible=*/false); - - auto methodPtr = FunctionPointer::forDirect( - classifyFunctionPointerKind(decodeSIL), fnPtr, - /*secondaryValue=*/nullptr, signature); - - return {decoder, decoderTy, witnessTable, methodPtr, methodTy}; - } + auto *decodeSIL = IGM.getSILModule().lookUpFunction(SILDeclRef(decodeFn)); + auto *fnPtr = IGM.getAddrOfSILFunction(decodeSIL, NotForDefinition, + /*isDynamicallyReplacible=*/false); - auto methodPtr = - emitVirtualMethodValue(IGF, decoderTy, SILDeclRef(decodeFn), methodTy); + auto methodPtr = FunctionPointer::forDirect( + classifyFunctionPointerKind(decodeSIL), fnPtr, + /*secondaryValue=*/nullptr, signature); return {decoder, decoderTy, witnessTable, methodPtr, methodTy}; } From fc662bc8e1b437ba588f2355b5d9dd0b7e6754bc Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 18 Apr 2022 17:27:26 -0700 Subject: [PATCH 8/8] [Distributed] NFC: Adjust accessor tests to check for direct call to `decodeNextArgument` --- .../Distributed/distributed_actor_accessor_thunks_32bit.swift | 2 +- .../Distributed/distributed_actor_accessor_thunks_64bit.swift | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/Distributed/distributed_actor_accessor_thunks_32bit.swift b/test/Distributed/distributed_actor_accessor_thunks_32bit.swift index b660ef727782c..257a92d442d19 100644 --- a/test/Distributed/distributed_actor_accessor_thunks_32bit.swift +++ b/test/Distributed/distributed_actor_accessor_thunks_32bit.swift @@ -119,7 +119,7 @@ public distributed actor MyOtherActor { // CHECK: missing-witness1: // CHECK-NEXT: call void @llvm.trap() // CHECK-NEXT: unreachable -// CHECK: call swiftcc void [[DECODE_NEXT_ARG]](%swift.opaque* noalias nocapture sret(%swift.opaque) [[ARG_0_RES_SLOT]], %swift.type* %arg_type, i8** [[ENCODABLE_WITNESS]], i8** [[DECODABLE_WITNESS]], %T27FakeDistributedActorSystems0A17InvocationDecoderC* swiftself [[DECODER]], %swift.error** noalias nocapture dereferenceable(4) %swifterror) +// CHECK: call swiftcc void @"$s27FakeDistributedActorSystems0A17InvocationDecoderC18decodeNextArgumentxyKSeRzSERzlF"(%swift.opaque* noalias nocapture sret(%swift.opaque) [[ARG_0_RES_SLOT]], %swift.type* %arg_type, i8** [[ENCODABLE_WITNESS]], i8** [[DECODABLE_WITNESS]], %T27FakeDistributedActorSystems0A17InvocationDecoderC* swiftself [[DECODER]], %swift.error** noalias nocapture dereferenceable(4) %swifterror) // CHECK: store %swift.error* null, %swift.error** %swifterror // CHECK-NEXT: [[ARG_0_VAL_ADDR:%.*]] = bitcast i8* [[ARG_0_VALUE_BUF]] to %TSi* diff --git a/test/Distributed/distributed_actor_accessor_thunks_64bit.swift b/test/Distributed/distributed_actor_accessor_thunks_64bit.swift index d6284a3748ae0..39a07847651c9 100644 --- a/test/Distributed/distributed_actor_accessor_thunks_64bit.swift +++ b/test/Distributed/distributed_actor_accessor_thunks_64bit.swift @@ -1,6 +1,6 @@ // 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 -disable-availability-checking -I %t 2>&1 %s | %IRGenFileCheck %s -check-prefix CHECK-%target-import-type --dump-input=always +// RUN: %target-swift-frontend -module-name distributed_actor_accessors -emit-irgen -disable-availability-checking -I %t 2>&1 %s | %IRGenFileCheck %s -check-prefix CHECK-%target-import-type // UNSUPPORTED: back_deploy_concurrency // REQUIRES: concurrency @@ -119,7 +119,7 @@ public distributed actor MyOtherActor { // CHECK: missing-witness1: // CHECK-NEXT: call void @llvm.trap() // CHECK-NEXT: unreachable -// CHECK: call swiftcc void @"$s27FakeDistributedActorSystems0A17InvocationDecoderC18decodeNextArgumentxyKSeRzSERzlF"(%swift.opaque* noalias nocapture sret(%swift.opaque) %25, %swift.type* %arg_type, i8** %26, i8** %29, %T27FakeDistributedActorSystems0A17InvocationDecoderC* swiftself %15, %swift.error** noalias nocapture swifterror dereferenceable(8) %swifterror) +// CHECK: call swiftcc void @"$s27FakeDistributedActorSystems0A17InvocationDecoderC18decodeNextArgumentxyKSeRzSERzlF"(%swift.opaque* noalias nocapture sret(%swift.opaque) [[ARG_0_RES_SLOT]], %swift.type* %arg_type, i8** [[ENCODABLE_WITNESS]], i8** [[DECODABLE_WITNESS]], %T27FakeDistributedActorSystems0A17InvocationDecoderC* swiftself [[DECODER]], %swift.error** noalias nocapture swifterror dereferenceable(8) %swifterror) // CHECK: store %swift.error* null, %swift.error** %swifterror // CHECK-NEXT: [[ARG_0_VAL_ADDR:%.*]] = bitcast i8* [[ARG_0_VALUE_BUF]] to %TSi*