Skip to content

Commit 8975d1e

Browse files
authored
Merge pull request #80578 from eeckstein/isolated-conformance-opt-6.2
[6.2] CastOptimizer: handle isolated conformances
2 parents 964a2e3 + 75dc04a commit 8975d1e

File tree

12 files changed

+188
-29
lines changed

12 files changed

+188
-29
lines changed

SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyCheckedCast.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,9 @@ private extension UnconditionalCheckedCastInst {
8181
return
8282
}
8383
let conformance = sourceFormalType.instanceTypeOfMetatype.checkConformance(to: proto)
84-
guard conformance.isValid else {
84+
guard conformance.isValid,
85+
conformance.matchesActorIsolation(in: parentFunction)
86+
else {
8587
return
8688
}
8789

SwiftCompilerSources/Sources/SIL/ASTExtensions.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,11 @@ extension SubstitutionMap {
5454
return SubstitutionMap(bridged: method.bridged.getMethodSubstitutions(bridged))
5555
}
5656
}
57+
58+
extension Conformance {
59+
/// Returns true if the conformance is not isolated or if its isolation matches
60+
/// the isolation in `function`.
61+
public func matchesActorIsolation(in function: Function) -> Bool {
62+
return function.bridged.conformanceMatchesActorIsolation(bridged)
63+
}
64+
}

