diff --git a/include/swift/SIL/ApplySite.h b/include/swift/SIL/ApplySite.h index 0dbd1c7f2e846..94e989eb0341f 100644 --- a/include/swift/SIL/ApplySite.h +++ b/include/swift/SIL/ApplySite.h @@ -894,6 +894,21 @@ class FullApplySite : public ApplySite { getNumIndirectSILErrorResults(); } + std::optional getActorIsolation() const { + if (auto isolation = getIsolationCrossing(); + isolation && isolation->getCalleeIsolation()) + return isolation->getCalleeIsolation(); + auto *calleeFunction = getCalleeFunction(); + if (!calleeFunction) + return {}; + return calleeFunction->getActorIsolation(); + } + + bool isCallerIsolationInheriting() const { + auto isolation = getActorIsolation(); + return isolation && isolation->isCallerIsolationInheriting(); + } + static FullApplySite getFromOpaqueValue(void *p) { return FullApplySite(p); } static bool classof(const SILInstruction *inst) { diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index 4176265e6502d..a0b84dbd89a77 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -95,6 +95,11 @@ llvm::cl::opt SILPrintGenericSpecializationInfo( llvm::cl::desc("Include generic specialization" "information info in SIL output")); +llvm::cl::opt SILPrintFunctionIsolationInfo( + "sil-print-function-isolation-info", llvm::cl::init(false), + llvm::cl::desc("Print out isolation info on functions in a manner that SIL " + "understands [e.x.: not in comments]")); + static std::string demangleSymbol(StringRef Name) { if (SILFullDemangle) return Demangle::demangleSymbolAsString(Name); @@ -3627,6 +3632,15 @@ void SILFunction::print(SILPrintContext &PrintCtx) const { OS << "[available " << availability.getVersionString() << "] "; } + // This is here only for testing purposes. + if (SILPrintFunctionIsolationInfo) { + if (auto isolation = getActorIsolation()) { + OS << "[isolation \""; + isolation->printForSIL(OS); + OS << "\"] "; + } + } + switch (getInlineStrategy()) { case NoInline: OS << "[noinline] "; break; case AlwaysInline: OS << "[always_inline] "; break; diff --git a/lib/SIL/Parser/ParseSIL.cpp b/lib/SIL/Parser/ParseSIL.cpp index 04918e779e100..c5ec5e5ce4e7d 100644 --- a/lib/SIL/Parser/ParseSIL.cpp +++ b/lib/SIL/Parser/ParseSIL.cpp @@ -685,7 +685,8 @@ static bool parseDeclSILOptional( AvailabilityRange *availability, bool *isWithoutActuallyEscapingThunk, SmallVectorImpl *Semantics, SmallVectorImpl *SpecAttrs, ValueDecl **ClangDecl, - EffectsKind *MRK, SILParser &SP, SILModule &M) { + EffectsKind *MRK, ActorIsolation *actorIsolation, SILParser &SP, + SILModule &M) { while (SP.P.consumeIf(tok::l_square)) { if (isLet && SP.P.Tok.is(tok::kw_let)) { *isLet = true; @@ -784,7 +785,27 @@ static bool parseDeclSILOptional( *isPerformanceConstraint = true; else if (markedAsUsed && SP.P.Tok.getText() == "used") *markedAsUsed = true; - else if (section && SP.P.Tok.getText() == "section") { + else if (actorIsolation && SP.P.Tok.getText() == "isolation") { + 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; + } + StringRef rawString = SP.P.Tok.getText().drop_front().drop_back(); + // TODO: By using a raw string here, we can perhaps put in a simple string + // representation of an actor that can be parsed back. For now this is + // just a quick hack so we can write tests. + auto optIsolation = ActorIsolation::forSILString( + SP.P.Context.getIdentifier(rawString).str()); + if (!optIsolation) { + SP.P.diagnose(SP.P.Tok, diag::expected_in_attribute_list); + return true; + } + *actorIsolation = *optIsolation; + SP.P.consumeToken(tok::string_literal); + SP.P.parseToken(tok::r_square, diag::expected_in_attribute_list); + continue; + } else if (section && SP.P.Tok.getText() == "section") { SP.P.consumeToken(tok::identifier); if (SP.P.Tok.getKind() != tok::string_literal) { SP.P.diagnose(SP.P.Tok, diag::expected_in_attribute_list); @@ -798,8 +819,7 @@ static bool parseDeclSILOptional( SP.P.parseToken(tok::r_square, diag::expected_in_attribute_list); continue; - } - else if (inlineStrategy && SP.P.Tok.getText() == "always_inline") + } else if (inlineStrategy && SP.P.Tok.getText() == "always_inline") *inlineStrategy = AlwaysInline; else if (MRK && SP.P.Tok.getText() == "readnone") *MRK = EffectsKind::ReadNone; @@ -7333,6 +7353,7 @@ bool SILParserState::parseDeclSIL(Parser &P) { SILFunction *DynamicallyReplacedFunction = nullptr; SILFunction *AdHocWitnessFunction = nullptr; Identifier objCReplacementFor; + ActorIsolation actorIsolation; if (parseSILLinkage(FnLinkage, P) || parseDeclSILOptional( &isTransparent, &isSerialized, &isCanonical, &hasOwnershipSSA, @@ -7344,7 +7365,7 @@ bool SILParserState::parseDeclSIL(Parser &P) { &optimizationMode, &perfConstr, &isPerformanceConstraint, &markedAsUsed, §ion, nullptr, &isWeakImported, &needStackProtection, &availability, &isWithoutActuallyEscapingThunk, - &Semantics, &SpecAttrs, &ClangDecl, &MRK, FunctionState, M) || + &Semantics, &SpecAttrs, &ClangDecl, &MRK, &actorIsolation, FunctionState, M) || P.parseToken(tok::at_sign, diag::expected_sil_function_name) || P.parseIdentifier(FnName, FnNameLoc, /*diagnoseDollarPrefix=*/false, diag::expected_sil_function_name) || @@ -7404,6 +7425,8 @@ bool SILParserState::parseDeclSIL(Parser &P) { for (auto &Attr : Semantics) { FunctionState.F->addSemanticsAttr(Attr); } + if (actorIsolation) + FunctionState.F->setActorIsolation(actorIsolation); // Now that we have a SILFunction parse the body, if present. bool isDefinition = false; @@ -7593,11 +7616,11 @@ bool SILParserState::parseSILGlobal(Parser &P) { SILParser State(P); if (parseSILLinkage(GlobalLinkage, P) || - parseDeclSILOptional(nullptr, &isSerialized, nullptr, nullptr, + parseDeclSILOptional(nullptr, &isSerialized, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, &isLet, 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) || P.parseIdentifier(GlobalName, NameLoc, /*diagnoseDollarPrefix=*/false, @@ -7651,7 +7674,7 @@ bool SILParserState::parseSILProperty(Parser &P) { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, SP, M)) + nullptr, nullptr, nullptr, SP, M)) return true; ValueDecl *VD; @@ -7721,7 +7744,7 @@ bool SILParserState::parseSILVTable(Parser &P) { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, VTableState, M)) + nullptr, nullptr, nullptr, VTableState, M)) return true; @@ -7844,7 +7867,8 @@ bool SILParserState::parseSILMoveOnlyDeinit(Parser &parser) { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, moveOnlyDeinitTableState, M)) + nullptr, nullptr, nullptr, + moveOnlyDeinitTableState, M)) return true; // Parse the class name. @@ -8373,12 +8397,12 @@ bool SILParserState::parseSILWitnessTable(Parser &P) { parseSILLinkage(Linkage, P); SerializedKind_t isSerialized = IsNotSerialized; - 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, nullptr, nullptr, nullptr, - nullptr, nullptr, WitnessState, M)) + 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, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, WitnessState, M)) return true; // Parse the protocol conformance. diff --git a/lib/SILGen/SILGenConcurrency.cpp b/lib/SILGen/SILGenConcurrency.cpp index 256a1f0605912..89fdbe8b47cb6 100644 --- a/lib/SILGen/SILGenConcurrency.cpp +++ b/lib/SILGen/SILGenConcurrency.cpp @@ -680,6 +680,35 @@ SILGenFunction::emitHopToTargetActor(SILLocation loc, } } +namespace { + +class HopToActorCleanup : public Cleanup { + SILValue value; + +public: + HopToActorCleanup(SILValue value) : value(value) {} + + void emit(SILGenFunction &SGF, CleanupLocation l, + ForUnwind_t forUnwind) override { + SGF.B.createHopToExecutor(l, value, false /*mandatory*/); + } + + void dump(SILGenFunction &) const override { +#ifndef NDEBUG + llvm::errs() << "HopToExecutorCleanup\n" + << "State:" << getState() << "\n" + << "Value:" << value << "\n"; +#endif + } +}; +} // end anonymous namespace + +CleanupHandle SILGenFunction::emitScopedHopToTargetActor(SILLocation loc, + SILValue actor) { + Cleanups.pushCleanup(actor); + return Cleanups.getTopCleanup(); +} + ExecutorBreadcrumb SILGenFunction::emitHopToTargetExecutor( SILLocation loc, SILValue executor) { // Record that we need to hop back to the current executor. diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index 06f51521162cd..120c0ded538eb 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -1333,6 +1333,12 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction emitHopToTargetActor(SILLocation loc, std::optional actorIso, std::optional actorSelf); + /// Emit a cleanup for a hop back to a target actor. + /// + /// Used to ensure that along error paths and normal scope exit paths we hop + /// back automatically. + CleanupHandle emitScopedHopToTargetActor(SILLocation loc, SILValue actor); + /// Emit a hop to the target executor, returning a breadcrumb with enough /// enough information to hop back. /// diff --git a/lib/SILGen/SILGenPoly.cpp b/lib/SILGen/SILGenPoly.cpp index 0089adcc1f9ff..20c1e661a50f8 100644 --- a/lib/SILGen/SILGenPoly.cpp +++ b/lib/SILGen/SILGenPoly.cpp @@ -5516,7 +5516,7 @@ static void buildThunkBody(SILGenFunction &SGF, SILLocation loc, // don't need to do anything. But if the input is synchronous and the // executor is asynchronous, we need to treat this like any other call // to a synchronous function from an asynchronous context. - bool hopToIsolatedParameter = false; + bool hopToIsolatedParameterForSync = false; if (outputSubstType->isAsync() && !inputSubstType->isAsync()) { auto inputIsolation = inputSubstType->getIsolation(); switch (inputIsolation.getKind()) { @@ -5533,7 +5533,7 @@ static void buildThunkBody(SILGenFunction &SGF, SILLocation loc, // For a function with parameter isolation, we'll have to dig the // argument out after translation but before making the call. case FunctionTypeIsolation::Kind::Parameter: - hopToIsolatedParameter = true; + hopToIsolatedParameterForSync = true; break; // For a function with global-actor isolation, hop to the appropriate @@ -5602,44 +5602,49 @@ static void buildThunkBody(SILGenFunction &SGF, SILLocation loc, argValues.push_back(*innerIndirectErrorAddr); } - // If we need to jump to an isolated parameter, do so before the call. - if (hopToIsolatedParameter) { + // If we need to jump to an isolated parameter for a synchronous function, do + // so before the call. + if (hopToIsolatedParameterForSync) { auto formalIsolatedIndex = getIsolatedParamIndex(inputSubstType); auto isolatedIndex = inputOrigType.getLoweredParamIndex(formalIsolatedIndex); SGF.B.createHopToExecutor(loc, args[isolatedIndex].getValue(), /*mandatory*/false); } - // If the input function has caller isolation, we need to fill in that - // argument with the formal isolation of the output function. + // If the input function has caller isolation (and thus is async), we need to + // fill in that argument with the formal isolation of the output function and + // hop to it so that it is able to assume that it does not need to hop in its + // prologue. if (options.contains(ThunkGenFlag::CalleeHasImplicitIsolatedParam)) { - auto outputIsolation = outputSubstType->getIsolation(); - switch (outputIsolation.getKind()) { - case FunctionTypeIsolation::Kind::NonIsolated: - case FunctionTypeIsolation::Kind::Erased: - // Converting a caller-isolated function to @isolated(any) makes - // it @concurrent. In either case, emit a nil actor reference. - argValues.push_back(SGF.emitNonIsolatedIsolation(loc).getValue()); - break; - case FunctionTypeIsolation::Kind::GlobalActor: { - auto globalActor = - outputIsolation.getGlobalActorType()->getCanonicalType(); - argValues.push_back( - SGF.emitGlobalActorIsolation(loc, globalActor).getValue()); - break; - } - case FunctionTypeIsolation::Kind::NonIsolatedCaller: { - argValues.push_back(implicitIsolationParam.getValue()); - break; - } - case FunctionTypeIsolation::Kind::Parameter: - // This would require a conversion from a type with caller - // isolation to a type with parameter isolation, which is not - // currently allowed and probably won't ever be. Anyway, to - // implement it, we'd need to borrow the isolated parameter - // and wrap it up as an `Optional`. - llvm_unreachable("Should never see this"); - } + auto forwardedIsolationValue = [&]() -> SILValue { + auto outputIsolation = outputSubstType->getIsolation(); + switch (outputIsolation.getKind()) { + case FunctionTypeIsolation::Kind::NonIsolated: + case FunctionTypeIsolation::Kind::Erased: + // Converting a caller-isolated function to @isolated(any) makes + // it @concurrent. In either case, emit a nil actor reference. + return SGF.emitNonIsolatedIsolation(loc).getValue(); + case FunctionTypeIsolation::Kind::GlobalActor: { + auto globalActor = + outputIsolation.getGlobalActorType()->getCanonicalType(); + return SGF.emitGlobalActorIsolation(loc, globalActor).getValue(); + } + case FunctionTypeIsolation::Kind::NonIsolatedCaller: { + return implicitIsolationParam.getValue(); + } + case FunctionTypeIsolation::Kind::Parameter: + // This would require a conversion from a type with caller + // isolation to a type with parameter isolation, which is not + // currently allowed and probably won't ever be. Anyway, to + // implement it, we'd need to borrow the isolated parameter + // and wrap it up as an `Optional`. + llvm::report_fatal_error("Should never see this?!"); + } + }(); + + assert(forwardedIsolationValue); + SGF.B.createHopToExecutor(loc, forwardedIsolationValue, false); + argValues.push_back(forwardedIsolationValue); } // Add the rest of the arguments. @@ -5648,6 +5653,16 @@ static void buildThunkBody(SILGenFunction &SGF, SILLocation loc, auto fun = fnType->isCalleeGuaranteed() ? fnValue.borrow(SGF, loc).getValue() : fnValue.forward(SGF); + // If our thunk is a caller isolated function, we need to insert a hop to + // return to our isolation so that our caller can assume that we preserve + // isolation. + if (auto isolatedArg = llvm::cast_or_null( + SGF.F.maybeGetIsolatedArgument()); + isolatedArg && isolatedArg->getKnownParameterInfo().hasOption( + SILParameterInfo::ImplicitLeading)) { + SGF.emitScopedHopToTargetActor(loc, isolatedArg); + } + SILValue innerResult = SGF.emitApplyWithRethrow(loc, fun, /*substFnType*/ fnValue.getType(), @@ -7073,6 +7088,15 @@ SILGenFunction::emitVTableThunk(SILDeclRef base, if (options.contains(ThunkGenFlag::CalleeHasImplicitIsolatedParam)) { auto baseIsolation = swift::getActorIsolation(base.getAbstractFunctionDecl()); + std::optional derivedIsolationCache; + auto getDerivedIsolation = [&]() -> ActorIsolation { + if (!derivedIsolationCache) { + derivedIsolationCache = + swift::getActorIsolation(derived.getAbstractFunctionDecl()); + } + return *derivedIsolationCache; + }; + switch (baseIsolation) { case ActorIsolation::Unspecified: case ActorIsolation::Nonisolated: @@ -7089,8 +7113,7 @@ SILGenFunction::emitVTableThunk(SILDeclRef base, } case ActorIsolation::ActorInstance: case ActorIsolation::CallerIsolationInheriting: { - auto derivedIsolation = - swift::getActorIsolation(derived.getAbstractFunctionDecl()); + auto derivedIsolation = getDerivedIsolation(); switch (derivedIsolation) { case ActorIsolation::Unspecified: case ActorIsolation::Nonisolated: @@ -7117,6 +7140,14 @@ SILGenFunction::emitVTableThunk(SILDeclRef base, break; } } + + // If our derived isolation is caller isolation inheriting and our base + // isn't, we need to insert a hop so that derived can assume that it does + // not have to hop in its prologue. + if (!baseIsolation.isCallerIsolationInheriting() && + getDerivedIsolation().isCallerIsolationInheriting()) { + B.createHopToExecutor(loc, args.back(), false /*mandatory*/); + } } // Then, the arguments. @@ -7135,6 +7166,15 @@ SILGenFunction::emitVTableThunk(SILDeclRef base, derivedRef = B.createFunctionRefFor(loc, implFn); } + // Check if our base was caller isolation inheriting. In such a case, we need + // to insert a hop back to our original actor. + if (auto isolatedArg = llvm::dyn_cast_or_null( + F.maybeGetIsolatedArgument()); + isolatedArg && isolatedArg->getKnownParameterInfo().hasOption( + SILParameterInfo::ImplicitLeading)) { + emitScopedHopToTargetActor(loc, {isolatedArg}); + } + SILValue result; switch (coroutineKind) { @@ -7185,7 +7225,7 @@ SILGenFunction::emitVTableThunk(SILDeclRef base, result = B.createTuple(loc, {}); break; } - + scope.pop(); B.createReturn(loc, result); @@ -7553,6 +7593,14 @@ void SILGenFunction::emitProtocolWitness( if (options.contains(ThunkGenFlag::CalleeHasImplicitIsolatedParam)) { auto reqtIsolation = swift::getActorIsolation(requirement.getAbstractFunctionDecl()); + std::optional witnessIsolationCache; + auto getWitnessIsolation = [&]() -> ActorIsolation { + if (!witnessIsolationCache) { + witnessIsolationCache = + swift::getActorIsolation(witness.getAbstractFunctionDecl()); + } + return *witnessIsolationCache; + }; switch (reqtIsolation) { case ActorIsolation::Unspecified: case ActorIsolation::Nonisolated: @@ -7569,8 +7617,7 @@ void SILGenFunction::emitProtocolWitness( } case ActorIsolation::ActorInstance: case ActorIsolation::CallerIsolationInheriting: { - auto witnessIsolation = - swift::getActorIsolation(witness.getAbstractFunctionDecl()); + auto witnessIsolation = getWitnessIsolation(); switch (witnessIsolation) { case ActorIsolation::Unspecified: case ActorIsolation::Nonisolated: @@ -7597,6 +7644,25 @@ void SILGenFunction::emitProtocolWitness( break; } } + + // If our reqtIsolation was not caller isolation inheriting, but our witness + // isolation is caller isolation inheriting, hop onto the reqtIsolation so + // that it is safe for our witness to assume that it is already on its + // actor. + if (!reqtIsolation.isCallerIsolationInheriting() && + getWitnessIsolation().isCallerIsolationInheriting()) { + B.createHopToExecutor(loc, args.back(), false /*mandatory*/); + } + } + + // Check if our caller has an implicit isolated param. If so, we need to + // insert a hop at the end of the function to ensure that our caller can + // assume that we are going to be on its isolation. + if (auto isolatedArg = + cast_or_null(F.maybeGetIsolatedArgument()); + isolatedArg && isolatedArg->getKnownParameterInfo().hasOption( + SILParameterInfo::ImplicitLeading)) { + emitScopedHopToTargetActor(loc, SILValue(isolatedArg)); } // - the rest of the arguments diff --git a/lib/SILOptimizer/Mandatory/OptimizeHopToExecutor.cpp b/lib/SILOptimizer/Mandatory/OptimizeHopToExecutor.cpp index fc4a42e89e671..04527ce359640 100644 --- a/lib/SILOptimizer/Mandatory/OptimizeHopToExecutor.cpp +++ b/lib/SILOptimizer/Mandatory/OptimizeHopToExecutor.cpp @@ -193,12 +193,16 @@ void OptimizeHopToExecutor::solveDataflowBackward() { /// Returns true if \p inst is a suspension point or an async call. static bool isSuspensionPoint(SILInstruction *inst) { if (auto applySite = FullApplySite::isa(inst)) { + // NOTE: For 6.2, we consider nonisolated(nonsending) to be a suspension + // point, when it really isn't. We do this so that we have a truly + // conservative change that does not change output. if (applySite.isAsync()) return true; return false; } if (isa(inst)) return true; + return false; } @@ -311,6 +315,41 @@ void OptimizeHopToExecutor::updateNeedExecutor(int &needExecutor, needExecutor = BlockState::NoExecutorNeeded; return; } + + // For 6.2 to be conservative, if we are calling a function with + // caller_isolation_inheriting isolation, treat the callsite as if the + // callsite is an instruction that needs an executor. + // + // DISCUSSION: The reason why we are doing this is that in 6.2, we are going + // to continue treating caller isolation inheriting functions as a suspension + // point for the purpose of eliminating redundant hop to executor to not make + // this optimization more aggressive. Post 6.2, we will stop treating caller + // isolation inheriting functions as suspension points, meaning this code can + // be deleted. + if (auto fas = FullApplySite::isa(inst); + fas && fas.isAsync() && fas.isCallerIsolationInheriting()) { + needExecutor = BlockState::ExecutorNeeded; + return; + } + + // For 6.2, if we are in a caller isolation inheriting function, we need to + // treat its return as an executor needing function before + // isSuspensionPoint. + // + // DISCUSSION: We need to do this here since for 6.2, a caller isolation + // inheriting function is going to be considered a suspension point to be + // conservative and make this optimization strictly more conservative. Post + // 6.2, since caller isolation inheriting functions will no longer be + // considered suspension points, we will be able to sink this code into needs + // executor. + if (isa(inst)) { + if (auto isolation = inst->getFunction()->getActorIsolation(); + isolation && isolation->isCallerIsolationInheriting()) { + needExecutor = BlockState::ExecutorNeeded; + return; + } + } + if (isSuspensionPoint(inst)) { needExecutor = BlockState::NoExecutorNeeded; return; diff --git a/test/Concurrency/attr_execution/classes_silgen.swift b/test/Concurrency/attr_execution/classes_silgen.swift index df3442594bb32..fe82210ff52a9 100644 --- a/test/Concurrency/attr_execution/classes_silgen.swift +++ b/test/Concurrency/attr_execution/classes_silgen.swift @@ -6,8 +6,7 @@ // We should only produce thunks when going to/from nonisolated(nonsending) // since that is the only thing that makes a true ABI change since we have an -// extra parameter. For isolation purposes, we can rely on async functions -// hopping in their prolog and not need a thunk for the purposes of isolation changing. +// extra parameter. // NOTE: We use implicit-check-not to make sure we do not create any other // thunks beyond the ones we pattern match. @@ -31,6 +30,7 @@ class AllConcurrent : SuperKlass { // CHECK: bb0([[ACTOR:%.*]] : @guaranteed $Optional, [[PARAM:%.*]] : @guaranteed $AllConcurrent): // CHECK: [[FUNC:%.*]] = function_ref @$s21attr_execution_silgen13AllConcurrentC10callerTestyyYaF : $@convention(method) @async (@guaranteed AllConcurrent) -> () // CHECK: apply [[FUNC]]([[PARAM]]) + // CHECK: hop_to_executor [[ACTOR]] // CHECK: } // end sil function '$s21attr_execution_silgen13AllConcurrentC10callerTestyyYaFAA10SuperKlassCADyyYaFTV' @concurrent override func callerTest() async {} @concurrent override func concurrentTest() async {} @@ -44,6 +44,7 @@ class AllNonIsolatedUnsafe : SuperKlass { // CHECK-NEXT: sil private [thunk] [ossa] @$s21attr_execution_silgen20AllNonIsolatedUnsafeC14concurrentTestyyYaFAA10SuperKlassCADyyYaFTV : $@convention(method) @async (@guaranteed AllNonIsolatedUnsafe) -> () { // CHECK: bb0([[ARG:%.*]] : @guaranteed // CHECK: [[ACTOR:%.*]] = enum $Optional, #Optional.none!enumelt + // CHECK: hop_to_executor [[ACTOR]] // CHECK: [[FUNC:%.*]] = function_ref @$s21attr_execution_silgen20AllNonIsolatedUnsafeC14concurrentTestyyYaF : $@convention(method) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional, @guaranteed AllNonIsolatedUnsafe) -> () // CHECK: apply [[FUNC]]([[ACTOR]], [[ARG]]) // CHECK: } // end sil function '$s21attr_execution_silgen20AllNonIsolatedUnsafeC14concurrentTestyyYaFAA10SuperKlassCADyyYaFTV' @@ -55,6 +56,7 @@ class AllNonIsolatedUnsafe : SuperKlass { // CHECK: [[ACTOR:%.*]] = apply {{%.*}}({{%.*}}) : $@convention(method) (@thick MainActor.Type) -> @owned MainActor // CHECK: [[ACTOR_E:%.*]] = init_existential_ref [[ACTOR]] // CHECK: [[ACTOR_E_OPT:%.*]] = enum $Optional, #Optional.some!enumelt, [[ACTOR_E]] + // CHECK: hop_to_executor [[ACTOR_E_OPT]] // CHECK: [[FUNC:%.*]] = function_ref @$s21attr_execution_silgen20AllNonIsolatedUnsafeC13mainActorTestyyYaF : $@convention(method) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional, @guaranteed AllNonIsolatedUnsafe) -> () // CHECK: apply [[FUNC]]([[ACTOR_E_OPT]], [[ARG]]) // CHECK: } // end sil function '$s21attr_execution_silgen20AllNonIsolatedUnsafeC13mainActorTestyyYaFAA10SuperKlassCADyyYaFTV' diff --git a/test/Concurrency/attr_execution/conversions_silgen.swift b/test/Concurrency/attr_execution/conversions_silgen.swift index 2271c686ed172..25c7ab2d168e3 100644 --- a/test/Concurrency/attr_execution/conversions_silgen.swift +++ b/test/Concurrency/attr_execution/conversions_silgen.swift @@ -58,6 +58,7 @@ public func testCallerToConcurrentNonIsolated(_ x: nonisolated(nonsending) @esca // CHECK-LABEL: sil shared [transparent] [serialized] [reabstraction_thunk] [ossa] @$sScA_pSgIegHgIL_IegH_TR : $@convention(thin) @async (@guaranteed @async @callee_guaranteed (@sil_isolated @sil_implicit_leading_param @guaranteed Optional) -> ()) -> () { // CHECK: bb0([[FUNC:%.*]] : @guaranteed $@async @callee_guaranteed (@sil_isolated @sil_implicit_leading_param @guaranteed Optional) -> ()): // CHECK: [[ENUM:%.*]] = enum $Optional, #Optional.none!enumelt +// CHECK: hop_to_executor [[ENUM]] // CHECK: apply [[FUNC]]([[ENUM]]) // CHECK: } // end sil function '$sScA_pSgIegHgIL_IegH_TR' @@ -88,8 +89,9 @@ public func testConcurrentToCallerNonIsolated(_ x: @escaping @concurrent () asyn // isolated parameter and calls the concurrent funciton. // // CHECK-LABEL: sil shared [transparent] [serialized] [reabstraction_thunk] [ossa] @$sIegH_ScA_pSgIegHgIL_TR : $@convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional, @guaranteed @async @callee_guaranteed () -> ()) -> () { -// CHECK: bb0({{%.*}} : @guaranteed $Optional, [[FUNC:%.*]] : @guaranteed $@async @callee_guaranteed () -> ()): +// CHECK: bb0([[ACTOR:%.*]] : @guaranteed $Optional, [[FUNC:%.*]] : @guaranteed $@async @callee_guaranteed () -> ()): // CHECK: apply [[FUNC]] +// CHECK: hop_to_executor [[ACTOR]] // CHECK: } // end sil function '$sIegH_ScA_pSgIegHgIL_TR' // CHECK-LABEL: sil [ossa] @$s21attr_execution_silgen42testConcurrentToCallerNonIsolatedMainActoryyyyYacYaF : $@convention(thin) @async (@guaranteed @async @callee_guaranteed () -> ()) -> () { @@ -260,6 +262,25 @@ public func testConcurrentCallerLocalVariables(_ x: @escaping @concurrent () asy // FIVE: apply [[V4_B_C_B]]() // CHECK: } // end sil function '$s21attr_execution_silgen22globalActorConversionsyyyyYac_yyYaYCctYaF' + +// FIVE-LABEL: sil shared [transparent] [serialized] [reabstraction_thunk] [ossa] @$sScA_pSgIegHgIL_IegH_TRScMTU : $@convention(thin) @async (@guaranteed @async @callee_guaranteed (@sil_isolated @sil_implicit_leading_param @guaranteed Optional) -> ()) -> () { +// FIVE: bb0([[FUNC:%.*]] : @guaranteed +// FIVE: [[ACTOR:%.*]] = apply {{%.*}}({{%.*}}) : $@convention(method) (@thick MainActor.Type) -> @owned MainActor +// FIVE: [[E:%.*]] = init_existential_ref [[ACTOR]] : $MainActor : $MainActor, $any Actor +// FIVE: [[E_OPT:%.*]] = enum $Optional, #Optional.some!enumelt, [[E]] +// FIVE: hop_to_executor [[E_OPT]] +// FIVE: apply [[FUNC]]([[E_OPT]]) : $@async @callee_guaranteed (@sil_isolated @sil_implicit_leading_param @guaranteed Optional) -> () +// FIVE: } // end sil function '$sScA_pSgIegHgIL_IegH_TRScMTU' + +// SIX-LABEL: sil shared [transparent] [serialized] [reabstraction_thunk] [ossa] @$sScA_pSgIetHgIL_IeghH_TRScMTU : $@convention(thin) @Sendable @async (@convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional) -> ()) -> () { +// SIX: bb0([[FUNC:%.*]] : $@convention(thin) @async (@sil_isolated +// SIX: [[ACTOR:%.*]] = apply {{%.*}}({{%.*}}) : $@convention(method) (@thick MainActor.Type) -> @owned MainActor +// SIX: [[E:%.*]] = init_existential_ref [[ACTOR]] : $MainActor : $MainActor, $any Actor +// SIX: [[E_OPT:%.*]] = enum $Optional, #Optional.some!enumelt, [[E]] +// SIX: hop_to_executor [[E_OPT]] +// SIX: apply [[FUNC]]([[E_OPT]]) : $@convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional) -> () +// SIX: } // end sil function '$sScA_pSgIetHgIL_IeghH_TRScMTU' + func globalActorConversions(_ x: @escaping @concurrent () async -> (), _ y: nonisolated(nonsending) @escaping () async -> ()) async { let v1: @MainActor () async -> Void = globalCallerFunc @@ -319,6 +340,31 @@ func globalActorConversions(_ x: @escaping @concurrent () async -> (), // FIVE: apply [[V4_B_C_B]]({{%.*}}) // CHECK: } // end sil function '$s21attr_execution_silgen23globalActorConversions2yyyAA13SendableKlassCYac_yADYaYCctYaF' + +// FIVE-LABEL: sil shared [transparent] [serialized] [reabstraction_thunk] [ossa] @$sScA_pSg21attr_execution_silgen13SendableKlassCIegHgILg_ADIegHg_TRScMTU : $@convention(thin) @async (@guaranteed SendableKlass, @guaranteed @async @callee_guaranteed (@sil_isolated @sil_implicit_leading_param @guaranteed Optional, @guaranteed SendableKlass) -> ()) -> () { +// FIVE: bb0([[ARG:%.*]] : @guaranteed $SendableKlass, [[FUNC:%.*]] : @guaranteed +// FIVE: [[ACTOR:%.*]] = apply {{%.*}}({{%.*}}) : $@convention(method) (@thick MainActor.Type) -> @owned MainActor +// FIVE: [[E:%.*]] = init_existential_ref [[ACTOR]] : $MainActor : $MainActor, $any Actor +// FIVE: [[E_OPT:%.*]] = enum $Optional, #Optional.some!enumelt, [[E]] +// FIVE: hop_to_executor [[E_OPT]] +// FIVE: apply [[FUNC]]([[E_OPT]], [[ARG]]) +// FIVE: } // end sil function '$sScA_pSg21attr_execution_silgen13SendableKlassCIegHgILg_ADIegHg_TRScMTU + +// SIX-LABEL: sil shared [transparent] [serialized] [reabstraction_thunk] [ossa] @$sScA_pSg21attr_execution_silgen13SendableKlassCIetHgILg_ADIeghHg_TRScMTU : $@convention(thin) @Sendable @async (@guaranteed SendableKlass, @convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional, @guaranteed SendableKlass) -> ()) -> () { +// SIX: bb0([[ARG:%.*]] : @guaranteed $SendableKlass, [[FUNC:%.*]] : $@convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional, @guaranteed SendableKlass) -> ()): +// SIX: [[ACTOR:%.*]] = apply {{%.*}}({{%.*}}) : $@convention(method) (@thick MainActor.Type) -> @owned MainActor +// SIX: [[E:%.*]] = init_existential_ref [[ACTOR]] : $MainActor : $MainActor, $any Actor +// SIX: [[E_OPT:%.*]] = enum $Optional, #Optional.some!enumelt, [[E]] +// SIX: hop_to_executor [[E_OPT]] +// SIX: apply [[FUNC]]([[E_OPT]], [[ARG]]) +// SIX: // end sil function '$sScA_pSg21attr_execution_silgen13SendableKlassCIetHgILg_ADIeghHg_TRScMTU' + +// CHECK-LABEL: sil shared [transparent] [serialized] [reabstraction_thunk] [ossa] @$sScA_pSg21attr_execution_silgen13SendableKlassCIegHgILg_ADIegHg_TR : $@convention(thin) @async (@guaranteed SendableKlass, @guaranteed @async @callee_guaranteed (@sil_isolated @sil_implicit_leading_param @guaranteed Optional, @guaranteed SendableKlass) -> ()) -> () { +// CHECK: bb0([[ARG:%.*]] : @guaranteed $SendableKlass, [[FUNC:%.*]] : @guaranteed +// CHECK: [[ACTOR:%.*]] = enum $Optional, #Optional.none!enumelt +// CHECK: hop_to_executor [[ACTOR]] +// CHECK: apply [[FUNC]]([[ACTOR]], [[ARG]]) +// CHECK: } // end sil function '$sScA_pSg21attr_execution_silgen13SendableKlassCIegHgILg_ADIegHg_TR' func globalActorConversions2(_ x: @escaping @concurrent (SendableKlass) async -> (), _ y: nonisolated(nonsending) @escaping (SendableKlass) async -> ()) async { let v1: @MainActor (SendableKlass) async -> Void = globalCallerFuncSendableKlass @@ -380,6 +426,30 @@ func globalActorConversions2(_ x: @escaping @concurrent (SendableKlass) async -> // CHECK: [[PA:%.*]] = partial_apply [callee_guaranteed] [[THUNK]]([[Y_C]]) // CHECK: [[V5:%.*]] = move_value [lexical] [var_decl] [[PA]] // CHECK: } // end sil function '$s21attr_execution_silgen23globalActorConversions3yyAA13SendableKlassCADYac_A2DYaYCctYaF' + +// FIVE-LABEL: sil shared [transparent] [serialized] [reabstraction_thunk] [ossa] @$sScA_pSg21attr_execution_silgen13SendableKlassCADIegHgILgo_A2DIegHgo_TRScMTU : $@convention(thin) @async (@guaranteed SendableKlass, @guaranteed @async @callee_guaranteed (@sil_isolated @sil_implicit_leading_param @guaranteed Optional, @guaranteed SendableKlass) -> @owned SendableKlass) -> @owned SendableKlass { +// FIVE: bb0([[ARG:%.*]] : @guaranteed $SendableKlass, [[FUNC:%.*]] : @guaranteed +// FIVE: [[ACTOR:%.*]] = apply {{%.*}}({{%.*}}) : $@convention(method) (@thick MainActor.Type) -> @owned MainActor +// FIVE: [[E:%.*]] = init_existential_ref [[ACTOR]] : $MainActor : $MainActor, $any Actor +// FIVE: [[E_OPT:%.*]] = enum $Optional, #Optional.some!enumelt, [[E]] +// FIVE: hop_to_executor [[E_OPT]] +// FIVE: apply [[FUNC]]([[E_OPT]], [[ARG]]) +// FIVE: } // end sil function '$sScA_pSg21attr_execution_silgen13SendableKlassCADIegHgILgo_A2DIegHgo_TRScMTU' + +// SIX-LABEL: sil shared [transparent] [serialized] [reabstraction_thunk] [ossa] @$sScA_pSg21attr_execution_silgen13SendableKlassCADIetHgILgo_A2DIeghHgo_TRScMTU : $@convention(thin) @Sendable @async (@guaranteed SendableKlass, @convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional, @guaranteed SendableKlass) -> @owned SendableKlass) -> @owned SendableKlass { +// SIX: bb0([[ARG:%.*]] : @guaranteed $SendableKlass, [[FUNC:%.*]] : $@convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional, @guaranteed SendableKlass) -> @owned SendableKlass): +// SIX: [[ACTOR:%.*]] = apply {{%.*}}({{%.*}}) : $@convention(method) (@thick MainActor.Type) -> @owned MainActor +// SIX: [[E:%.*]] = init_existential_ref [[ACTOR]] : $MainActor : $MainActor, $any Actor +// SIX: [[E_OPT:%.*]] = enum $Optional, #Optional.some!enumelt, [[E]] +// SIX: hop_to_executor [[E_OPT]] +// SIX: apply [[FUNC]]([[E_OPT]], [[ARG]]) +// SIX: } // end sil function '$sScA_pSg21attr_execution_silgen13SendableKlassCADIetHgILgo_A2DIeghHgo_TRScMTU' + +// CHECK-LABEL: sil shared [transparent] [serialized] [reabstraction_thunk] [ossa] @$sScA_pSg21attr_execution_silgen13SendableKlassCADIegHgILgo_A2DIegHgo_TR : $@convention(thin) @async (@guaranteed SendableKlass, @guaranteed @async @callee_guaranteed (@sil_isolated @sil_implicit_leading_param @guaranteed Optional, @guaranteed SendableKlass) -> @owned SendableKlass) -> @owned SendableKlass { +// CHECK: bb0([[ARG:%.*]] : @guaranteed $SendableKlass, [[FUNC:%.*]] : @guarantee +// CHECK: [[ACTOR:%.*]] = enum $Optional, #Optional.none!enumelt +// CHECK: hop_to_executor [[ACTOR]] +// CHECK: } // end sil function '$sScA_pSg21attr_execution_silgen13SendableKlassCADIegHgILgo_A2DIegHgo_TR' func globalActorConversions3(_ x: @escaping @concurrent (SendableKlass) async -> SendableKlass, _ y: nonisolated(nonsending) @escaping (SendableKlass) async -> SendableKlass) async { let v1: @MainActor (SendableKlass) async -> SendableKlass = globalCallerFuncSendableKlass @@ -413,6 +483,30 @@ func globalActorConversions3(_ x: @escaping @concurrent (SendableKlass) async -> // CHECK: } // end sil function '$s21attr_execution_silgen26conversionsFromSyncToAsyncyyyAA16NonSendableKlassCYbc_yAA0jK0CYbScMYcctYaF' +// CHECK-LABEL: sil shared [transparent] [serialized] [reabstraction_thunk] [ossa] @$s21attr_execution_silgen16NonSendableKlassCIeghg_ScA_pSgACIegHgILg_TR : $@convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional, @guaranteed NonSendableKlass, @guaranteed @Sendable @callee_guaranteed (@guaranteed NonSendableKlass) -> ()) -> () { +// CHECK: bb0([[ACTOR:%.*]] : @guaranteed $Optional +// CHECK: apply {{%.*}}({{%.*}}) : $@Sendable @callee_guaranteed (@guaranteed NonSendableKlass) -> () +// CHECK: hop_to_executor [[ACTOR]] +// CHECK: } // end sil function '$s21attr_execution_silgen16NonSendableKlassCIeghg_ScA_pSgACIegHgILg_TR' + +// CHECK-LABEL: sil shared [transparent] [serialized] [reabstraction_thunk] [ossa] @$s21attr_execution_silgen13SendableKlassCIeghg_ScA_pSgACIegHgILg_TRScMTU : $@convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional, @guaranteed SendableKlass, @guaranteed @Sendable @callee_guaranteed (@guaranteed SendableKlass) -> ()) -> () { +// CHECK: bb0([[ACTOR:%.*]] : @guaranteed $Optional, [[ARG:%.*]] : @guaranteed $SendableKlass, [[FUNC:%.*]] : @guaranteed $@Sendable @callee_guaranteed +// CHECK: [[MAIN_ACTOR:%.*]] = apply {{%.*}}({{%.*}}) : $@convention(method) (@thick MainActor.Type) -> @owned MainActor +// CHECK: [[MAIN_ACTOR_B:%.*]] = begin_borrow [[MAIN_ACTOR]] +// CHECK: hop_to_executor [[MAIN_ACTOR_B]] +// CHECK: apply [[FUNC]]([[ARG]]) +// CHECK: hop_to_executor [[ACTOR]] +// CHECK: } // end sil function '$s21attr_execution_silgen13SendableKlassCIeghg_ScA_pSgACIegHgILg_TRScMTU' + +// CHECK-LABEL: sil shared [transparent] [serialized] [reabstraction_thunk] [ossa] @$s21attr_execution_silgen13SendableKlassCIeghg_ACIegHg_TRScMTU : $@convention(thin) @async (@guaranteed SendableKlass, @guaranteed @Sendable @callee_guaranteed (@guaranteed SendableKlass) -> ()) -> () { +// CHECK: bb0([[ARG:%.*]] : @guaranteed $SendableKlass, [[FUNC:%.*]] : @guaranteed $@Sendable @callee_guaranteed (@guaranteed SendableKlass) -> ()): +// CHECK: [[MAIN_ACTOR:%.*]] = apply {{%.*}}({{%.*}}) : $@convention(method) (@thick MainActor.Type) -> @owned MainActor +// CHECK: [[MAIN_ACTOR_B:%.*]] = begin_borrow [[MAIN_ACTOR]] +// CHECK: hop_to_executor [[MAIN_ACTOR_B]] +// CHECK-NOT: hop_to_executor +// CHECK: apply [[FUNC]]([[ARG]]) +// CHECK: } // end sil function '$s21attr_execution_silgen13SendableKlassCIeghg_ACIegHg_TRScMTU' + func conversionsFromSyncToAsync(_ x: @escaping @Sendable (NonSendableKlass) -> Void, _ y: @escaping @MainActor @Sendable (SendableKlass) -> Void) async { let _: nonisolated(nonsending) (NonSendableKlass) async -> Void = x @@ -433,6 +527,20 @@ func testThatClosuresAssumeIsolation(fn: inout nonisolated(nonsending) (Int) asy // CHECK-LABEL: sil private [ossa] @$s21attr_execution_silgen31testThatClosuresAssumeIsolation2fnyySiYaYCcz_tFyyYaXEfU0_ : $@convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional) -> () { // CHECK: bb0([[EXECUTOR:%.*]] : @guaranteed $Optional): // CHECK: hop_to_executor [[EXECUTOR]] + // CHECK: } // end sil function '$s21attr_execution_silgen31testThatClosuresAssumeIsolation2fnyySiYaYCcz_tFyyYaXEfU0_' + + // FIVE-LABEL: sil shared [transparent] [serialized] [reabstraction_thunk] [ossa] @$sScA_pSgIetHgIL_IegH_TR : $@convention(thin) @async (@convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional) -> ()) -> () { + // FIVE: bb0([[FUNC:%.*]] : $@convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional) -> ()): + // FIVE: [[ACTOR:%.*]] = enum $Optional, #Optional.none!enumelt + // FIVE: hop_to_executor [[ACTOR]] + // FIVE: apply [[FUNC]]([[ACTOR]]) + // FIVE: } // end sil function '$sScA_pSgIetHgIL_IegH_TR' + + // CHECK-LABEL: sil shared [transparent] [serialized] [reabstraction_thunk] [ossa] @$sIgH_ScA_pSgs5Error_pIegHgILzo_TR : $@convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional, @guaranteed @noescape @async @callee_guaranteed () -> ()) -> @error any Error { + // CHECK: bb0([[ACTOR:%.*]] : @guaranteed $Optional, [[FUNC:%.*]] : @guaranteed $@noescape @async @callee_guaranteed () -> ()): + // CHECK: apply [[FUNC]]() + // CHECK: hop_to_executor [[ACTOR]] + // CHECK: } // end sil function '$sIgH_ScA_pSgs5Error_pIegHgILzo_TR' testParam { 42 } // CHECK-LABEL: sil private [ossa] @$s21attr_execution_silgen31testThatClosuresAssumeIsolation2fnyySiYaYCcz_tFyyYaXEfU1_ : $@convention(thin) @async () -> () @@ -448,6 +556,13 @@ func testThatClosuresAssumeIsolation(fn: inout nonisolated(nonsending) (Int) asy // CHECK-LABEL: sil private [ossa] @$s21attr_execution_silgen31testThatClosuresAssumeIsolation2fnyySiYaYCcz_tFySiYacfU3_ : $@convention(thin) @async (Int) -> () // CHECK: [[GENERIC_EXECUTOR:%.*]] = enum $Optional, #Optional.none!enumelt // CHECK: hop_to_executor [[GENERIC_EXECUTOR]] + // CHECK: } // end sil function '$s21attr_execution_silgen31testThatClosuresAssumeIsolation2fnyySiYaYCcz_tFySiYacfU3_' + + // CHECK-LABEL: sil shared [transparent] [serialized] [reabstraction_thunk] [ossa] @$sSiIegHy_ScA_pSgSiIegHgILy_TR : $@convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional, Int, @guaranteed @async @callee_guaranteed (Int) -> ()) -> () { + // CHECK: bb0([[ACTOR:%.*]] : @guaranteed $Optional, [[ARG:%.*]] : $Int, [[FUNC:%.*]] : @guaranteed $@async @callee_guaranteed (Int) -> ()): + // CHECK: apply [[FUNC]]([[ARG]]) + // CHECK: hop_to_executor [[ACTOR]] + // CHECK: } // end sil function '$sSiIegHy_ScA_pSgSiIegHgILy_TR' fn = { @concurrent _ in } } diff --git a/test/Concurrency/attr_execution/protocols_silgen.swift b/test/Concurrency/attr_execution/protocols_silgen.swift index 76381de5b7c31..e5c2dd440e751 100644 --- a/test/Concurrency/attr_execution/protocols_silgen.swift +++ b/test/Concurrency/attr_execution/protocols_silgen.swift @@ -24,6 +24,7 @@ struct AllDefault : P { // CHECK: [[LOAD:%.*]] = load [trivial] [[SELF]] // CHECK: [[FUNC:%.*]] = function_ref @$s21attr_execution_silgen10AllDefaultV10callerTestyyYaF : $@convention(method) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional, AllDefault) -> () // CHECK: apply [[FUNC]]([[ACTOR]], [[LOAD]]) + // CHECK: hop_to_executor [[ACTOR]] // CHECK: } // end sil function '$s21attr_execution_silgen10AllDefaultVAA1PA2aDP10callerTestyyYaFTW' func callerTest() async {} @@ -50,6 +51,7 @@ struct AllCaller : P { // CHECK: [[LOAD:%.*]] = load [trivial] [[SELF]] // CHECK: [[FUNC:%.*]] = function_ref @$s21attr_execution_silgen9AllCallerV10callerTestyyYaF : $@convention(method) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional, AllCaller) -> () // CHECK: apply [[FUNC]]([[ACTOR]], [[LOAD]]) + // CHECK: hop_to_executor [[ACTOR]] // CHECK: } // end sil function '$s21attr_execution_silgen9AllCallerVAA1PA2aDP10callerTestyyYaFTW' nonisolated(nonsending) func callerTest() async {} @@ -58,6 +60,7 @@ struct AllCaller : P { // CHECK: [[LOAD:%.*]] = load [trivial] [[SELF]] // CHECK: [[FUNC:%.*]] = function_ref @$s21attr_execution_silgen9AllCallerV14concurrentTestyyYaF : $@convention(method) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional, AllCaller) -> () // CHECK: [[NIL:%.*]] = enum $Optional, #Optional.none!enumelt + // CHECK: hop_to_executor [[NIL]] // CHECK: apply [[FUNC]]([[NIL]], [[LOAD]]) // CHECK: } // end sil function '$s21attr_execution_silgen9AllCallerVAA1PA2aDP14concurrentTestyyYaFTW' nonisolated(nonsending) func concurrentTest() async {} @@ -69,6 +72,7 @@ struct AllCaller : P { // CHECK: [[MAIN_ACTOR:%.*]] = apply {{%.*}}({{%.*}}) : $@convention(method) (@thick MainActor.Type) -> @owned MainActor // CHECK: [[EXIS_MAIN_ACTOR:%.*]] = init_existential_ref [[MAIN_ACTOR]] // CHECK: [[OPT_MAIN_ACTOR:%.*]] = enum $Optional, #Optional.some!enumelt, [[EXIS_MAIN_ACTOR]] + // CHECK: hop_to_executor [[OPT_MAIN_ACTOR]] // CHECK: apply [[FUNC]]([[OPT_MAIN_ACTOR]], [[LOAD]]) // CHECK: } // end sil function '$s21attr_execution_silgen9AllCallerVAA1PA2aDP13mainActorTestyyYaFTW' nonisolated(nonsending) func mainActorTest() async {} @@ -80,6 +84,7 @@ struct AllConcurrent : P { // CHECK: [[LOAD:%.*]] = load [trivial] [[SELF]] // CHECK: [[FUNC:%.*]] = function_ref @$s21attr_execution_silgen13AllConcurrentV10callerTestyyYaF : $@convention(method) @async (AllConcurrent) -> () // CHECK: apply [[FUNC]]([[LOAD]]) + // CHECK: hop_to_executor [[ACTOR]] // CHECK: } // end sil function '$s21attr_execution_silgen13AllConcurrentVAA1PA2aDP10callerTestyyYaFTW' @concurrent func callerTest() async {} @@ -106,6 +111,7 @@ struct AllMainActor : P { // CHECK: [[LOAD:%.*]] = load [trivial] [[SELF]] // CHECK: [[FUNC:%.*]] = function_ref @$s21attr_execution_silgen12AllMainActorV10callerTestyyYaF : $@convention(method) @async (AllMainActor) -> () // CHECK: apply [[FUNC]]([[LOAD]]) + // CHECK: hop_to_executor [[ACTOR]] // CHECK: } // end sil function '$s21attr_execution_silgen12AllMainActorVAA1PA2aDP10callerTestyyYaFTW' @MainActor func callerTest() async {} @@ -141,6 +147,7 @@ struct TestWitnessWithStorage: Q { // CHECK-NEXT: [[BORROWED_SELF:%.*]] = load_borrow [[SELF]] // CHECK: [[WITNESS:%.*]] = function_ref @$s21attr_execution_silgen22TestWitnessWithStorageV4testSSvg : $@convention(method) (@guaranteed TestWitnessWithStorage) -> @owned String // CHECK-NEXT: [[RESULT:%.*]] = apply [[WITNESS]]([[BORROWED_SELF]]) : $@convention(method) (@guaranteed TestWitnessWithStorage) -> @owned String + // CHECK-NEXT: hop_to_executor [[ISOLATION]] // CHECK-NEXT: end_borrow [[BORROWED_SELF]] // CHECK-NEXT: return [[RESULT]] // CHECK-NEXT: } // end sil function '$s21attr_execution_silgen22TestWitnessWithStorageVAA1QA2aDP4testSSvgTW' @@ -151,6 +158,7 @@ struct TestWitnessWithStorage: Q { // CHECK: [[WITNESS:%.*]] = function_ref @$s21attr_execution_silgen22TestWitnessWithStorageV02fnD0yyF : $@convention(method) (@guaranteed TestWitnessWithStorage) -> () // CHECK-NEXT: {{.*}} = apply [[WITNESS]]([[BORROWED_SELF]]) : $@convention(method) (@guaranteed TestWitnessWithStorage) -> () // CHECK-NEXT: [[RESULT:%.*]] = tuple () + // CHECK-NEXT: hop_to_executor [[ISOLATION]] // CHECK-NEXT: end_borrow [[BORROWED_SELF]] // CHECK-NEXT: return [[RESULT]] // CHECK-NEXT: } // end sil function '$s21attr_execution_silgen22TestWitnessWithStorageVAA1QA2aDP02fnD0yyYaFTW' @@ -165,6 +173,7 @@ struct TestSyncWitness: Q { // CHECK-NEXT: [[BORROWED_SELF:%.*]] = load [trivial] [[SELF]] // CHECK: [[WITNESS:%.*]] = function_ref @$s21attr_execution_silgen15TestSyncWitnessV4testSSvg : $@convention(method) (TestSyncWitness) -> @owned String // CHECK-NEXT: [[RESULT:%.*]] = apply [[WITNESS]]([[BORROWED_SELF]]) : $@convention(method) (TestSyncWitness) -> @owned String + // CHECK-NEXT: hop_to_executor [[ISOLATION]] // CHECK-NEXT: return [[RESULT]] // CHECK-NEXT: } // end sil function '$s21attr_execution_silgen15TestSyncWitnessVAA1QA2aDP4testSSvgTW' diff --git a/test/Concurrency/nonisolated_nonsending_optimize_hoptoexecutor.swift b/test/Concurrency/nonisolated_nonsending_optimize_hoptoexecutor.swift new file mode 100644 index 0000000000000..69cc4c4a22e0e --- /dev/null +++ b/test/Concurrency/nonisolated_nonsending_optimize_hoptoexecutor.swift @@ -0,0 +1,34 @@ +// RUN: %target-swift-frontend -module-name test -swift-version 6 -emit-sil %s | %FileCheck --implicit-check-not=hop_to_executor %s + +// REQUIRES: concurrency + +// CHECK-LABEL: sil hidden [noinline] @$s4testAAyyYaF : $@convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional) -> () { +// CHECK: hop_to_executor +// CHECK: } // end sil function '$s4testAAyyYaF' +@inline(never) +nonisolated(nonsending) func test() async {} + +// CHECK-LABEL: sil hidden [noinline] @$s4test5test2yyYaF : $@convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional) -> () { +// CHECK: hop_to_executor +// CHECK: hop_to_executor +// CHECK: } // end sil function '$s4test5test2yyYaF' +@inline(never) +nonisolated(nonsending) func test2() async { + await test() +} + +@inline(never) +func test3() async { +} + +// CHECK-LABEL: sil @$s4test6calleryyYaF : $@convention(thin) @async () -> () { +// CHECK: hop_to_executor +// CHECK: function_ref @$s4testAAyyYaF : $@convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional) -> () +// CHECK: hop_to_executor +// CHECK: function_ref @$s4test5test2yyYaF : $@convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional) -> () +// CHECK: } // end sil function '$s4test6calleryyYaF' +public func caller() async { + await test() + await test2() + await test3() +} diff --git a/test/SIL/Parser/actor_isolation.sil b/test/SIL/Parser/actor_isolation.sil new file mode 100644 index 0000000000000..fc315037ae150 --- /dev/null +++ b/test/SIL/Parser/actor_isolation.sil @@ -0,0 +1,39 @@ +// RUN: %target-sil-opt -sil-print-function-isolation-info -enable-objc-interop -enable-sil-verify-all=true %s | %target-sil-opt -sil-print-function-isolation-info -enable-objc-interop -enable-sil-verify-all=true | %FileCheck %s + +// REQUIRES: asserts + +// CHECK: // func_with_caller_isolation_inheriting +// CHECK: // Isolation: caller_isolation_inheriting +// CHECK: sil [isolation "caller_isolation_inheriting"] @func_with_caller_isolation_inheriting : $@convention(thin) () -> () { +sil [isolation "caller_isolation_inheriting"] @func_with_caller_isolation_inheriting : $@convention(thin) () -> () { +bb0: + %0 = tuple () + return %0 : $() +} + +// CHECK: // func_with_global_actor_isolation +// CHECK: // Isolation: global_actor. type: +// CHECK: sil [isolation "global_actor"] @func_with_global_actor_isolation : $@convention(thin) () -> () { +sil [isolation "global_actor"] @func_with_global_actor_isolation : $@convention(thin) () -> () { +bb0: + %0 = tuple () + return %0 : $() +} + +// CHECK: // func_with_actor_instance_isolation +// CHECK: // Isolation: actor_instance +// CHECK: sil [isolation "actor_instance"] @func_with_actor_instance_isolation : $@convention(thin) () -> () { +sil [isolation "actor_instance"] @func_with_actor_instance_isolation : $@convention(thin) () -> () { +bb0: + %0 = tuple () + return %0 : $() +} + +// CHECK: // func_with_nonisolated_isolation +// CHECK: // Isolation: nonisolated +// CHECK: sil [isolation "nonisolated"] @func_with_nonisolated_isolation : $@convention(thin) () -> () { +sil [isolation "nonisolated"] @func_with_nonisolated_isolation : $@convention(thin) () -> () { +bb0: + %0 = tuple () + return %0 : $() +} \ No newline at end of file diff --git a/test/SILGen/objc_async_from_swift.swift b/test/SILGen/objc_async_from_swift.swift index 5179077f1d35b..49bd3b9705fbb 100644 --- a/test/SILGen/objc_async_from_swift.swift +++ b/test/SILGen/objc_async_from_swift.swift @@ -117,6 +117,89 @@ protocol NativelySlowServing { extension SlowServer: NativelySlowServing {} +// protocol witness for NativelySlowServing.doSomethingSlow(_:) in conformance SlowServer +// +// CHECK-C-LABEL: sil private [transparent] [thunk] [ossa] @$sSo10SlowServerC21objc_async_from_swift08NativelyA7ServingA2cDP011doSomethingA0ySiSSYaFTW : $@convention(witness_method: NativelySlowServing) @async (@guaranteed String, @in_guaranteed SlowServer) -> Int { +// CHECK-C: bb0([[ARG:%.*]] : @guaranteed $String, [[SELF_ADDR:%.*]] : $*SlowServer): +// CHECK-C: [[SELF:%.*]] = load_borrow [[SELF_ADDR]] +// CHECK-C: [[FUNC:%.*]] = function_ref @$sSo10SlowServerC011doSomethingA0ySiSSYaFTO : $@convention(method) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional, @guaranteed String, @guaranteed SlowServer) -> Int +// CHECK-C: [[ACTOR:%.*]] = enum $Optional, #Optional.none!enumelt +// CHECK-C: hop_to_executor [[ACTOR]] +// CHECK-C: apply [[FUNC]]([[ACTOR]], [[ARG]], [[SELF]]) +// CHECK-C: } // end sil function '$sSo10SlowServerC21objc_async_from_swift08NativelyA7ServingA2cDP011doSomethingA0ySiSSYaFTW' + +// CHECK-NN-LABEL: sil private [transparent] [thunk] [ossa] @$sSo10SlowServerC21objc_async_from_swift08NativelyA7ServingA2cDP011doSomethingA0ySiSSYaFTW : $@convention(witness_method: NativelySlowServing) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional, @guaranteed String, @in_guaranteed SlowServer) -> Int { +// CHECK-NN: bb0([[ACTOR:%.*]] : @guaranteed $Optional, [[ARG:%.*]] : @guaranteed $String, [[SELF_ADDR:%.*]] : $*SlowServer): +// CHECK-NN: [[SELF:%.*]] = load_borrow [[SELF_ADDR]] +// CHECK-NN: [[FUNC:%.*]] = function_ref @$sSo10SlowServerC011doSomethingA0ySiSSYaFTO : $@convention(method) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional, @guaranteed String, @guaranteed SlowServer) -> Int +// CHECK-NN: apply [[FUNC]]([[ACTOR]], [[ARG]], [[SELF]]) +// CHECK-NN: hop_to_executor [[ACTOR]] +// CHECK-NN: } // end sil function '$sSo10SlowServerC21objc_async_from_swift08NativelyA7ServingA2cDP011doSomethingA0ySiSSYaFTW' + +// protocol witness for NativelySlowServing.findAnswer() in conformance SlowServer +// CHECK-C-LABEL: sil private [transparent] [thunk] [ossa] @$sSo10SlowServerC21objc_async_from_swift08NativelyA7ServingA2cDP10findAnswerSSyYaKFTW : $@convention(witness_method: NativelySlowServing) @async (@in_guaranteed SlowServer) -> (@owned String, @error any Error) { +// CHECK-C: bb0([[SELF_ADDR:%.*]] : $*SlowServer): +// CHECK-C: [[SELF:%.*]] = load_borrow [[SELF_ADDR]] +// CHECK-C: [[FUNC:%.*]] = function_ref @$sSo10SlowServerC10findAnswerSSyYaKFTO : $@convention(method) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional, @guaranteed SlowServer) -> (@owned String, @error any Error) +// CHECK-C: [[ACTOR:%.*]] = enum $Optional, #Optional.none!enumelt +// CHECK-C: hop_to_executor [[ACTOR]] +// CHECK-C: try_apply [[FUNC]]([[ACTOR]], [[SELF]]) +// CHECK-C: } // end sil function '$sSo10SlowServerC21objc_async_from_swift08NativelyA7ServingA2cDP10findAnswerSSyYaKFTW' + +// CHECK-NN-LABEL: sil private [transparent] [thunk] [ossa] @$sSo10SlowServerC21objc_async_from_swift08NativelyA7ServingA2cDP10findAnswerSSyYaKFTW : $@convention(witness_method: NativelySlowServing) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional, @in_guaranteed SlowServer) -> (@owned String, @error any Error) { +// CHECK-NN: bb0([[ACTOR:%.*]] : @guaranteed $Optional, [[SELF_ADDR:%.*]] : $*SlowServer): +// CHECK-NN: [[SELF:%.*]] = load_borrow [[SELF_ADDR]] +// CHECK-NN: [[FUNC:%.*]] = function_ref @$sSo10SlowServerC10findAnswerSSyYaKFTO : $@convention(method) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional, @guaranteed SlowServer) -> (@owned String, @error any Error) +// CHECK-NN: try_apply [[FUNC]]([[ACTOR]], [[SELF]]) +// +// CHECK-NN: bb1( +// CHECK-NN: hop_to_executor [[ACTOR]] +// +// CHECK-NN: bb2( +// CHECK-NN: hop_to_executor [[ACTOR]] +// CHECK-NN: } // end sil function '$sSo10SlowServerC21objc_async_from_swift08NativelyA7ServingA2cDP10findAnswerSSyYaKFTW' + +// protocol witness for NativelySlowServing.serverRestart(_:) in conformance SlowServer +// CHECK-C-LABEL: sil private [transparent] [thunk] [ossa] @$sSo10SlowServerC21objc_async_from_swift08NativelyA7ServingA2cDP13serverRestartyySSYaFTW : $@convention(witness_method: NativelySlowServing) @async (@guaranteed String, @in_guaranteed SlowServer) -> () { +// CHECK-C: bb0([[ARG:%.*]] : @guaranteed $String, [[SELF_PTR:%.*]] : $*SlowServer): +// CHECK-C: [[SELF:%.*]] = load_borrow [[SELF_PTR]] +// CHECK-C: [[FUNC:%.*]] = function_ref @$sSo10SlowServerC13serverRestartyySSYaFTO : $@convention(method) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional, @guaranteed String, @guaranteed SlowServer) -> () +// CHECK-C: [[ACTOR:%.*]] = enum $Optional, #Optional.none!enumelt +// CHECK-C: hop_to_executor [[ACTOR]] +// CHECK-C: apply [[FUNC]]([[ACTOR]], [[ARG]], [[SELF]]) +// CHECK-C: } // end sil function '$sSo10SlowServerC21objc_async_from_swift08NativelyA7ServingA2cDP13serverRestartyySSYaFTW' + +// CHECK-NN-LABEL: sil private [transparent] [thunk] [ossa] @$sSo10SlowServerC21objc_async_from_swift08NativelyA7ServingA2cDP13serverRestartyySSYaFTW : $@convention(witness_method: NativelySlowServing) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional, @guaranteed String, @in_guaranteed SlowServer) -> () { +// CHECK-NN: bb0([[ACTOR:%.*]] : @guaranteed $Optional, [[ARG:%.*]] : @guaranteed $String, [[SELF_PTR:%.*]] : $*SlowServer): +// CHECK-NN: [[SELF:%.*]] = load_borrow [[SELF_PTR]] +// CHECK-NN: [[FUNC:%.*]] = function_ref @$sSo10SlowServerC13serverRestartyySSYaFTO : $@convention(method) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional, @guaranteed String, @guaranteed SlowServer) -> () +// CHECK-NN: apply [[FUNC]]([[ACTOR]], [[ARG]], [[SELF]]) +// CHECK-NN: hop_to_executor [[ACTOR]] +// CHECK-NN: } // end sil function '$sSo10SlowServerC21objc_async_from_swift08NativelyA7ServingA2cDP13serverRestartyySSYaFTW' + +// protocol witness for NativelySlowServing.findMultipleAnswers() in conformance SlowServer +// CHECK-C-LABEL: sil private [transparent] [thunk] [ossa] @$sSo10SlowServerC21objc_async_from_swift08NativelyA7ServingA2cDP19findMultipleAnswersSS_SityYaKFTW : $@convention(witness_method: NativelySlowServing) @async (@in_guaranteed SlowServer) -> (@owned String, Int, @error any Error) { +// CHECK-C: bb0([[SELF_ADDR:%.*]] : $*SlowServer): +// CHECK-C: [[SELF:%.*]] = load_borrow [[SELF_ADDR]] +// CHECK-C: [[FUNC:%.*]] = function_ref @$sSo10SlowServerC19findMultipleAnswersSS_SityYaKFTO : $@convention(method) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional, @guaranteed SlowServer) -> (@owned String, Int, @error any Error) +// CHECK-C: [[ACTOR:%.*]] = enum $Optional, #Optional.none!enumelt +// CHECK-C: hop_to_executor [[ACTOR]] +// CHECK-C: try_apply [[FUNC]]([[ACTOR]], [[SELF]]) +// CHECK-C: } // end sil function '$sSo10SlowServerC21objc_async_from_swift08NativelyA7ServingA2cDP19findMultipleAnswersSS_SityYaKFTW' + +// CHECK-NN-LABEL: sil private [transparent] [thunk] [ossa] @$sSo10SlowServerC21objc_async_from_swift08NativelyA7ServingA2cDP19findMultipleAnswersSS_SityYaKFTW : $@convention(witness_method: NativelySlowServing) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional, @in_guaranteed SlowServer) -> (@owned String, Int, @error any Error) { +// CHECK-NN: bb0([[ACTOR:%.*]] : @guaranteed $Optional, [[SELF_ADDR:%.*]] : $*SlowServer): +// CHECK-NN: [[SELF:%.*]] = load_borrow [[SELF_ADDR]] +// CHECK-NN: [[FUNC:%.*]] = function_ref @$sSo10SlowServerC19findMultipleAnswersSS_SityYaKFTO : $@convention(method) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional, @guaranteed SlowServer) -> (@owned String, Int, @error any Error) +// CHECK-NN: try_apply [[FUNC]]([[ACTOR]], [[SELF]]) +// +// CHECK-NN: bb1( +// CHECK-NN: hop_to_executor [[ACTOR]] +// +// CHECK-NN: bb2( +// CHECK-NN: hop_to_executor [[ACTOR]] +// CHECK-NN: } // end sil function '$sSo10SlowServerC21objc_async_from_swift08NativelyA7ServingA2cDP19findMultipleAnswersSS_SityYaKFTW' + class SlowServerlet: SlowServer { // Native Function // @@ -460,6 +543,15 @@ func testAutoclosureInStaticMethod() { // CHECK-C: } // end sil function '$s21objc_async_from_swift29testAutoclosureInStaticMethodyyF9TestKlassL_C8getValue2id11valueForKeySSSgSS_S2SYaKXEtYaFZfA0_S2SYaKYCcSo10SlowServerCcfu_S2SYaKYCcfu0_' // CHECK-NN: } // end sil function '$s21objc_async_from_swift29testAutoclosureInStaticMethodyyF9TestKlassL_C8getValue2id11valueForKeySSSgSS_S2SYaKYCXEtYaFZfA0_S2SYaKYCcSo10SlowServerCcfu_S2SYaKYCcfu0_' + // thunk for @escaping @callee_guaranteed @async (@guaranteed Actor?, @guaranteed String) -> (@owned String, @error @owned Error) + + // CHECK-C-LABEL: sil shared [transparent] [serialized] [reabstraction_thunk] [ossa] @$sScA_pSgS2Ss5Error_pIegHgILgozo_S2SsAB_pIegHgozo_TR : $@convention(thin) @async (@guaranteed String, @guaranteed @async @callee_guaranteed (@sil_isolated @sil_implicit_leading_param @guaranteed Optional, @guaranteed String) -> (@owned String, @error any Error)) -> (@owned String, @error any Error) { + // CHECK-C: bb0([[ARG:%.*]] : @guaranteed $String, [[FUNC:%.*]] : @guaranteed $@async @callee_guaranteed (@sil_isolated @sil_implicit_leading_param @guaranteed Optional, @guaranteed String) -> (@owned String, @error any Error)): + // CHECK-C: [[ACTOR:%.*]] = enum $Optional, #Optional.none!enumelt + // CHECK-C: hop_to_executor [[ACTOR]] + // CHECK-C: try_apply [[FUNC]]([[ACTOR]], [[ARG]]) + // CHECK-C: } // end sil function '$sScA_pSgS2Ss5Error_pIegHgILgozo_S2SsAB_pIegHgozo_TR' + // Actual static method // // We do not actually care about FileChecking this... we just need to @@ -474,13 +566,14 @@ func testAutoclosureInStaticMethod() { // CHECK-C: hop_to_executor [[EXEC_NONE]] // CHECK-C: hop_to_executor [[EXEC_NONE]] // CHECK-C: } // end sil function '$s21objc_async_from_swift29testAutoclosureInStaticMethodyyF9TestKlassL_C8getValue2id11valueForKeySSSgSS_S2SYaKXEtYaFZ' - // + // CHECK-NN-LABEL: sil private [ossa] @$s21objc_async_from_swift29testAutoclosureInStaticMethodyyF9TestKlassL_C8getValue2id11valueForKeySSSgSS_S2SYaKYCXEtYaFZ : $@convention(method) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional, @guaranteed String, @guaranteed @noescape @async @callee_guaranteed (@sil_isolated @sil_implicit_leading_param @guaranteed Optional, @guaranteed String) -> (@owned String, @error any Error), @thick TestKlass.Type) -> @owned Optional { // CHECK-NN: bb0([[ACTOR:%.*]] : @guaranteed $Optional, [[STRING:%.*]] : @guaranteed $String, [[COMPLETION:%.*]] : @guaranteed $@noescape @async @callee_guaranteed (@sil_isolated @sil_implicit_leading_param @guaranteed Optional, @guaranteed String) -> (@owned String, @error any Error), %3 : $@thick TestKlass.Type): // CHECK-NN: hop_to_executor [[ACTOR]] // CHECK-NN: hop_to_executor [[ACTOR]] // CHECK-NN: hop_to_executor [[ACTOR]] // CHECK-NN: } // end sil function '$s21objc_async_from_swift29testAutoclosureInStaticMethodyyF9TestKlassL_C8getValue2id11valueForKeySSSgSS_S2SYaKYCXEtYaFZ' + static func getValue(id: String, valueForKey: (_ identifier: String) async throws -> String = SlowServer.standard.value(withKey:)) async -> String? { let result: String @@ -579,6 +672,21 @@ func testAutoclosureInStaticMethod() { // CHECK-C: } // end sil function '$s21objc_async_from_swift29testAutoclosureInStaticMethodyyF9TestKlassL_C17getMainActorValue2id11valueForKeySSSgSS_S2SYaKXEtYaFZfA0_S2SYaKScMYccSo10SlowServerCcfu_S2SYaKScMYccfu0_' // CHECK-NN: } // end sil function '$s21objc_async_from_swift29testAutoclosureInStaticMethodyyF9TestKlassL_C17getMainActorValue2id11valueForKeySSSgSS_S2SYaKYCXEtYaFZfA0_S2SYaKScMYccSo10SlowServerCcfu_S2SYaKScMYccfu0_' + // NOTE: This is earlier in the file when not compiling with + // nonisolated(nonsending) by default just due to the way the compiler emits + // thunks at different times due to different usages. + // + // CHECK-NN-LABEL: sil shared [transparent] [serialized] [reabstraction_thunk] [ossa] @$sS2Ss5Error_pIegHgozo_ScA_pSgS2SsAA_pIegHgILgozo_TR : $@convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional, @guaranteed String, @guaranteed @async @callee_guaranteed (@guaranteed String) -> (@owned String, @error any Error)) -> (@owned String, @error any Error) { + // CHECK-NN: bb0([[ACTOR:%.*]] : @guaranteed $Optional, [[ARG:%.*]] : @guaranteed $String, [[FUNC:%.*]] : @guaranteed $@async @callee_guaranteed (@guaranteed String) -> (@owned String, @error any Error)): + // CHECK-NN: try_apply [[FUNC]]([[ARG]]) + // + // CHECK-NN: bb1( + // CHECK-NN: hop_to_executor [[ACTOR]] + // + // CHECK-NN: bb2( + // CHECK-NN: hop_to_executor [[ACTOR]] + // CHECK-NN: } // end sil function '$sS2Ss5Error_pIegHgozo_ScA_pSgS2SsAA_pIegHgILgozo_TR' + // Actual static method // // This is not main actor isolated diff --git a/test/SILOptimizer/optimize_hop_to_executor.sil b/test/SILOptimizer/optimize_hop_to_executor.sil index 6f5991055b8e5..e1e7292536696 100644 --- a/test/SILOptimizer/optimize_hop_to_executor.sil +++ b/test/SILOptimizer/optimize_hop_to_executor.sil @@ -1,6 +1,7 @@ -// RUN: %target-sil-opt -enable-sil-verify-all %s -optimize-hop-to-executor | %FileCheck %s +// RUN: %target-sil-opt -sil-print-function-isolation-info -enable-sil-verify-all %s -optimize-hop-to-executor | %FileCheck %s // REQUIRES: concurrency +// REQUIRES: asserts sil_stage canonical @@ -13,6 +14,7 @@ actor MyActor { sil [ossa] @requiredToRunOnActor : $@convention(method) (@guaranteed MyActor) -> () sil [ossa] @anotherAsyncFunction : $@convention(thin) @async () -> () +sil [ossa] @syncFunction : $@convention(thin) () -> () // CHECK-LABEL: sil [ossa] @simpleRedundantActor : $@convention(method) @async (@guaranteed MyActor) -> () { // CHECK: bb0(%0 : @guaranteed $MyActor): @@ -302,3 +304,138 @@ bb0(%0 : @guaranteed $MyActor): %r = tuple () return %r : $() } + +// CHECK-LABEL: sil [ossa] @simpleDCEAsync : $@convention(thin) @async (@guaranteed MyActor) -> () { +// CHECK-NOT: hop_to_executor +// CHECK: } // end sil function 'simpleDCEAsync' +sil [ossa] @simpleDCEAsync : $@convention(thin) @async (@guaranteed MyActor) -> () { +bb0(%0 : @guaranteed $MyActor): + hop_to_executor %0 + %f = function_ref @anotherAsyncFunction : $@convention(thin) @async () -> () + apply %f() : $@convention(thin) @async () -> () + %9999 = tuple () + return %9999 : $() +} + +// CHECK-LABEL: sil [ossa] @simpleCallerIsolationInheritingStopsDCE : $@convention(thin) @async (@guaranteed MyActor) -> () { +// CHECK: hop_to_executor +// CHECK: } // end sil function 'simpleCallerIsolationInheritingStopsDCE' +sil [ossa] @simpleCallerIsolationInheritingStopsDCE : $@convention(thin) @async (@guaranteed MyActor) -> () { +bb0(%0 : @guaranteed $MyActor): + hop_to_executor %0 + %f = function_ref @anotherAsyncFunction : $@convention(thin) @async () -> () + apply [callee_isolation=caller_isolation_inheriting] [caller_isolation=caller_isolation_inheriting] %f() : $@convention(thin) @async () -> () + %9999 = tuple () + return %9999 : $() +} + +// We should eliminate none of the hop_to_executor here since +// caller_isolation_inheriting @async apply sites do not cross isolation +// boundaries. +// +// CHECK-LABEL: sil [ossa] @simpleCallerIsolationInheritingStopsDCE2 : $@convention(thin) @async (@guaranteed MyActor, @guaranteed MyActor, @guaranteed MyActor) -> () { +// CHECK: hop_to_executor +// CHECK: hop_to_executor +// CHECK: hop_to_executor +// CHECK: } // end sil function 'simpleCallerIsolationInheritingStopsDCE2' +sil [ossa] @simpleCallerIsolationInheritingStopsDCE2 : $@convention(thin) @async (@guaranteed MyActor, @guaranteed MyActor, @guaranteed MyActor) -> () { +bb0(%0 : @guaranteed $MyActor, %1 : @guaranteed $MyActor, %2 : @guaranteed $MyActor): + hop_to_executor %0 + %f = function_ref @anotherAsyncFunction : $@convention(thin) @async () -> () + apply [callee_isolation=caller_isolation_inheriting] [caller_isolation=caller_isolation_inheriting] %f() : $@convention(thin) @async () -> () + hop_to_executor %1 + %f2 = function_ref @syncFunction : $@convention(thin) () -> () + apply %f2() : $@convention(thin) () -> () + apply [callee_isolation=caller_isolation_inheriting] [caller_isolation=caller_isolation_inheriting] %f() : $@convention(thin) @async () -> () + hop_to_executor %2 + apply %f2() : $@convention(thin) () -> () + apply [callee_isolation=caller_isolation_inheriting] [caller_isolation=caller_isolation_inheriting] %f() : $@convention(thin) @async () -> () + %9999 = tuple () + return %9999 : $() +} + +// CHECK-LABEL: sil [ossa] @simpleWithoutCallerIsolationInheritingHaveDCE : $@convention(thin) @async (@guaranteed MyActor, @guaranteed MyActor, @guaranteed MyActor) -> () { +// CHECK: bb0([[ARG1:%.*]] : @guaranteed $MyActor, [[ARG2:%.*]] : @guaranteed $MyActor, [[ARG3:%.*]] : @guaranteed $MyActor): +// CHECK-NEXT: // function_ref anotherAsyncFunction +// CHECK-NEXT: [[FUNC:%.*]] = function_ref @anotherAsyncFunction : $@convention(thin) @async () -> () +// CHECK-NEXT: apply [[FUNC]]() : $@convention(thin) @async () -> () +// CHECK-NEXT: hop_to_executor [[ARG2]] +// CHECK-NEXT: // function_ref syncFunction +// CHECK-NEXT: [[FUNC2:%.*]] = function_ref @syncFunction : $@convention(thin) () -> () +// CHECK-NEXT: apply [[FUNC2]]() : $@convention(thin) () -> () +// CHECK-NEXT: apply [[FUNC]]() : $@convention(thin) @async () -> () +// CHECK-NEXT: hop_to_executor [[ARG3]] +// CHECK-NEXT: apply [[FUNC2]]() : $@convention(thin) () -> () +// CHECK-NEXT: apply [[FUNC]]() : $@convention(thin) @async () -> () +// CHECK-NEXT: apply [[FUNC]]() : $@convention(thin) @async () -> () +// CHECK: } // end sil function 'simpleWithoutCallerIsolationInheritingHaveDCE' +sil [ossa] @simpleWithoutCallerIsolationInheritingHaveDCE : $@convention(thin) @async (@guaranteed MyActor, @guaranteed MyActor, @guaranteed MyActor) -> () { +bb0(%0 : @guaranteed $MyActor, %1 : @guaranteed $MyActor, %2 : @guaranteed $MyActor): + hop_to_executor %0 + %f = function_ref @anotherAsyncFunction : $@convention(thin) @async () -> () + apply %f() : $@convention(thin) @async () -> () + hop_to_executor %1 + %f2 = function_ref @syncFunction : $@convention(thin) () -> () + apply %f2() : $@convention(thin) () -> () + apply %f() : $@convention(thin) @async () -> () + hop_to_executor %2 + apply %f2() : $@convention(thin) () -> () + apply %f() : $@convention(thin) @async () -> () + hop_to_executor %2 + apply %f() : $@convention(thin) @async () -> () + %9999 = tuple () + return %9999 : $() +} + +// We do not allow for these to be propagated yet through caller isolation +// inheriting, so we should have all of the hop_to_executor. +// +// CHECK-LABEL: sil [ossa] @callerIsolationInheritingStopsAllowsPropagating : $@convention(thin) @async (@guaranteed MyActor, @guaranteed MyActor, @guaranteed MyActor) -> () { +// CHECK: bb0( +// CHECK: hop_to_executor +// CHECK: hop_to_executor +// CHECK: hop_to_executor +// CHECK: } // end sil function 'callerIsolationInheritingStopsAllowsPropagating' +sil [ossa] @callerIsolationInheritingStopsAllowsPropagating : $@convention(thin) @async (@guaranteed MyActor, @guaranteed MyActor, @guaranteed MyActor) -> () { +bb0(%0 : @guaranteed $MyActor, %1 : @guaranteed $MyActor, %2 : @guaranteed $MyActor): + hop_to_executor %0 + %f = function_ref @anotherAsyncFunction : $@convention(thin) @async () -> () + apply [callee_isolation=caller_isolation_inheriting] [caller_isolation=caller_isolation_inheriting] %f() : $@convention(thin) @async () -> () + hop_to_executor %0 + %f2 = function_ref @syncFunction : $@convention(thin) () -> () + apply %f2() : $@convention(thin) () -> () + apply [callee_isolation=caller_isolation_inheriting] [caller_isolation=caller_isolation_inheriting] %f() : $@convention(thin) @async () -> () + hop_to_executor %0 + apply %f2() : $@convention(thin) () -> () + apply [callee_isolation=caller_isolation_inheriting] [caller_isolation=caller_isolation_inheriting] %f() : $@convention(thin) @async () -> () + %9999 = tuple () + return %9999 : $() +} + +// Since we are caller isolation inheriting, make sure that we leave around the +// hop_to_executor due to ehre elase. We do eliminate one of the hop_to_executor +// though. +// +// CHECK: sil [isolation "caller_isolation_inheriting"] [ossa] @callerIsolationInheritingStopsReturnDeadEnd : $@convention(thin) @async (@guaranteed MyActor, @guaranteed MyActor, @guaranteed MyActor) -> () { +// CHECK: bb0( +// CHECK-NEXT: hop_to_executor +// CHECK-NEXT: tuple +// CHECK-NEXT: return +// CHECK: } // end sil function 'callerIsolationInheritingStopsReturnDeadEnd' +sil [isolation "caller_isolation_inheriting"] [ossa] @callerIsolationInheritingStopsReturnDeadEnd : $@convention(thin) @async (@guaranteed MyActor, @guaranteed MyActor, @guaranteed MyActor) -> () { +bb0(%0 : @guaranteed $MyActor, %1 : @guaranteed $MyActor, %2 : @guaranteed $MyActor): + hop_to_executor %0 + hop_to_executor %0 + %9999 = tuple () + return %9999 : $() +} + +// CHECK-LABEL: sil [ossa] @noIsolationStillRemoves : $@convention(thin) @async (@guaranteed MyActor, @guaranteed MyActor, @guaranteed MyActor) -> () { +// CHECK-NOT: hop_to_executor +// CHECK: } // end sil function 'noIsolationStillRemoves' +sil [ossa] @noIsolationStillRemoves : $@convention(thin) @async (@guaranteed MyActor, @guaranteed MyActor, @guaranteed MyActor) -> () { +bb0(%0 : @guaranteed $MyActor, %1 : @guaranteed $MyActor, %2 : @guaranteed $MyActor): + hop_to_executor %0 + %9999 = tuple () + return %9999 : $() +}