Skip to content

Commit ec7acc6

Browse files
committed
[sil-cse] Fix a bug in the CSE of open_existential_ref instructions
When performing a CSE of open_existential_ref instructions, we replace the new archetype by the old archetype by cloning the uses and re-mapping the archetypes. But we also need to consider that some of the uses of a open_existential_ref instruction (e.g. loads) may produce results depending on the opened archetype being replaced. Therefore, for every such use its own uses (and their uses) should be eventually recursively cloned and type-remapped as well if they depend on the opened archetype being replaced. Fixes rdar://28136015 and https://bugs.swift.org/browse/SR-2545
1 parent 893ee41 commit ec7acc6

File tree

7 files changed

+193
-145
lines changed

7 files changed

+193
-145
lines changed

include/swift/SIL/SILBuilder.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -954,8 +954,9 @@ class SILBuilder {
954954
DynamicMethodInst *createDynamicMethod(SILLocation Loc, SILValue Operand,
955955
SILDeclRef Member, SILType MethodTy,
956956
bool Volatile = false) {
957-
return insert(new (F.getModule()) DynamicMethodInst(
958-
getSILDebugLocation(Loc), Operand, Member, MethodTy, Volatile));
957+
return insert(DynamicMethodInst::create(
958+
getSILDebugLocation(Loc), Operand, Member, MethodTy, Volatile,
959+
&F, OpenedArchetypes));
959960
}
960961

961962
OpenExistentialAddrInst *

include/swift/SIL/SILCloner.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@ class SILBuilderWithPostProcess : public SILBuilder {
270270
{
271271
setInsertionPoint(SC.getBuilder().getInsertionBB(),
272272
SC.getBuilder().getInsertionPoint());
273+
setOpenedArchetypesTracker(SC.getBuilder().getOpenedArchetypesTracker());
273274
}
274275

275276
~SILBuilderWithPostProcess() {

include/swift/SIL/SILInstruction.h

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3613,14 +3613,25 @@ class WitnessMethodInst final
36133613
/// constant referring to some Objective-C method, performs dynamic method
36143614
/// lookup to extract the implementation of that method. This method lookup
36153615
/// can fail at run-time
3616-
class DynamicMethodInst
3617-
: public UnaryInstructionBase<ValueKind::DynamicMethodInst, MethodInst>
3616+
class DynamicMethodInst final
3617+
: public UnaryInstructionWithOpenArchetypesBase<
3618+
ValueKind::DynamicMethodInst,
3619+
DynamicMethodInst,
3620+
MethodInst,
3621+
true>
36183622
{
36193623
friend class SILBuilder;
36203624

36213625
DynamicMethodInst(SILDebugLocation DebugLoc, SILValue Operand,
3622-
SILDeclRef Member, SILType Ty, bool Volatile = false)
3623-
: UnaryInstructionBase(DebugLoc, Operand, Ty, Member, Volatile) {}
3626+
ArrayRef<SILValue> TypeDependentOperands,
3627+
SILDeclRef Member, SILType Ty, bool Volatile)
3628+
: UnaryInstructionWithOpenArchetypesBase(DebugLoc, Operand,
3629+
TypeDependentOperands, Ty, Member, Volatile) {}
3630+
3631+
static DynamicMethodInst *
3632+
create(SILDebugLocation DebugLoc, SILValue Operand,
3633+
SILDeclRef Member, SILType Ty, bool Volatile, SILFunction *F,
3634+
SILOpenedArchetypesState &OpenedArchetypes);
36243635
};
36253636

36263637
/// Given the address of an existential, "opens" the

lib/SIL/SILInstructions.cpp

Lines changed: 82 additions & 101 deletions
Large diffs are not rendered by default.

lib/SIL/SILVerifier.cpp

Lines changed: 38 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -698,18 +698,16 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
698698
return fnTy->substGenericArgs(F.getModule(), M, subs);
699699
}
700700

701-
/// Check that for each opened archetype in substitutions, there is an
702-
/// opened archetype operand.
703-
void checkApplySubstitutionsOpenedArchetypes(SILInstruction *AI,
704-
ArrayRef<Substitution> Subs) {
705-
// If we have a substitution whose replacement type is an archetype, make
706-
// sure that the replacement archetype is in the context generic params of
707-
// the caller function.
701+
/// Check that for each opened archetype in substitutions
702+
/// or the calle type, there is a type dependent operand.
703+
void checkApplyTypeDependentArguments(ApplySite AS) {
704+
SILInstruction *AI = AS.getInstruction();
705+
708706
llvm::DenseSet<CanType> FoundOpenedArchetypes;
709-
for (auto &Sub : Subs) {
710-
Sub.getReplacement().visit([&](Type Ty) {
711-
if (!Ty->isOpenedExistential())
712-
return;
707+
708+
// Function to collect opened archetypes in FoundOpenedArchetypes.
709+
auto HandleType = [&](Type Ty) {
710+
if (Ty->isOpenedExistential()) {
713711
auto *A = Ty->getAs<ArchetypeType>();
714712
require(isArchetypeValidInFunction(A, AI->getFunction()),
715713
"Archetype to be substituted must be valid in function.");
@@ -718,35 +716,42 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
718716
// Also check that they are properly tracked inside the current
719717
// function.
720718
auto Def =
721-
OpenedArchetypes.getOpenedArchetypeDef(Ty.getCanonicalTypeOrNull());
719+
OpenedArchetypes.getOpenedArchetypeDef(Ty.getCanonicalTypeOrNull());
722720
require(Def, "Opened archetype should be registered in SILFunction");
723721
require(Def == AI ||
724-
Dominance->properlyDominates(cast<SILInstruction>(Def), AI),
722+
Dominance->properlyDominates(cast<SILInstruction>(Def), AI),
725723
"Use of an opened archetype should be dominated by a "
726724
"definition of this opened archetype");
727-
});
725+
}
726+
};
727+
728+
// Search for opened archetypes.
729+
for (auto &Sub : AS.getSubstitutions()) {
730+
Sub.getReplacement().visit(HandleType);
728731
}
732+
AS.getSubstCalleeType().visit(HandleType);
729733

730734
require(FoundOpenedArchetypes.size() ==
731735
AI->getOpenedArchetypeOperands().size(),
732-
"Number of opened archetypes in the substitutions list should "
733-
"match the number of opened archetype operands");
736+
"Number of opened archetypes in the substitutions "
737+
"list should match the number of type dependent operands");
734738

735739
for (auto &Op : AI->getOpenedArchetypeOperands()) {
736740
auto V = Op.get();
737741
require(isa<SILInstruction>(V),
738-
"opened archetype operand should refer to a SIL instruction");
742+
"opened archetype operand should refer to a SIL instruction");
739743
auto Archetype = getOpenedArchetypeOf(cast<SILInstruction>(V));
740-
require(Archetype, "opened archetype operand should define an opened archetype");
744+
require(Archetype,
745+
"opened archetype operand should define an opened archetype");
741746
require(FoundOpenedArchetypes.count(Archetype),
742-
"opened archetype operand does not correspond to any opened archetype from "
743-
"the substitutions list");
747+
"opened archetype operand does not correspond to any opened "
748+
"archetype from the substitutions list");
744749
}
745750
}
746751

