Skip to content

Commit 70678ab

Browse files
committed
SILOptimizer: Fix analysis invalidation after devirtualization.
Devirtualizing try_apply modified the CFG, but passes that run devirtualization were not invalidating any CFG analyses, such as the domtree. This could hypothetically have caused miscompilation, but will be caught by running with -sil-verify-all.
1 parent 75bad90 commit 70678ab

File tree

5 files changed

+99
-69
lines changed

5 files changed

+99
-69
lines changed

include/swift/SILOptimizer/Utils/Devirtualize.h

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,11 @@ SubstitutionMap getWitnessMethodSubstitutions(SILModule &Module, ApplySite AI,
6565
///
6666
/// If this succeeds, the caller must call deleteDevirtualizedApply on
6767
/// the original apply site.
68-
ApplySite tryDevirtualizeApply(ApplySite AI,
69-
ClassHierarchyAnalysis *CHA,
70-
OptRemark::Emitter *ORE = nullptr);
68+
///
69+
/// Return the new apply and true if the CFG was also modified.
70+
std::pair<ApplySite, bool>
71+
tryDevirtualizeApply(ApplySite AI, ClassHierarchyAnalysis *CHA,
72+
OptRemark::Emitter *ORE = nullptr);
7173
bool canDevirtualizeApply(FullApplySite AI, ClassHierarchyAnalysis *CHA);
7274
bool canDevirtualizeClassMethod(FullApplySite AI, ClassDecl *CD,
7375
OptRemark::Emitter *ORE = nullptr,
@@ -79,21 +81,23 @@ CanType getSelfInstanceType(CanType ClassOrMetatypeType);
7981
/// Devirtualize the given apply site, which is known to be devirtualizable.
8082
///
8183
/// The caller must call deleteDevirtualizedApply on the original apply site.
82-
FullApplySite devirtualizeClassMethod(FullApplySite AI,
83-
SILValue ClassInstance,
84-
ClassDecl *CD,
85-
OptRemark::Emitter *ORE);
84+
///
85+
/// Return the new apply and true if the CFG was also modified.
86+
std::pair<FullApplySite, bool> devirtualizeClassMethod(FullApplySite AI,
87+
SILValue ClassInstance,
88+
ClassDecl *CD,
89+
OptRemark::Emitter *ORE);
8690

8791
/// Attempt to devirtualize the given apply site, which is known to be
8892
/// of a class method. If this fails, the returned FullApplySite will be null.
8993
///
9094
/// If this succeeds, the caller must call deleteDevirtualizedApply on
9195
/// the original apply site.
92-
FullApplySite
93-
tryDevirtualizeClassMethod(FullApplySite AI,
94-
SILValue ClassInstance,
95-
ClassDecl *CD,
96-
OptRemark::Emitter *ORE,
96+
///
97+
/// Return the new apply and true if the CFG was also modified.
98+
std::pair<FullApplySite, bool>
99+
tryDevirtualizeClassMethod(FullApplySite AI, SILValue ClassInstance,
100+
ClassDecl *CD, OptRemark::Emitter *ORE,
97101
bool isEffectivelyFinalMethod = false);
98102

99103
/// Attempt to devirtualize the given apply site, which is known to be
@@ -102,7 +106,9 @@ tryDevirtualizeClassMethod(FullApplySite AI,
102106
///
103107
/// If this succeeds, the caller must call deleteDevirtualizedApply on
104108
/// the original apply site.
105-
ApplySite tryDevirtualizeWitnessMethod(ApplySite AI, OptRemark::Emitter *ORE);
109+
///
110+
/// Return the new apply and true if the CFG was also modified.
111+
std::pair<ApplySite, bool> tryDevirtualizeWitnessMethod(ApplySite AI, OptRemark::Emitter *ORE);
106112

107113
/// Delete a successfully-devirtualized apply site. This must always be
108114
/// called after devirtualizing an apply; not only is it not semantically

lib/SILOptimizer/Mandatory/MandatoryInlining.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -760,7 +760,7 @@ getCalleeFunction(SILFunction *F, FullApplySite AI, bool &IsThick,
760760

761761
static SILInstruction *tryDevirtualizeApplyHelper(FullApplySite InnerAI,
762762
ClassHierarchyAnalysis *CHA) {
763-
auto NewInst = tryDevirtualizeApply(InnerAI, CHA);
763+
auto NewInst = tryDevirtualizeApply(InnerAI, CHA).first;
764764
if (!NewInst)
765765
return InnerAI.getInstruction();
766766

lib/SILOptimizer/Transforms/Devirtualizer.cpp

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,10 @@ using namespace swift;
3030
namespace {
3131

3232
class Devirtualizer : public SILFunctionTransform {
33+
bool Changed = false;
34+
bool ChangedCFG = false;
3335

34-
bool devirtualizeAppliesInFunction(SILFunction &F,
36+
void devirtualizeAppliesInFunction(SILFunction &F,
3537
ClassHierarchyAnalysis *CHA);
3638

3739
/// The entry point to the transformation.
@@ -41,17 +43,22 @@ class Devirtualizer : public SILFunctionTransform {
4143
LLVM_DEBUG(llvm::dbgs() << "***** Devirtualizer on function:" << F.getName()
4244
<< " *****\n");
4345

44-
if (devirtualizeAppliesInFunction(F, CHA))
46+
Changed = false;
47+
ChangedCFG = false;
48+
devirtualizeAppliesInFunction(F, CHA);
49+
if (ChangedCFG)
50+
invalidateAnalysis(SILAnalysis::InvalidationKind::Everything);
51+
else if (Changed)
4552
invalidateAnalysis(SILAnalysis::InvalidationKind::CallsAndInstructions);
4653
}
4754

4855
};
4956

5057
} // end anonymous namespace
5158

52-
bool Devirtualizer::devirtualizeAppliesInFunction(SILFunction &F,
59+
// Return true if any calls changed, and true if the CFG also changed.
60+
void Devirtualizer::devirtualizeAppliesInFunction(SILFunction &F,
5361
ClassHierarchyAnalysis *CHA) {
54-
bool Changed = false;
5562
llvm::SmallVector<ApplySite, 8> NewApplies;
5663
OptRemark::Emitter ORE(DEBUG_TYPE, F.getModule());
5764

@@ -69,11 +76,14 @@ bool Devirtualizer::devirtualizeAppliesInFunction(SILFunction &F,
6976
}
7077
}
7178
for (auto Apply : Applies) {
72-
auto NewInst = tryDevirtualizeApply(Apply, CHA, &ORE);
79+
ApplySite NewInst;
80+
bool modifiedCFG;
81+
std::tie(NewInst, modifiedCFG) = tryDevirtualizeApply(Apply, CHA, &ORE);
7382
if (!NewInst)
7483
continue;
7584

7685
Changed = true;
86+
ChangedCFG |= modifiedCFG;
7787

7888
deleteDevirtualizedApply(Apply);
7989
NewApplies.push_back(NewInst);
@@ -105,8 +115,6 @@ bool Devirtualizer::devirtualizeAppliesInFunction(SILFunction &F,
105115
if (CalleeFn->isDefinition() && CalleeFn->shouldOptimize())
106116
addFunctionToPassManagerWorklist(CalleeFn, nullptr);
107117
}
108-
109-
return Changed;
110118
}
111119

112120
SILTransform *swift::createDevirtualizer() { return new Devirtualizer(); }

lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,8 @@ static FullApplySite speculateMonomorphicTarget(FullApplySite AI,
229229

230230
// Devirtualize the apply instruction on the identical path.
231231
auto NewInst =
232-
devirtualizeClassMethod(IdenAI, DownCastedClassInstance, CD, nullptr);
232+
devirtualizeClassMethod(IdenAI, DownCastedClassInstance, CD, nullptr)
233+
.first;
233234
assert(NewInst && "Expected to be able to devirtualize apply!");
234235
(void)NewInst;
235236

@@ -414,7 +415,8 @@ static bool tryToSpeculateTarget(FullApplySite AI, ClassHierarchyAnalysis *CHA,
414415
// try to devirtualize it completely.
415416
ClassHierarchyAnalysis::ClassList Subs;
416417
if (isDefaultCaseKnown(CHA, AI, CD, Subs)) {
417-
auto NewInst = tryDevirtualizeClassMethod(AI, SubTypeValue, CD, &ORE);
418+
auto NewInst =
419+
tryDevirtualizeClassMethod(AI, SubTypeValue, CD, &ORE).first;
418420
if (NewInst)
419421
deleteDevirtualizedApply(AI);
420422
return bool(NewInst);
@@ -574,7 +576,8 @@ static bool tryToSpeculateTarget(FullApplySite AI, ClassHierarchyAnalysis *CHA,
574576
ORE.emit(RB);
575577
return true;
576578
}
577-
auto NewInst = tryDevirtualizeClassMethod(AI, SubTypeValue, CD, nullptr);
579+
auto NewInst =
580+
tryDevirtualizeClassMethod(AI, SubTypeValue, CD, nullptr).first;
578581
if (NewInst) {
579582
ORE.emit(RB);
580583
deleteDevirtualizedApply(AI);

lib/SILOptimizer/Utils/Devirtualize.cpp

Lines changed: 58 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -482,12 +482,12 @@ static ApplyInst *replaceApplyInst(SILBuilder &builder, SILLocation loc,
482482
return newAI;
483483
}
484484

485-
static TryApplyInst *replaceTryApplyInst(SILBuilder &builder, SILLocation loc,
486-
TryApplyInst *oldTAI, SILValue newFn,
487-
SubstitutionMap newSubs,
488-
ArrayRef<SILValue> newArgs,
489-
SILFunctionConventions conv,
490-
ArrayRef<SILValue> newArgBorrows) {
485+
// Return the new try_apply and true if a cast required CFG modification.
486+
static std::pair<TryApplyInst *, bool>
487+
replaceTryApplyInst(SILBuilder &builder, SILLocation loc, TryApplyInst *oldTAI,
488+
SILValue newFn, SubstitutionMap newSubs,
489+
ArrayRef<SILValue> newArgs, SILFunctionConventions conv,
490+
ArrayRef<SILValue> newArgBorrows) {
491491
SILBasicBlock *normalBB = oldTAI->getNormalBB();
492492
SILBasicBlock *resultBB = nullptr;
493493

@@ -537,7 +537,7 @@ static TryApplyInst *replaceTryApplyInst(SILBuilder &builder, SILLocation loc,
537537
}
538538

539539
builder.setInsertionPoint(normalBB->begin());
540-
return newTAI;
540+
return {newTAI, resultCastRequired};
541541
}
542542

543543
static BeginApplyInst *
@@ -599,17 +599,18 @@ replacePartialApplyInst(SILBuilder &builder, SILLocation loc,
599599
return newPAI;
600600
}
601601

602-
static ApplySite replaceApplySite(SILBuilder &builder, SILLocation loc,
603-
ApplySite oldAS, SILValue newFn,
604-
SubstitutionMap newSubs,
605-
ArrayRef<SILValue> newArgs,
606-
SILFunctionConventions conv,
607-
ArrayRef<SILValue> newArgBorrows) {
602+
// Return the new apply and true if the CFG was also modified.
603+
static std::pair<ApplySite, bool>
604+
replaceApplySite(SILBuilder &builder, SILLocation loc, ApplySite oldAS,
605+
SILValue newFn, SubstitutionMap newSubs,
606+
ArrayRef<SILValue> newArgs, SILFunctionConventions conv,
607+
ArrayRef<SILValue> newArgBorrows) {
608608
switch (oldAS.getKind()) {
609609
case ApplySiteKind::ApplyInst: {
610610
auto *oldAI = cast<ApplyInst>(oldAS);
611-
return replaceApplyInst(builder, loc, oldAI, newFn, newSubs, newArgs,
612-
newArgBorrows);
611+
return {replaceApplyInst(builder, loc, oldAI, newFn, newSubs, newArgs,
612+
newArgBorrows),
613+
false};
613614
}
614615
case ApplySiteKind::TryApplyInst: {
615616
auto *oldTAI = cast<TryApplyInst>(oldAS);
@@ -618,14 +619,16 @@ static ApplySite replaceApplySite(SILBuilder &builder, SILLocation loc,
618619
}
619620
case ApplySiteKind::BeginApplyInst: {
620621
auto *oldBAI = dyn_cast<BeginApplyInst>(oldAS);
621-
return replaceBeginApplyInst(builder, loc, oldBAI, newFn, newSubs, newArgs,
622-
newArgBorrows);
622+
return {replaceBeginApplyInst(builder, loc, oldBAI, newFn, newSubs, newArgs,
623+
newArgBorrows),
624+
false};
623625
}
624626
case ApplySiteKind::PartialApplyInst: {
625627
assert(newArgBorrows.empty());
626628
auto *oldPAI = cast<PartialApplyInst>(oldAS);
627-
return replacePartialApplyInst(builder, loc, oldPAI, newFn, newSubs,
628-
newArgs);
629+
return {
630+
replacePartialApplyInst(builder, loc, oldPAI, newFn, newSubs, newArgs),
631+
false};
629632
}
630633
}
631634
llvm_unreachable("covered switch");
@@ -729,10 +732,12 @@ bool swift::canDevirtualizeClassMethod(FullApplySite applySite, ClassDecl *cd,
729732
/// \p ClassOrMetatype is a class value or metatype value that is the
730733
/// self argument of the apply we will devirtualize.
731734
/// return the result value of the new ApplyInst if created one or null.
732-
FullApplySite swift::devirtualizeClassMethod(FullApplySite applySite,
733-
SILValue classOrMetatype,
734-
ClassDecl *cd,
735-
OptRemark::Emitter *ore) {
735+
///
736+
/// Return the new apply and true if the CFG was also modified.
737+
std::pair<FullApplySite, bool>
738+
swift::devirtualizeClassMethod(FullApplySite applySite,
739+
SILValue classOrMetatype, ClassDecl *cd,
740+
OptRemark::Emitter *ore) {
736741
LLVM_DEBUG(llvm::dbgs() << " Trying to devirtualize : "
737742
<< *applySite.getInstruction());
738743

@@ -793,8 +798,10 @@ FullApplySite swift::devirtualizeClassMethod(FullApplySite applySite,
793798
newArgs.push_back(arg);
794799
++paramArgIter;
795800
}
796-
ApplySite newAS = replaceApplySite(builder, loc, applySite, fri, subs,
797-
newArgs, substConv, newArgBorrows);
801+
ApplySite newAS;
802+
bool modifiedCFG;
803+
std::tie(newAS, modifiedCFG) = replaceApplySite(
804+
builder, loc, applySite, fri, subs, newArgs, substConv, newArgBorrows);
798805
FullApplySite newAI = FullApplySite::isa(newAS.getInstruction());
799806
assert(newAI);
800807

@@ -808,16 +815,14 @@ FullApplySite swift::devirtualizeClassMethod(FullApplySite applySite,
808815
});
809816
NumClassDevirt++;
810817

811-
return newAI;
818+
return {newAI, modifiedCFG};
812819
}
813820

814-
FullApplySite swift::tryDevirtualizeClassMethod(FullApplySite applySite,
815-
SILValue classInstance,
816-
ClassDecl *cd,
817-
OptRemark::Emitter *ore,
818-
bool isEffectivelyFinalMethod) {
821+
std::pair<FullApplySite, bool> swift::tryDevirtualizeClassMethod(
822+
FullApplySite applySite, SILValue classInstance, ClassDecl *cd,
823+
OptRemark::Emitter *ore, bool isEffectivelyFinalMethod) {
819824
if (!canDevirtualizeClassMethod(applySite, cd, ore, isEffectivelyFinalMethod))
820-
return FullApplySite();
825+
return {FullApplySite(), false};
821826
return devirtualizeClassMethod(applySite, classInstance, cd, ore);
822827
}
823828

@@ -960,9 +965,12 @@ swift::getWitnessMethodSubstitutions(SILModule &module, ApplySite applySite,
960965
/// Generate a new apply of a function_ref to replace an apply of a
961966
/// witness_method when we've determined the actual function we'll end
962967
/// up calling.
963-
static ApplySite devirtualizeWitnessMethod(ApplySite applySite, SILFunction *f,
964-
ProtocolConformanceRef cRef,
965-
OptRemark::Emitter *ore) {
968+
///
969+
/// Return the new apply and true if the CFG was also modified.
970+
static std::pair<ApplySite, bool>
971+
devirtualizeWitnessMethod(ApplySite applySite, SILFunction *f,
972+
ProtocolConformanceRef cRef,
973+
OptRemark::Emitter *ore) {
966974
// We know the witness thunk and the corresponding set of substitutions
967975
// required to invoke the protocol method at this point.
968976
auto &module = applySite.getModule();
@@ -1017,7 +1025,9 @@ static ApplySite devirtualizeWitnessMethod(ApplySite applySite, SILFunction *f,
10171025
SILLocation loc = applySite.getLoc();
10181026
auto *fri = applyBuilder.createFunctionRefFor(loc, f);
10191027

1020-
ApplySite newApplySite =
1028+
ApplySite newApplySite;
1029+
bool modifiedCFG;
1030+
std::tie(newApplySite, modifiedCFG) =
10211031
replaceApplySite(applyBuilder, loc, applySite, fri, subMap, arguments,
10221032
substConv, borrowedArgs);
10231033

@@ -1029,7 +1039,7 @@ static ApplySite devirtualizeWitnessMethod(ApplySite applySite, SILFunction *f,
10291039
<< "Devirtualized call to " << NV("Method", f);
10301040
});
10311041
NumWitnessDevirt++;
1032-
return newApplySite;
1042+
return {newApplySite, modifiedCFG};
10331043
}
10341044

10351045
static bool canDevirtualizeWitnessMethod(ApplySite applySite) {
@@ -1066,10 +1076,11 @@ static bool canDevirtualizeWitnessMethod(ApplySite applySite) {
10661076
/// In the cases where we can statically determine the function that
10671077
/// we'll call to, replace an apply of a witness_method with an apply
10681078
/// of a function_ref, returning the new apply.
1069-
ApplySite swift::tryDevirtualizeWitnessMethod(ApplySite applySite,
1070-
OptRemark::Emitter *ore) {
1079+
std::pair<ApplySite, bool>
1080+
swift::tryDevirtualizeWitnessMethod(ApplySite applySite,
1081+
OptRemark::Emitter *ore) {
10711082
if (!canDevirtualizeWitnessMethod(applySite))
1072-
return ApplySite();
1083+
return {ApplySite(), false};
10731084

10741085
SILFunction *f;
10751086
SILWitnessTable *wt;
@@ -1088,9 +1099,11 @@ ApplySite swift::tryDevirtualizeWitnessMethod(ApplySite applySite,
10881099

10891100
/// Attempt to devirtualize the given apply if possible, and return a
10901101
/// new instruction in that case, or nullptr otherwise.
1091-
ApplySite swift::tryDevirtualizeApply(ApplySite applySite,
1092-
ClassHierarchyAnalysis *cha,
1093-
OptRemark::Emitter *ore) {
1102+
///
1103+
/// Return the new apply and true if the CFG was also modified.
1104+
std::pair<ApplySite, bool>
1105+
swift::tryDevirtualizeApply(ApplySite applySite, ClassHierarchyAnalysis *cha,
1106+
OptRemark::Emitter *ore) {
10941107
LLVM_DEBUG(llvm::dbgs() << " Trying to devirtualize: "
10951108
<< *applySite.getInstruction());
10961109

@@ -1105,7 +1118,7 @@ ApplySite swift::tryDevirtualizeApply(ApplySite applySite,
11051118
// TODO: check if we can also de-virtualize partial applies of class methods.
11061119
FullApplySite fas = FullApplySite::isa(applySite.getInstruction());
11071120
if (!fas)
1108-
return ApplySite();
1121+
return {ApplySite(), false};
11091122

11101123
/// Optimize a class_method and alloc_ref pair into a direct function
11111124
/// reference:
@@ -1151,7 +1164,7 @@ ApplySite swift::tryDevirtualizeApply(ApplySite applySite,
11511164
return tryDevirtualizeClassMethod(fas, instance, cd, ore);
11521165
}
11531166

1154-
return ApplySite();
1167+
return {ApplySite(), false};
11551168
}
11561169

11571170
bool swift::canDevirtualizeApply(FullApplySite applySite,

0 commit comments

Comments
 (0)