Skip to content

Commit 0e0665b

Browse files
committed
[concurrency] Add support for HopToMainActorIfNeededThunk.
It is behind the experimental flag GenerateForceToMainActorThunks.
1 parent f0f5ad5 commit 0e0665b

25 files changed

+575
-25
lines changed

include/swift/AST/SILThunkKind.h

+8-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,12 @@ struct SILThunkKind {
3434
/// purposes of the underlying thunking machinery.
3535
Identity = 1,
3636

37-
MaxValue = Identity,
37+
/// A thunk that checks if a value is on the main actor and if it isn't
38+
/// jumps to the main actor. It expects that the passed in function does not
39+
/// have any arguments, results, is synchronous, and does not throw.
40+
HopToMainActorIfNeeded = 2,
41+
42+
MaxValue = HopToMainActorIfNeeded,
3843
};
3944

4045
InnerTy innerTy;
@@ -68,6 +73,8 @@ struct SILThunkKind {
6873
return Demangle::MangledSILThunkKind::Invalid;
6974
case Identity:
7075
return Demangle::MangledSILThunkKind::Identity;
76+
case HopToMainActorIfNeeded:
77+
return Demangle::MangledSILThunkKind::HopToMainActorIfNeeded;
7178
}
7279
}
7380
};

include/swift/Basic/Features.def

+3
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,9 @@ EXPERIMENTAL_FEATURE(CoroutineAccessorsUnwindOnCallerError, false)
433433
/// modify/read coroutines use the callee-allocated ABI
434434
EXPERIMENTAL_FEATURE(CoroutineAccessorsAllocateInCallee, false)
435435

436+
// When a parameter has unspecified isolation, infer it as main actor isolated.
437+
EXPERIMENTAL_FEATURE(GenerateForceToMainActorThunks, false)
438+
436439
#undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE
437440
#undef EXPERIMENTAL_FEATURE
438441
#undef UPCOMING_FEATURE

include/swift/Demangling/Demangle.h

