Skip to content

[6.2][concurrency] Make optimize hop to executor more conservative for 6.2 around caller isolation inheriting functions. #83084

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jul 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions include/swift/SIL/ApplySite.h
Original file line number Diff line number Diff line change
Expand Up @@ -894,6 +894,21 @@ class FullApplySite : public ApplySite {
getNumIndirectSILErrorResults();
}

std::optional<ActorIsolation> 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) {
Expand Down
14 changes: 14 additions & 0 deletions lib/SIL/IR/SILPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@ llvm::cl::opt<bool> SILPrintGenericSpecializationInfo(
llvm::cl::desc("Include generic specialization"
"information info in SIL output"));

llvm::cl::opt<bool> 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);
Expand Down Expand Up @@ -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;
Expand Down
56 changes: 40 additions & 16 deletions lib/SIL/Parser/ParseSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -685,7 +685,8 @@ static bool parseDeclSILOptional(
AvailabilityRange *availability, bool *isWithoutActuallyEscapingThunk,
SmallVectorImpl<std::string> *Semantics,
SmallVectorImpl<ParsedSpecAttr> *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;
Expand Down Expand Up @@ -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);
Expand All @@ -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;
Expand Down Expand Up @@ -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,
Expand All @@ -7344,7 +7365,7 @@ bool SILParserState::parseDeclSIL(Parser &P) {
&optimizationMode, &perfConstr, &isPerformanceConstraint,
&markedAsUsed, &section, 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) ||
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;


Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down
29 changes: 29 additions & 0 deletions lib/SILGen/SILGenConcurrency.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<HopToActorCleanup>(actor);
return Cleanups.getTopCleanup();
}

ExecutorBreadcrumb SILGenFunction::emitHopToTargetExecutor(
SILLocation loc, SILValue executor) {
// Record that we need to hop back to the current executor.
Expand Down
6 changes: 6 additions & 0 deletions lib/SILGen/SILGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -1333,6 +1333,12 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
emitHopToTargetActor(SILLocation loc, std::optional<ActorIsolation> actorIso,
std::optional<ManagedValue> 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.
///
Expand Down
Loading