747752
void checkFullApplySite(FullApplySite site) {
748-
checkApplySubstitutionsOpenedArchetypes(site.getInstruction(),
749-
site.getSubstitutions());
753+
checkApplyTypeDependentArguments(site);
754+
750755
// Then make sure that we have a type that can be substituted for the
751756
// callee.
752757
auto substTy = checkApplySubstitutions(site.getSubstitutions(),
@@ -866,7 +871,7 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
866871
require(resultInfo->getExtInfo().hasContext(),
867872
"result of closure cannot have a thin function type");
868873

869-
checkApplySubstitutionsOpenedArchetypes(PAI, PAI->getSubstitutions());
874+
checkApplyTypeDependentArguments(PAI);
870875

871876
auto substTy = checkApplySubstitutions(PAI->getSubstitutions(),
872877
PAI->getCallee()->getType());
@@ -1429,9 +1434,10 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
14291434
void checkMetatypeInst(MetatypeInst *MI) {
14301435
require(MI->getType().is<MetatypeType>(),
14311436
"metatype instruction must be of metatype type");
1432-
require(MI->getType().castTo<MetatypeType>()->hasRepresentation(),
1437+
auto MetaTy = MI->getType().castTo<MetatypeType>();
1438+
require(MetaTy->hasRepresentation(),
14331439
"metatype instruction must have a metatype representation");
1434-
verifyOpenedArchetype(MI, MI->getType().getSwiftRValueType());
1440+
verifyOpenedArchetype(MI, MetaTy.getInstanceType());
14351441
}
14361442
void checkValueMetatypeInst(ValueMetatypeInst *MI) {
14371443
require(MI->getType().is<MetatypeType>(),
@@ -1703,7 +1709,7 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
17031709
auto lookupType = AMI->getLookupType();
17041710
if (getOpenedArchetype(lookupType)) {
17051711
require(AMI->getOpenedArchetypeOperands().size() == 1,
1706-
"Must have an opened existential operand");
1712+
"Must have a type dependent operand for the opened archetype");
17071713
verifyOpenedArchetype(AMI, lookupType);
17081714
} else {
17091715
require(AMI->getOpenedArchetypeOperands().empty(),
@@ -1793,6 +1799,7 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
17931799
.getSwiftRValueType()
17941800
->isBindableTo(EMI->getType().getSwiftRValueType(), nullptr),
17951801
"result must be of the method's type");
1802+
verifyOpenedArchetype(EMI, EMI->getType().getSwiftRValueType());
17961803
}
17971804

17981805
void checkClassMethodInst(ClassMethodInst *CMI) {
@@ -2075,18 +2082,18 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
20752082
SILType resultType = I->getType();
20762083
require(resultType.is<ExistentialMetatypeType>(),
20772084
"init_existential_metatype result must be an existential metatype");
2085+
auto MetaTy = resultType.castTo<ExistentialMetatypeType>();
20782086
require(resultType.isObject(),
20792087
"init_existential_metatype result must not be an address");
2080-
require(resultType.castTo<ExistentialMetatypeType>()->hasRepresentation(),
2088+
require(MetaTy->hasRepresentation(),
20812089
"init_existential_metatype result must have a representation");
2082-
require(resultType.castTo<ExistentialMetatypeType>()->getRepresentation()
2090+
require(MetaTy->getRepresentation()
20832091
== operandType.castTo<MetatypeType>()->getRepresentation(),
20842092
"init_existential_metatype result must match representation of "
20852093
"operand");
20862094

20872095
checkExistentialProtocolConformances(resultType, I->getConformances());
2088-
verifyOpenedArchetype(
2089-
I, getOpenedArchetypeOf(I->getType().getSwiftRValueType()));
2096+
verifyOpenedArchetype(I, MetaTy.getInstanceType());
20902097
}
20912098

20922099
void checkExistentialProtocolConformances(SILType resultType,
@@ -2172,9 +2179,7 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
21722179
void verifyOpenedArchetype(SILInstruction *I, CanType Ty) {
21732180
if (!Ty)
21742181
return;
2175-
// Check for each referenced opened archetype from Ty
2176-
// that the instruction contains an opened archetype operand
2177-
// for it.
2182+
// Check the type and all of its contained types.
21782183
Ty.visit([&](Type t) {
21792184
if (!t->isOpenedExistential())
21802185
return;

lib/SILOptimizer/Transforms/CSE.cpp

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -681,13 +681,52 @@ bool CSE::processOpenExistentialRef(SILInstruction *Inst, ValueBase *V,
681681
auto &Builder = Cloner.getBuilder();
682682
Builder.setOpenedArchetypesTracker(&OpenedArchetypesTracker);
683683

684+
llvm::SmallPtrSet<SILInstruction *, 16> Processed;
684685
// Now clone each candidate and replace the opened archetype
685686
// by a dominating one.
686-
for (auto Candidate : Candidates) {
687+
while (!Candidates.empty()) {
688+
auto Candidate = Candidates.pop_back_val();
689+
if (Processed.count(Candidate))
690+
continue;
691+
// DependsOnOldOpenedArchetype is true if a candidate depends on the old
692+
// opened archetype.
693+
// First, check if it has any opened archetype operands.
694+
bool DependsOnOldOpenedArchetype =
695+
!Candidate->getOpenedArchetypeOperands().empty();
696+
// Then check if the result may depend on the opened archetype.
697+
if (!Candidate->use_empty() &&
698+
Candidate->getType().getSwiftRValueType()->hasOpenedExistential()) {
699+
// Check if the result type of the candidate depends on the opened
700+
// existential in question.
701+
auto ResultDependsOnOldOpenedArchetype =
702+
Candidate->getType().getSwiftRValueType().findIf(
703+
[&OldOpenedArchetype](Type t) -> bool {
704+
if (t.getCanonicalTypeOrNull() == OldOpenedArchetype)
705+
return true;
706+
return false;
707+
});
708+
if (ResultDependsOnOldOpenedArchetype) {
709+
DependsOnOldOpenedArchetype = true;
710+
// We need to update uses of this candidate, because their types
711+
// may be affected.
712+
for (auto Use : Candidate->getUses()) {
713+
Candidates.insert(Use->getUser());
714+
}
715+
}
716+
}
717+
// Remember that this candidate was processed already.
718+
Processed.insert(Candidate);
719+
720+
// No need to clone if there is no dependency on the old opened archetype.
721+
if (!DependsOnOldOpenedArchetype)
722+
continue;
723+
687724
Builder.getOpenedArchetypes().addOpenedArchetypeOperands(
688725
Candidate->getOpenedArchetypeOperands());
689726
Builder.setInsertionPoint(Candidate);
690727
auto NewI = Cloner.clone(Candidate);
728+
// Result types of candidate's uses instructions may be using this archetype.
729+
// Thus, we need to try to replace it there.
691730
Candidate->replaceAllUsesWith(NewI);
692731
if (I == Candidate->getIterator())
693732
I = NewI->getIterator();

test/SILOptimizer/cse.sil

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1325,6 +1325,8 @@ protocol Proto : class {
13251325
}
13261326

13271327
// Check that all open_existential_ref instructions are CSEd
1328+
// Any reference to the $@opened("1B685052-4796-11E6-B7DF-B8E856428C60") Proto should be replaced by
1329+
// references to $@opened("1B68354A-4796-11E6-B7DF-B8E856428C60") Proto.
13281330
// CHECK-LABEL: sil @cse_open_existential : $@convention(thin) (@guaranteed Proto, Bool) -> ()
13291331
// CHECK: bb0
13301332
// CHECK: %[[OPENED_EXISTENTIAL:[0-9]+]] = open_existential_ref %{{[0-9]+}} : $Proto to $@opened("1B68354A-4796-11E6-B7DF-B8E856428C60") Proto
@@ -1334,10 +1336,12 @@ protocol Proto : class {
13341336
// CHECK: bb1:
13351337
// CHECK-NEXT: %[[WM2:[0-9]+]] = witness_method $@opened("1B68354A-4796-11E6-B7DF-B8E856428C60") Proto, #Proto.doThat!1
13361338
// CHECK-NEXT: apply %[[WM2]]{{.*}}(%[[OPENED_EXISTENTIAL]])
1337-
// CHECK-NEXT: br bb
1339+
// CHECK-NOT: 1B6851A6-4796-11E6-B7DF-B8E856428C60
1340+
// CHECK: br bb
13381341
// CHECK: bb2:
13391342
// CHECK-NEXT: apply %[[WM1]]{{.*}}(%[[OPENED_EXISTENTIAL]])
1340-
// CHECK-NEXT: br bb
1343+
// CHECK-NOT: 1B6851A6-4796-11E6-B7DF-B8E856428C60
1344+
// CHECK: br bb
13411345
// CHECK: bb3:
13421346
// CHECK-NEXT: tuple
13431347
// CHECK-NEXT: return
@@ -1359,10 +1363,16 @@ bb2: // Preds: bb0
13591363
%13 = open_existential_ref %0 : $Proto to $@opened("1B6851A6-4796-11E6-B7DF-B8E856428C60") Proto
13601364
%14 = witness_method $@opened("1B6851A6-4796-11E6-B7DF-B8E856428C60") Proto, #Proto.doThis!1, %13 : $@opened("1B6851A6-4796-11E6-B7DF-B8E856428C60") Proto : $@convention(witness_method) <τ_0_0 where τ_0_0 : Proto> (@guaranteed τ_0_0) -> ()
13611365
%15 = apply %14<@opened("1B6851A6-4796-11E6-B7DF-B8E856428C60") Proto>(%13) : $@convention(witness_method) <τ_0_0 where τ_0_0 : Proto> (@guaranteed τ_0_0) -> ()
1366+
%16 = alloc_stack $@opened("1B6851A6-4796-11E6-B7DF-B8E856428C60") Proto
1367+
store %13 to %16 : $*@opened("1B6851A6-4796-11E6-B7DF-B8E856428C60") Proto
1368+
// This is to check that result types of instructions are updated by CSE as well.
1369+
%17 = load %16 : $*@opened("1B6851A6-4796-11E6-B7DF-B8E856428C60") Proto
1370+
strong_release %17 : $@opened("1B6851A6-4796-11E6-B7DF-B8E856428C60") Proto
1371+
dealloc_stack %16 : $*@opened("1B6851A6-4796-11E6-B7DF-B8E856428C60") Proto
13621372
br bb3
13631373

13641374
bb3:
1365-
%17 = tuple ()
1366-
return %17 : $()
1375+
%20 = tuple ()
1376+
return %20 : $()
13671377
}
13681378

0 commit comments

Comments
 (0)