+1
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ enum class MangledDifferentiabilityKind : char {
139139
enum class MangledSILThunkKind : char {
140140
Invalid = 0,
141141
Identity = 'I',
142+
HopToMainActorIfNeeded = 'H',
142143
};
143144

144145
/// The pass that caused the specialization to occur. We use this to make sure

include/swift/Demangling/DemangleNodes.def

+1
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@ NODE(ReflectionMetadataSuperclassDescriptor)
292292
NODE(GenericTypeParamDecl)
293293
NODE(CurryThunk)
294294
NODE(SILThunkIdentity)
295+
NODE(SILThunkHopToMainActorIfNeeded)
295296
NODE(DispatchThunk)
296297
NODE(MethodDescriptor)
297298
NODE(ProtocolRequirementsBaseDescriptor)

include/swift/SIL/SILBuilder.h

+7
Original file line numberDiff line numberDiff line change
@@ -1251,6 +1251,13 @@ class SILBuilder {
12511251
return createThunk(Loc, Op, ThunkInst::Kind::Identity, substitutionMap);
12521252
}
12531253

1254+
ThunkInst *
1255+
createHopToMainActorIfNeededThunk(SILLocation Loc, SILValue Op,
1256+
SubstitutionMap substitutionMap = {}) {
1257+
return createThunk(Loc, Op, ThunkInst::Kind::HopToMainActorIfNeeded,
1258+
substitutionMap);
1259+
}
1260+
12541261
ConvertEscapeToNoEscapeInst *
12551262
createConvertEscapeToNoEscape(SILLocation Loc, SILValue Op, SILType Ty,
12561263
bool lifetimeGuaranteed) {

lib/AST/FeatureSet.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ UNINTERESTING_FEATURE(SameElementRequirements)
199199
UNINTERESTING_FEATURE(UnspecifiedMeansMainActorIsolated)
200200
UNINTERESTING_FEATURE(GlobalActorInferenceCutoff)
201201
UNINTERESTING_FEATURE(KeyPathWithStaticMembers)
202+
UNINTERESTING_FEATURE(GenerateForceToMainActorThunks)
202203

203204
static bool usesFeatureSendingArgsAndResults(Decl *decl) {
204205
auto isFunctionTypeWithSending = [](Type type) {

lib/Demangling/Demangler.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -2796,6 +2796,9 @@ NodePointer Demangler::demangleThunkOrSpecialization() {
27962796
switch (char c = nextChar()) {
27972797
case 'I':
27982798
return createWithChild(Node::Kind::SILThunkIdentity, popNode(isEntity));
2799+
case 'H':
2800+
return createWithChild(Node::Kind::SILThunkHopToMainActorIfNeeded,
2801+
popNode(isEntity));
27992802
default:
28002803
return nullptr;
28012804
}

lib/Demangling/NodePrinter.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,7 @@ class NodePrinter {
368368
case Node::Kind::CoroutineContinuationPrototype:
369369
case Node::Kind::CurryThunk:
370370
case Node::Kind::SILThunkIdentity:
371+
case Node::Kind::SILThunkHopToMainActorIfNeeded:
371372
case Node::Kind::DispatchThunk:
372373
case Node::Kind::Deallocator:
373374
case Node::Kind::IsolatedDeallocator:
@@ -1430,6 +1431,10 @@ NodePointer NodePrinter::print(NodePointer Node, unsigned depth,
14301431
Printer << "identity thunk of ";
14311432
print(Node->getChild(0), depth + 1);
14321433
return nullptr;
1434+
case Node::Kind::SILThunkHopToMainActorIfNeeded:
1435+
Printer << "hop to main actor thunk of ";
1436+
print(Node->getChild(0), depth + 1);
1437+
return nullptr;
14331438
case Node::Kind::DispatchThunk:
14341439
Printer << "dispatch thunk of ";
14351440
print(Node->getChild(0), depth + 1);

lib/Demangling/OldRemangler.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -2530,6 +2530,12 @@ ManglingError Remangler::mangleSILThunkIdentity(Node *node, unsigned depth) {
25302530
return ManglingError::Success;
25312531
}
25322532

2533+
ManglingError Remangler::mangleSILThunkHopToMainActorIfNeeded(Node *node,
2534+
unsigned depth) {
2535+
Buffer << "<sil-hop-to-main-actor-if-needed-thunk>";
2536+
return ManglingError::Success;
2537+
}
2538+
25332539
ManglingError Remangler::mangleDispatchThunk(Node *node, unsigned depth) {
25342540
Buffer << "<dispatch-thunk>";
25352541
return ManglingError::Success;

lib/Demangling/Remangler.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -3382,6 +3382,14 @@ ManglingError Remangler::mangleSILThunkIdentity(Node *node, unsigned depth) {
33823382
return ManglingError::Success;
33833383
}
33843384

3385+
ManglingError Remangler::mangleSILThunkHopToMainActorIfNeeded(Node *node,
3386+
unsigned depth) {
3387+
RETURN_IF_ERROR(mangleSingleChildNode(node, depth + 1)); // type
3388+
Buffer << "TT"
3389+
<< "H";
3390+
return ManglingError::Success;
3391+
}
3392+
33853393
ManglingError Remangler::mangleDispatchThunk(Node *node, unsigned depth) {
33863394
RETURN_IF_ERROR(mangleSingleChildNode(node, depth + 1));
33873395
Buffer << "Tj";

lib/SIL/IR/SILInstructions.cpp

+39
Original file line numberDiff line numberDiff line change
@@ -2821,6 +2821,42 @@ bool ConvertFunctionInst::onlyConvertsSendable() const {
28212821
getNonSendableFuncType(getType());
28222822
}
28232823

2824+
static CanSILFunctionType getDerivedFunctionTypeForHopToMainActorIfNeeded(
2825+
SILFunction *fn, CanSILFunctionType inputFunctionType,
2826+
SubstitutionMap subMap) {
2827+
inputFunctionType = inputFunctionType->substGenericArgs(
2828+
fn->getModule(), subMap, fn->getTypeExpansionContext());
2829+
bool needsSubstFunctionType = false;
2830+
for (auto param : inputFunctionType->getParameters()) {
2831+
needsSubstFunctionType |= param.getInterfaceType()->hasTypeParameter();
2832+
}
2833+
for (auto result : inputFunctionType->getResults()) {
2834+
needsSubstFunctionType |= result.getInterfaceType()->hasTypeParameter();
2835+
}
2836+
for (auto yield : inputFunctionType->getYields()) {
2837+
needsSubstFunctionType |= yield.getInterfaceType()->hasTypeParameter();
2838+
}
2839+
2840+
SubstitutionMap appliedSubs;
2841+
if (needsSubstFunctionType) {
2842+
appliedSubs = inputFunctionType->getCombinedSubstitutions();
2843+
}
2844+
2845+
auto extInfoBuilder =
2846+
inputFunctionType->getExtInfo()
2847+
.intoBuilder()
2848+
.withRepresentation(SILFunctionType::Representation::Thick)
2849+
.withIsPseudogeneric(false);
2850+
2851+
return SILFunctionType::get(
2852+
nullptr, extInfoBuilder.build(), inputFunctionType->getCoroutineKind(),
2853+
ParameterConvention::Direct_Guaranteed,
2854+
inputFunctionType->getParameters(), inputFunctionType->getYields(),
2855+
inputFunctionType->getResults(),
2856+
inputFunctionType->getOptionalErrorResult(), appliedSubs,
2857+
SubstitutionMap(), inputFunctionType->getASTContext());
2858+
}
2859+
28242860
static CanSILFunctionType
28252861
getDerivedFunctionTypeForIdentityThunk(SILFunction *fn,
28262862
CanSILFunctionType inputFunctionType,
@@ -2873,6 +2909,9 @@ ThunkInst::Kind::getDerivedFunctionType(SILFunction *fn,
28732909
case Identity:
28742910
return getDerivedFunctionTypeForIdentityThunk(fn, inputFunctionType,
28752911
subMap);
2912+
case HopToMainActorIfNeeded:
2913+
return getDerivedFunctionTypeForHopToMainActorIfNeeded(
2914+
fn, inputFunctionType, subMap);
28762915
}
28772916

28782917
llvm_unreachable("Covered switch isn't covered?!");

lib/SIL/IR/SILPrinter.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -2058,6 +2058,9 @@ class SILPrinter : public SILInstructionVisitor<SILPrinter> {
20582058
case ThunkInst::Kind::Identity:
20592059
*this << "[identity] ";
20602060
break;
2061+
case ThunkInst::Kind::HopToMainActorIfNeeded:
2062+
*this << "[hop_to_mainactor_if_needed] ";
2063+
break;
20612064
}
20622065
*this << Ctx.getID(ti->getOperand());
20632066
printSubstitutions(

lib/SIL/Parser/ParseSIL.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -4046,6 +4046,8 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B,
40464046

40474047
auto kind = llvm::StringSwitch<ThunkInst::Kind>(attrName)
40484048
.Case("identity", ThunkInst::Kind::Identity)
4049+
.Case("hop_to_mainactor_if_needed",
4050+
ThunkInst::Kind::HopToMainActorIfNeeded)
40494051
.Default(ThunkInst::Kind::Invalid);
40504052
if (!kind) {
40514053
P.diagnose(OpcodeLoc, diag::sil_thunkinst_failed_to_parse_kind, attrName);

lib/SIL/Verifier/SILVerifier.cpp

+21
Original file line numberDiff line numberDiff line change
@@ -5225,6 +5225,27 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
52255225
require(resTI == ti->getThunkKind().getDerivedFunctionType(
52265226
ti->getFunction(), objTI, ti->getSubstitutionMap()),
52275227
"resTI is not the thunk kind assigned derived function type");
5228+
5229+
auto originalCalleeFuncType =
5230+
ti->getOperand()->getType().castTo<SILFunctionType>();
5231+
5232+
switch (ti->getThunkKind()) {
5233+
case ThunkInst::Kind::Invalid:
5234+
break;
5235+
case ThunkInst::Kind::Identity:
5236+
break;
5237+
case ThunkInst::Kind::HopToMainActorIfNeeded:
5238+
require(originalCalleeFuncType->getParameters().empty(),
5239+
"Hop To Main Actor If Needed cannot have any parameters");
5240+
require(originalCalleeFuncType->getResults().empty(),
5241+
"Hop To Main Actor If Needed cannot have any results");
5242+
// We require that hop_to_main_actor inputs do not an error since we
5243+
// have to have no results.
5244+
require(!originalCalleeFuncType->hasErrorResult(),
5245+
"HopToMainActorIfNeeded thunks cannot have input without an "
5246+
"error result");
5247+
break;
5248+
}
52285249
}
52295250

52305251
void checkConvertEscapeToNoEscapeInst(ConvertEscapeToNoEscapeInst *ICI) {

lib/SILGen/ManagedValue.h

+4
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,10 @@ class ManagedValue {
324324
assert(!hasCleanup());
325325
return getValue();
326326
}
327+
328+
/// Treat getValue() as used so that we can access the value directly when
329+
/// debugging without needing to interpret the flag field.
330+
LLVM_ATTRIBUTE_USED
327331
SILValue getValue() const { return valueAndFlag.getPointer(); }
328332

329333
SILType getType() const { return getValue()->getType(); }

lib/SILGen/SILGenBuilder.cpp

+14
Original file line numberDiff line numberDiff line change
@@ -1204,3 +1204,17 @@ ManagedValue SILGenBuilder::borrowObjectRValue(SILGenFunction &SGF,
12041204
}
12051205
return SGF.emitFormalEvaluationManagedBeginBorrow(loc, value);
12061206
}
1207+
1208+
ManagedValue SILGenBuilder::createHopToMainActorIfNeededThunk(
1209+
SILLocation loc, ManagedValue op, SubstitutionMap substitutionMap) {
1210+
if (op.getOwnershipKind() == OwnershipKind::None) {
1211+
auto *thunkedFunc = createHopToMainActorIfNeededThunk(
1212+
loc, op.forward(getSILGenFunction()), substitutionMap);
1213+
return SGF.emitManagedRValueWithCleanup(thunkedFunc);
1214+
}
1215+
1216+
CleanupCloner cloner(*this, op);
1217+
auto *thunkedFunc = createHopToMainActorIfNeededThunk(
1218+
loc, op.forward(getSILGenFunction()), substitutionMap);
1219+
return cloner.clone(thunkedFunc);
1220+
}

lib/SILGen/SILGenBuilder.h

+5
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,11 @@ class SILGenBuilder : public SILBuilder {
535535

536536
createTupleAddrConstructor(loc, destAddr, values, isInitOfDest);
537537
}
538+
539+
using SILBuilder::createHopToMainActorIfNeededThunk;
540+
ManagedValue
541+
createHopToMainActorIfNeededThunk(SILLocation loc, ManagedValue op,
542+
SubstitutionMap substitutionMap = {});
538543
};
539544

540545
} // namespace Lowering

lib/SILGen/SILGenFunction.cpp

+43
Original file line numberDiff line numberDiff line change
@@ -1079,6 +1079,49 @@ SILGenFunction::emitClosureValue(SILLocation loc, SILDeclRef constant,
10791079
}
10801080
}
10811081

1082+
// If GenerateForceToMainActorThunks and Dynamic Actor Isolation Checking is
1083+
// enabled and we have a synchronous function...
1084+
if (F.getASTContext().LangOpts.hasFeature(
1085+
Feature::GenerateForceToMainActorThunks) &&
1086+
F.getASTContext().LangOpts.isDynamicActorIsolationCheckingEnabled() &&
1087+
!functionTy.castTo<SILFunctionType>()->isAsync()) {
1088+
1089+
// See if that function is a closure which requires dynamic isolation
1090+
// checking that doesn't take any parameters or results. In such a case, we
1091+
// create a hop to main actor if needed thunk.
1092+
if (auto *closureExpr = constant.getClosureExpr()) {
1093+
if (closureExpr->requiresDynamicIsolationChecking()) {
1094+
auto actorIsolation = closureExpr->getActorIsolation();
1095+
switch (actorIsolation) {
1096+
case ActorIsolation::Unspecified:
1097+
case ActorIsolation::Nonisolated:
1098+
case ActorIsolation::NonisolatedUnsafe:
1099+
case ActorIsolation::ActorInstance:
1100+
break;
1101+
1102+
case ActorIsolation::Erased:
1103+
llvm_unreachable("closure cannot have erased isolation");
1104+
1105+
case ActorIsolation::GlobalActor:
1106+
// For now only do this if we are using the main actor and are calling
1107+
// a function that doesn't depend on its result and doesn't have any
1108+
// parameters.
1109+
//
1110+
// NOTE: Since errors are results, this means that we cannot do this
1111+
// for throwing functions.
1112+
if (auto *args = closureExpr->getArgs();
1113+
(!args || args->empty()) && actorIsolation.isMainActor() &&
1114+
closureExpr->getResultType()->getCanonicalType() ==
1115+
B.getASTContext().TheEmptyTupleType &&
1116+
!result.getType().castTo<SILFunctionType>()->hasErrorResult()) {
1117+
result = B.createHopToMainActorIfNeededThunk(loc, result, subs);
1118+
}
1119+
break;
1120+
}
1121+
}
1122+
}
1123+
}
1124+
10821125
return result;
10831126
}
10841127

0 commit comments

Comments
 (0)