include/swift/SIL/DynamicCasts.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,15 @@ atBest(DynamicCastFeasibility feasibility, DynamicCastFeasibility bestCase) {
6464
/// Classify the feasibility of a dynamic cast. The source and target
6565
/// types should be unlowered formal types.
6666
DynamicCastFeasibility classifyDynamicCast(
67-
ModuleDecl *context,
67+
SILFunction *function,
6868
CanType sourceType, CanType targetType,
6969
bool isSourceTypeExact = false,
7070
bool isWholeModuleOpts = false);
7171

72+
/// Returns true if the conformance is not isolated or if its isolation matches
73+
/// the isolation `inFunction`.
74+
bool matchesActorIsolation(ProtocolConformanceRef conformance, SILFunction *inFunction);
75+
7276
SILValue emitSuccessfulScalarUnconditionalCast(SILBuilder &B, SILLocation loc,
7377
SILDynamicCastInst inst);
7478

@@ -386,7 +390,7 @@ struct SILDynamicCastInst {
386390

387391
DynamicCastFeasibility classifyFeasibility(bool allowWholeModule) const {
388392
return swift::classifyDynamicCast(
389-
getModule().getSwiftModule(),
393+
getFunction(),
390394
getSourceFormalType(), getTargetFormalType(),
391395
isSourceTypeExact(), allowWholeModule && getModule().isWholeModule());
392396
}

include/swift/SIL/SILBridging.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,7 @@ struct BridgedFunction {
507507
BRIDGED_INLINE BridgedLinkage getLinkage() const;
508508
BRIDGED_INLINE void setLinkage(BridgedLinkage linkage) const;
509509
BRIDGED_INLINE void setIsSerialized(bool isSerialized) const;
510+
BRIDGED_INLINE bool conformanceMatchesActorIsolation(BridgedConformance conformance) const;
510511
bool isTrapNoReturn() const;
511512
bool isConvertPointerToPointerArgument() const;
512513
bool isAutodiffVJP() const;

include/swift/SIL/SILBridgingImpl.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "swift/Basic/BasicBridging.h"
2929
#include "swift/Basic/Nullability.h"
3030
#include "swift/SIL/ApplySite.h"
31+
#include "swift/SIL/DynamicCasts.h"
3132
#include "swift/SIL/InstWrappers.h"
3233
#include "swift/SIL/SILBuilder.h"
3334
#include "swift/SIL/SILDefaultWitnessTable.h"
@@ -823,6 +824,10 @@ void BridgedFunction::setIsSerialized(bool isSerialized) const {
823824
getFunction()->setSerializedKind(isSerialized ? swift::IsSerialized : swift::IsNotSerialized);
824825
}
825826

827+
bool BridgedFunction::conformanceMatchesActorIsolation(BridgedConformance conformance) const {
828+
return swift::matchesActorIsolation(conformance.unbridged(), getFunction());
829+
}
830+
826831
bool BridgedFunction::isResilientNominalDecl(BridgedDeclObj decl) const {
827832
return decl.getAs<swift::NominalTypeDecl>()->isResilient(getFunction()->getModule().getSwiftModule(),
828833
getFunction()->getResilienceExpansion());

lib/SIL/Utils/DynamicCasts.cpp

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ static CanType unwrapExistential(CanType e) {
9393
/// into an existential type by performing a static check
9494
/// of protocol conformances if it is possible.
9595
static DynamicCastFeasibility
96-
classifyDynamicCastToProtocol(CanType source, CanType target,
96+
classifyDynamicCastToProtocol(SILFunction *function, CanType source, CanType target,
9797
bool isWholeModuleOpts) {
9898
assert(target.isExistentialType() &&
9999
"target should be an existential type");
@@ -115,8 +115,12 @@ classifyDynamicCastToProtocol(CanType source, CanType target,
115115

116116
// If checkConformance() returns a valid conformance, then all conditional
117117
// requirements were satisfied.
118-
if (checkConformance(source, TargetProtocol))
118+
if (auto conformance = checkConformance(source, TargetProtocol)) {
119+
if (!matchesActorIsolation(conformance, function))
120+
return DynamicCastFeasibility::MaySucceed;
121+
119122
return DynamicCastFeasibility::WillSucceed;
123+
}
120124

121125
auto *SourceNominalTy = source.getAnyNominal();
122126
if (!SourceNominalTy)
@@ -468,7 +472,7 @@ static bool isCFBridgingConversion(CanType sourceFormalType,
468472

469473
/// Try to classify the dynamic-cast relationship between two types.
470474
DynamicCastFeasibility
471-
swift::classifyDynamicCast(ModuleDecl *M,
475+
swift::classifyDynamicCast(SILFunction *function,
472476
CanType source,
473477
CanType target,
474478
bool isSourceTypeExact,
@@ -481,19 +485,20 @@ swift::classifyDynamicCast(ModuleDecl *M,
481485

482486
auto sourceObject = source.getOptionalObjectType();
483487
auto targetObject = target.getOptionalObjectType();
488+
ModuleDecl *M = function->getModule().getSwiftModule();
484489

485490
// A common level of optionality doesn't affect the feasibility,
486491
// except that we can't fold things to failure because nil inhabits
487492
// both types.
488493
if (sourceObject && targetObject) {
489-
return atWorst(classifyDynamicCast(M, sourceObject, targetObject),
494+
return atWorst(classifyDynamicCast(function, sourceObject, targetObject),
490495
DynamicCastFeasibility::MaySucceed);
491496

492497
// Casting to a more optional type follows the same rule unless we
493498
// know that the source cannot dynamically be an optional value,
494499
// in which case we'll always just cast and inject into an optional.
495500
} else if (targetObject) {
496-
auto result = classifyDynamicCast(M, source, targetObject,
501+
auto result = classifyDynamicCast(function, source, targetObject,
497502
/* isSourceTypeExact */ false,
498503
isWholeModuleOpts);
499504
if (canDynamicallyStoreOptional(source))
@@ -502,12 +507,12 @@ swift::classifyDynamicCast(ModuleDecl *M,
502507

503508
// Casting to a less-optional type can always fail.
504509
} else if (sourceObject) {
505-
auto result = atBest(classifyDynamicCast(M, sourceObject, target,
510+
auto result = atBest(classifyDynamicCast(function, sourceObject, target,
506511
/* isSourceTypeExact */ false,
507512
isWholeModuleOpts),
508513
DynamicCastFeasibility::MaySucceed);
509514
if (target.isExistentialType()) {
510-
result = atWorst(result, classifyDynamicCastToProtocol(
515+
result = atWorst(result, classifyDynamicCastToProtocol(function,
511516
source, target, isWholeModuleOpts));
512517
}
513518
return result;
@@ -522,7 +527,7 @@ swift::classifyDynamicCast(ModuleDecl *M,
522527
// Check conversions from non-protocol types into protocol types.
523528
if (!source.isExistentialType() &&
524529
target.isExistentialType())
525-
return classifyDynamicCastToProtocol(source, target,
530+
return classifyDynamicCastToProtocol(function, source, target,
526531
isWholeModuleOpts);
527532

528533
// Check conversions from protocol types to non-protocol types.
@@ -552,7 +557,7 @@ swift::classifyDynamicCast(ModuleDecl *M,
552557
// Hashable is not actually a legal existential type right now, but
553558
// the check doesn't care about that.
554559
if (auto hashable = getHashableExistentialType(M)) {
555-
return classifyDynamicCastToProtocol(source, hashable,
560+
return classifyDynamicCastToProtocol(function, source, hashable,
556561
isWholeModuleOpts);
557562
}
558563
}
@@ -589,7 +594,7 @@ swift::classifyDynamicCast(ModuleDecl *M,
589594

590595
if (targetMetatype.isAnyExistentialType() && target->isExistentialType()) {
591596
auto Feasibility =
592-
classifyDynamicCastToProtocol(source, target, isWholeModuleOpts);
597+
classifyDynamicCastToProtocol(function, source, target, isWholeModuleOpts);
593598
// Cast from existential metatype to existential metatype may still
594599
// succeed, even if we cannot prove anything statically.
595600
if (Feasibility != DynamicCastFeasibility::WillFail ||
@@ -699,7 +704,7 @@ swift::classifyDynamicCast(ModuleDecl *M,
699704

700705
// Combine the result of prior elements with this element type.
701706
result = std::max(result,
702-
classifyDynamicCast(M,
707+
classifyDynamicCast(function,
703708
sourceElt.getType()->getCanonicalType(),
704709
targetElt.getType()->getCanonicalType(),
705710
isSourceTypeExact,
@@ -758,7 +763,7 @@ swift::classifyDynamicCast(ModuleDecl *M,
758763
// question there.
759764
if (bridgedSource) source = bridgedSource;
760765
if (bridgedTarget) target = bridgedTarget;
761-
return classifyDynamicCast(M, source, target, false, isWholeModuleOpts);
766+
return classifyDynamicCast(function, source, target, false, isWholeModuleOpts);
762767
}
763768

764769
// Casts from a class into a non-class can never succeed if the target must
@@ -833,7 +838,7 @@ swift::classifyDynamicCast(ModuleDecl *M,
833838
if (Type ObjCTy = M->getASTContext().getBridgedToObjC(M, source)) {
834839
// If the bridged ObjC type is known, check if
835840
// this type can be cast into target type.
836-
return classifyDynamicCast(M,
841+
return classifyDynamicCast(function,
837842
ObjCTy->getCanonicalType(),
838843
target,
839844
/* isSourceTypeExact */ false, isWholeModuleOpts);
@@ -861,16 +866,16 @@ swift::classifyDynamicCast(ModuleDecl *M,
861866
// Arrays and sets.
862867
if (sourceStruct->isArray() || sourceStruct->isSet()) {
863868
auto valueFeasibility =
864-
classifyDynamicCast(M, sourceArgs[0], targetArgs[0]);
869+
classifyDynamicCast(function, sourceArgs[0], targetArgs[0]);
865870
return atWorst(valueFeasibility,
866871
DynamicCastFeasibility::MaySucceed);
867872

868873
// Dictionaries.
869874
} else if (sourceStruct->isDictionary()) {
870875
auto keyFeasibility =
871-
classifyDynamicCast(M, sourceArgs[0], targetArgs[0]);
876+
classifyDynamicCast(function, sourceArgs[0], targetArgs[0]);
872877
auto valueFeasibility =
873-
classifyDynamicCast(M, sourceArgs[1], targetArgs[1]);
878+
classifyDynamicCast(function, sourceArgs[1], targetArgs[1]);
874879
return atWorst(atBest(keyFeasibility, valueFeasibility),
875880
DynamicCastFeasibility::MaySucceed);
876881
}
@@ -881,6 +886,25 @@ swift::classifyDynamicCast(ModuleDecl *M,
881886
return DynamicCastFeasibility::WillFail;
882887
}
883888

889+
bool swift::matchesActorIsolation(ProtocolConformanceRef conformance, SILFunction *inFunction) {
890+
return !conformance.forEachIsolatedConformance([&](ProtocolConformanceRef isolatedConf) -> bool {
891+
if (!isolatedConf.isConcrete())
892+
return false;
893+
894+
ActorIsolation isolation = isolatedConf.getConcrete()->getIsolation();
895+
if (isolation.isNonisolated())
896+
return false;
897+
898+
if (isolation.isGlobalActor()) {
899+
if (auto functionIsolation = inFunction->getActorIsolation()) {
900+
if (isolation == functionIsolation.value())
901+
return false;
902+
}
903+
}
904+
return true;
905+
});
906+
}
907+
884908
static unsigned getOptionalDepth(CanType type) {
885909
unsigned depth = 0;
886910
while (CanType objectType = type.getOptionalObjectType()) {
@@ -1221,7 +1245,7 @@ swift::emitSuccessfulScalarUnconditionalCast(SILBuilder &B, ModuleDecl *M,
12211245
CanType sourceFormalType,
12221246
CanType targetFormalType,
12231247
SILInstruction *existingCast) {
1224-
assert(classifyDynamicCast(M, sourceFormalType, targetFormalType)
1248+
assert(classifyDynamicCast(&B.getFunction(), sourceFormalType, targetFormalType)
12251249
== DynamicCastFeasibility::WillSucceed);
12261250

12271251
// Casts to/from existential types cannot be further improved.
@@ -1258,7 +1282,7 @@ bool swift::emitSuccessfulIndirectUnconditionalCast(
12581282
SILBuilder &B, ModuleDecl *M, SILLocation loc, SILValue src,
12591283
CanType sourceFormalType, SILValue dest, CanType targetFormalType,
12601284
SILInstruction *existingCast) {
1261-
assert(classifyDynamicCast(M, sourceFormalType, targetFormalType)
1285+
assert(classifyDynamicCast(&B.getFunction(), sourceFormalType, targetFormalType)
12621286
== DynamicCastFeasibility::WillSucceed);
12631287

12641288
assert(src->getType().isAddress());

lib/SILOptimizer/Utils/CastOptimizer.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1482,6 +1482,9 @@ static bool optimizeStaticallyKnownProtocolConformance(
14821482
if (Conformance.isInvalid())
14831483
return false;
14841484

1485+
if (!matchesActorIsolation(Conformance, Inst->getFunction()))
1486+
return false;
1487+
14851488
auto layout = TargetType->getExistentialLayout();
14861489
if (layout.getProtocols().size() != 1)
14871490
return false;

lib/SILOptimizer/Utils/ConstExpr.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1874,7 +1874,7 @@ ConstExprFunctionState::evaluateInstructionAndGetNext(
18741874
CanType targetType = substituteGenericParamsAndSimplify(
18751875
checkedCastInst->getTargetFormalType());
18761876
DynamicCastFeasibility castResult = classifyDynamicCast(
1877-
inst->getModule().getSwiftModule(), sourceType, targetType);
1877+
inst->getFunction(), sourceType, targetType);
18781878
if (castResult == DynamicCastFeasibility::MaySucceed) {
18791879
return {std::nullopt, getUnknown(evaluator, inst->asSILNode(),
18801880
UnknownReason::UnknownCastResult)};

lib/SILOptimizer/Utils/OptimizerBridging.cpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -530,10 +530,7 @@ BridgedDynamicCastResult classifyDynamicCastBridged(BridgedCanType sourceTy, Bri
530530
static_assert((int)DynamicCastFeasibility::WillFail == (int)BridgedDynamicCastResult::willFail);
531531

532532
return static_cast<BridgedDynamicCastResult>(
533-
classifyDynamicCast(function.getFunction()->getModule().getSwiftModule(),
534-
sourceTy.unbridged(),
535-
destTy.unbridged(),
536-
sourceTypeIsExact));
533+
classifyDynamicCast(function.getFunction(), sourceTy.unbridged(), destTy.unbridged(), sourceTypeIsExact));
537534
}
538535

539536
BridgedDynamicCastResult classifyDynamicCastBridged(BridgedInstruction inst) {

test/SILOptimizer/constant_propagation_casts_ossa.sil

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
1-
// RUN: %target-sil-opt -enable-sil-verify-all %s -diagnostic-constant-propagation | %FileCheck %s
1+
// RUN: %target-sil-opt -enable-sil-verify-all %s -diagnostic-constant-propagation -enable-experimental-feature IsolatedConformances | %FileCheck %s
2+
3+
// REQUIRES: swift_feature_IsolatedConformances
4+
// REQUIRES: concurrency
5+
26
sil_stage canonical
37

4-
//import Swift
8+
import Builtin
9+
import Swift
10+
import _Concurrency
11+
import SwiftShims
512

613
class Klass {
714
}
@@ -12,6 +19,15 @@ class SubKlass : Klass {
1219
class NoSubKlass {
1320
}
1421

22+
protocol P {
23+
func f() -> Int
24+
}
25+
26+
struct X : @MainActor P {
27+
func f() -> Int
28+
init()
29+
}
30+
1531
sil [noinline] @blackhole1 : $@convention(thin) (@guaranteed SubKlass) -> ()
1632
sil [noinline] @blackhole2 : $@convention(thin) (@guaranteed NoSubKlass) -> ()
1733

@@ -94,3 +110,26 @@ bb3:
94110
return %r : $()
95111
}
96112

113+
// CHECK-LABEL: sil [ossa] @isolated_conf_ucca :
114+
// CHECK: unconditional_checked_cast_addr
115+
// CHECK-LABEL: } // end sil function 'isolated_conf_ucca'
116+
sil [ossa] @isolated_conf_ucca : $@convention(thin) (@in X) -> @out P {
117+
bb0(%0 : $*P, %1 : $*X):
118+
unconditional_checked_cast_addr X in %1 to any P in %0
119+
%2 = tuple ()
120+
return %2
121+
}
122+
123+
// CHECK-LABEL: sil [ossa] @isolated_conf_ccab :
124+
// CHECK: checked_cast_addr_br
125+
// CHECK-LABEL: } // end sil function 'isolated_conf_ccab'
126+
sil [ossa] @isolated_conf_ccab : $@convention(thin) (@in X) -> @out P {
127+
bb0(%0 : $*P, %1 : $*X):
128+
checked_cast_addr_br take_always X in %1 to any P in %0, bb1, bb2
129+
bb1:
130+
%2 = tuple ()
131+
return %2
132+
bb2:
133+
unreachable
134+
}
135+

0 commit comments

Comments
 (0)