Skip to content

Commit 87be0cd

Browse files
committed
Fix non-determinisms
GVNSink used to order instructions based on their pointer values and was prone to non-determinism because of that. Using DFSNumber for ordering all the values fixes the non-determinism
1 parent 2f58b9a commit 87be0cd

File tree

2 files changed

+75
-14
lines changed

2 files changed

+75
-14
lines changed

llvm/lib/Transforms/Scalar/GVNSink.cpp

Lines changed: 51 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -222,20 +222,29 @@ raw_ostream &operator<<(raw_ostream &OS, const SinkingInstructionCandidate &C) {
222222
class ModelledPHI {
223223
SmallVector<Value *, 4> Values;
224224
SmallVector<BasicBlock *, 4> Blocks;
225+
DominatorTree *DT;
225226

226227
public:
227228
ModelledPHI() = default;
228229

229-
ModelledPHI(const PHINode *PN) {
230+
ModelledPHI(const PHINode *PN, DominatorTree *DT) : DT(DT) {
230231
// BasicBlock comes first so we sort by basic block pointer order, then by value pointer order.
231-
SmallVector<std::pair<BasicBlock *, Value *>, 4> Ops;
232+
using OpsType = std::pair<BasicBlock *, Value *>;
233+
SmallVector<OpsType, 4> Ops;
232234
for (unsigned I = 0, E = PN->getNumIncomingValues(); I != E; ++I)
233235
Ops.push_back({PN->getIncomingBlock(I), PN->getIncomingValue(I)});
234-
llvm::sort(Ops);
236+
237+
auto DFSOrder = [DT](OpsType O1, OpsType O2) {
238+
return DT->getNode(O1.first) < DT->getNode(O2.first);
239+
};
240+
// Sort by DFSNumber to have a deterministic order.
241+
llvm::sort(Ops, DFSOrder);
242+
235243
for (auto &P : Ops) {
236244
Blocks.push_back(P.first);
237245
Values.push_back(P.second);
238246
}
247+
verifyModelledPHI();
239248
}
240249

241250
/// Create a dummy ModelledPHI that will compare unequal to any other ModelledPHI
@@ -247,19 +256,37 @@ class ModelledPHI {
247256
return M;
248257
}
249258

259+
void verifyModelledPHI() {
260+
assert(Values.size() > 1 && Blocks.size() > 1 &&
261+
"Modelling PHI with less than 2 values");
262+
auto DFSOrder = [this](const BasicBlock *BB1, const BasicBlock *BB2) {
263+
return this->DT->getNode(BB1) < this->DT->getNode(BB2);
264+
};
265+
assert(llvm::is_sorted(Blocks, DFSOrder));
266+
int C = 0;
267+
llvm::for_each(Values, [&C, this](const Value* V) {
268+
const Instruction *I = cast<Instruction>(V);
269+
assert(I->getParent() == this->Blocks[C++]);
270+
});
271+
}
250272
/// Create a PHI from an array of incoming values and incoming blocks.
251-
template <typename VArray, typename BArray>
252-
ModelledPHI(const VArray &V, const BArray &B) {
273+
ModelledPHI(SmallVectorImpl<Instruction *> &V,
274+
SmallSetVector<BasicBlock *, 4> &B, DominatorTree *DT)
275+
: DT(DT) {
276+
// The order of Values and Blocks are already as per their DFSNumbers.
253277
llvm::copy(V, std::back_inserter(Values));
254278
llvm::copy(B, std::back_inserter(Blocks));
279+
verifyModelledPHI();
255280
}
256281

257282
/// Create a PHI from [I[OpNum] for I in Insts].
258-
template <typename BArray>
259-
ModelledPHI(ArrayRef<Instruction *> Insts, unsigned OpNum, const BArray &B) {
283+
ModelledPHI(ArrayRef<Instruction *> Insts, unsigned OpNum,
284+
SmallSetVector<BasicBlock *, 4> &B, DominatorTree *DT)
285+
: DT(DT) {
260286
llvm::copy(B, std::back_inserter(Blocks));
261287
for (auto *I : Insts)
262288
Values.push_back(I->getOperand(OpNum));
289+
verifyModelledPHI();
263290
}
264291

265292
/// Restrict the PHI's contents down to only \c NewBlocks.
@@ -297,7 +324,8 @@ class ModelledPHI {
297324

298325
// Hash functor
299326
unsigned hash() const {
300-
return (unsigned)hash_combine_range(Values.begin(), Values.end());
327+
// Is deterministic because Values are saved in DFSOrder.
328+
return (unsigned)hash_combine_range(Values.begin(), Values.end());
301329
}
302330

303331
bool operator==(const ModelledPHI &Other) const {
@@ -566,7 +594,7 @@ class ValueTable {
566594

567595
class GVNSink {
568596
public:
569-
GVNSink() = default;
597+
GVNSink(DominatorTree *DT) : DT(DT) {}
570598

571599
bool run(Function &F) {
572600
LLVM_DEBUG(dbgs() << "GVNSink: running on function @" << F.getName()
@@ -583,6 +611,7 @@ class GVNSink {
583611

584612
private:
585613
ValueTable VN;
614+
DominatorTree *DT;
586615

587616
bool shouldAvoidSinkingInstruction(Instruction *I) {
588617
// These instructions may change or break semantics if moved.
@@ -603,7 +632,7 @@ class GVNSink {
603632
void analyzeInitialPHIs(BasicBlock *BB, ModelledPHISet &PHIs,
604633
SmallPtrSetImpl<Value *> &PHIContents) {
605634
for (PHINode &PN : BB->phis()) {
606-
auto MPHI = ModelledPHI(&PN);
635+
auto MPHI = ModelledPHI(&PN, DT);
607636
PHIs.insert(MPHI);
608637
for (auto *V : MPHI.getValues())
609638
PHIContents.insert(V);
@@ -691,7 +720,7 @@ GVNSink::analyzeInstructionForSinking(LockstepReverseIterator &LRI,
691720
}
692721

693722
// The sunk instruction's results.
694-
ModelledPHI NewPHI(NewInsts, ActivePreds);
723+
ModelledPHI NewPHI(NewInsts, ActivePreds, DT);
695724

696725
// Does sinking this instruction render previous PHIs redundant?
697726
if (NeededPHIs.erase(NewPHI))
@@ -728,7 +757,7 @@ GVNSink::analyzeInstructionForSinking(LockstepReverseIterator &LRI,
728757
return std::nullopt;
729758

730759
for (unsigned OpNum = 0, E = I0->getNumOperands(); OpNum != E; ++OpNum) {
731-
ModelledPHI PHI(NewInsts, OpNum, ActivePreds);
760+
ModelledPHI PHI(NewInsts, OpNum, ActivePreds, DT);
732761
if (PHI.areAllIncomingValuesSame())
733762
continue;
734763
if (!canReplaceOperandWithVariable(I0, OpNum))
@@ -774,7 +803,11 @@ unsigned GVNSink::sinkBB(BasicBlock *BBEnd) {
774803
}
775804
if (Preds.size() < 2)
776805
return 0;
777-
llvm::sort(Preds);
806+
auto DFSOrder = [this](const BasicBlock *BB1, const BasicBlock *BB2) {
807+
return this->DT->getNode(BB1) < this->DT->getNode(BB2);
808+
};
809+
// Sort by DFSNumber to have a deterministic order.
810+
llvm::sort(Preds, DFSOrder);
778811

779812
unsigned NumOrigPreds = Preds.size();
780813
// We can only sink instructions through unconditional branches.
@@ -886,8 +919,12 @@ void GVNSink::sinkLastInstruction(ArrayRef<BasicBlock *> Blocks,
886919
} // end anonymous namespace
887920

888921
PreservedAnalyses GVNSinkPass::run(Function &F, FunctionAnalysisManager &AM) {
889-
GVNSink G;
922+
auto &DT = AM.getResult<DominatorTreeAnalysis>(F);
923+
GVNSink G(&DT);
890924
if (!G.run(F))
891925
return PreservedAnalyses::all();
926+
927+
// PHI nodes get inserted which haven't been added to the Dominator Tree.
928+
// FIXME: Update DominatorTree to account for sunk instructions.
892929
return PreservedAnalyses::none();
893930
}

llvm/test/Transforms/GVNSink/int_sideeffect.ll

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,27 @@ if.end:
2828
ret float %phi
2929
}
3030

31+
; CHECK-LABEL: scalarsSinkingReverse
32+
; CHECK-NOT: fmul
33+
; CHECK: = phi
34+
; CHECK: = fmul
35+
define float @scalarsSinkingReverse(float %d, float %m, float %a, i1 %cmp) {
36+
entry:
37+
br i1 %cmp, label %if.then, label %if.else
38+
39+
if.then:
40+
%add = fadd float %m, %a
41+
%mul1 = fmul float %add, %d
42+
br label %if.end
43+
44+
if.else:
45+
call void @llvm.sideeffect()
46+
%sub = fsub float %m, %a
47+
%mul0 = fmul float %sub, %d
48+
br label %if.end
49+
50+
if.end:
51+
%phi = phi float [ %mul1, %if.then ], [ %mul0, %if.else ]
52+
ret float %phi
53+
}
54+

0 commit comments

Comments
 (0)