Skip to content

Commit 829992c

Browse files
authored
[NewGVN] Prevent cyclic dependencies by ensuring Leader has min RPO number (#82110)
Cyclic dependencies in NewGVN can result in miscompilation and termination issues. This patch ensures that the Class Leader is always the member with the lowest RPO number. This ensures that the Class Leader is processed before all other members, making the cyclic dependence impossible. This fixes #35683. Regressions: - 'simp-to-self.ll' regresses due to a known limitation in the way NewGVN and InstSimplify interact. With the new leader, InstSimplify does not know that %conv is 1 and fails to simplify.
1 parent 109b508 commit 829992c

File tree

3 files changed

+51
-22
lines changed

3 files changed

+51
-22
lines changed

llvm/lib/Transforms/Scalar/NewGVN.cpp

Lines changed: 46 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,8 @@ class CongruenceClass {
297297
using MemoryMemberSet = SmallPtrSet<const MemoryMemberType *, 2>;
298298

299299
explicit CongruenceClass(unsigned ID) : ID(ID) {}
300-
CongruenceClass(unsigned ID, Value *Leader, const Expression *E)
300+
CongruenceClass(unsigned ID, std::pair<Value *, unsigned int> Leader,
301+
const Expression *E)
301302
: ID(ID), RepLeader(Leader), DefiningExpr(E) {}
302303

303304
unsigned getID() const { return ID; }
@@ -311,15 +312,23 @@ class CongruenceClass {
311312
}
312313

313314
// Leader functions
314-
Value *getLeader() const { return RepLeader; }
315-
void setLeader(Value *Leader) { RepLeader = Leader; }
315+
Value *getLeader() const { return RepLeader.first; }
316+
void setLeader(std::pair<Value *, unsigned int> Leader) {
317+
RepLeader = Leader;
318+
}
316319
const std::pair<Value *, unsigned int> &getNextLeader() const {
317320
return NextLeader;
318321
}
319322
void resetNextLeader() { NextLeader = {nullptr, ~0}; }
320-
void addPossibleNextLeader(std::pair<Value *, unsigned int> LeaderPair) {
321-
if (LeaderPair.second < NextLeader.second)
323+
bool addPossibleLeader(std::pair<Value *, unsigned int> LeaderPair) {
324+
if (LeaderPair.second < RepLeader.second) {
325+
NextLeader = RepLeader;
326+
RepLeader = LeaderPair;
327+
return true;
328+
} else if (LeaderPair.second < NextLeader.second) {
322329
NextLeader = LeaderPair;
330+
}
331+
return false;
323332
}
324333

325334
Value *getStoredValue() const { return RepStoredValue; }
@@ -392,11 +401,13 @@ class CongruenceClass {
392401
private:
393402
unsigned ID;
394403

395-
// Representative leader.
396-
Value *RepLeader = nullptr;
404+
// Representative leader and its corresponding RPO number.
405+
// The leader must have the lowest RPO number.
406+
std::pair<Value *, unsigned int> RepLeader = {nullptr, ~0U};
397407

398-
// The most dominating leader after our current leader, because the member set
399-
// is not sorted and is expensive to keep sorted all the time.
408+
// The most dominating leader after our current leader (given by the RPO
409+
// number), because the member set is not sorted and is expensive to keep
410+
// sorted all the time.
400411
std::pair<Value *, unsigned int> NextLeader = {nullptr, ~0U};
401412

402413
// If this is represented by a store, the value of the store.
@@ -735,7 +746,19 @@ class NewGVN {
735746

736747
// Congruence class handling.
737748
CongruenceClass *createCongruenceClass(Value *Leader, const Expression *E) {
738-
auto *result = new CongruenceClass(NextCongruenceNum++, Leader, E);
749+
// Set RPO to 0 for values that are always available (constants and function
750+
// args). These should always be made leader.
751+
unsigned LeaderDFS = 0;
752+
753+
// If Leader is not specified, either we have a memory class or the leader
754+
// will be set later. Otherwise, if Leader is an Instruction, set LeaderDFS
755+
// to its RPO number.
756+
if (!Leader)
757+
LeaderDFS = ~0;
758+
else if (auto *I = dyn_cast<Instruction>(Leader))
759+
LeaderDFS = InstrToDFSNum(I);
760+
auto *result =
761+
new CongruenceClass(NextCongruenceNum++, {Leader, LeaderDFS}, E);
739762
CongruenceClasses.emplace_back(result);
740763
return result;
741764
}
@@ -2248,8 +2271,13 @@ void NewGVN::moveValueToNewCongruenceClass(Instruction *I, const Expression *E,
22482271
OldClass->erase(I);
22492272
NewClass->insert(I);
22502273

2251-
if (NewClass->getLeader() != I)
2252-
NewClass->addPossibleNextLeader({I, InstrToDFSNum(I)});
2274+
// Ensure that the leader has the lowest RPO. If the leader changed notify all
2275+
// members of the class.
2276+
if (NewClass->getLeader() != I &&
2277+
NewClass->addPossibleLeader({I, InstrToDFSNum(I)})) {
2278+
markValueLeaderChangeTouched(NewClass);
2279+
}
2280+
22532281
// Handle our special casing of stores.
22542282
if (auto *SI = dyn_cast<StoreInst>(I)) {
22552283
OldClass->decStoreCount();
@@ -2273,7 +2301,7 @@ void NewGVN::moveValueToNewCongruenceClass(Instruction *I, const Expression *E,
22732301
<< " because store joined class\n");
22742302
// If we changed the leader, we have to mark it changed because we don't
22752303
// know what it will do to symbolic evaluation.
2276-
NewClass->setLeader(SI);
2304+
NewClass->setLeader({SI, InstrToDFSNum(SI)});
22772305
}
22782306
// We rely on the code below handling the MemoryAccess change.
22792307
}
@@ -2319,7 +2347,8 @@ void NewGVN::moveValueToNewCongruenceClass(Instruction *I, const Expression *E,
23192347
if (OldClass->getStoredValue())
23202348
OldClass->setStoredValue(nullptr);
23212349
}
2322-
OldClass->setLeader(getNextValueLeader(OldClass));
2350+
OldClass->setLeader({getNextValueLeader(OldClass),
2351+
InstrToDFSNum(getNextValueLeader(OldClass))});
23232352
OldClass->resetNextLeader();
23242353
markValueLeaderChangeTouched(OldClass);
23252354
}
@@ -2358,15 +2387,15 @@ void NewGVN::performCongruenceFinding(Instruction *I, const Expression *E) {
23582387

23592388
// Constants and variables should always be made the leader.
23602389
if (const auto *CE = dyn_cast<ConstantExpression>(E)) {
2361-
NewClass->setLeader(CE->getConstantValue());
2390+
NewClass->setLeader({CE->getConstantValue(), 0});
23622391
} else if (const auto *SE = dyn_cast<StoreExpression>(E)) {
23632392
StoreInst *SI = SE->getStoreInst();
2364-
NewClass->setLeader(SI);
2393+
NewClass->setLeader({SI, InstrToDFSNum(SI)});
23652394
NewClass->setStoredValue(SE->getStoredValue());
23662395
// The RepMemoryAccess field will be filled in properly by the
23672396
// moveValueToNewCongruenceClass call.
23682397
} else {
2369-
NewClass->setLeader(I);
2398+
NewClass->setLeader({I, InstrToDFSNum(I)});
23702399
}
23712400
assert(!isa<VariableExpression>(E) &&
23722401
"VariableExpression should have been handled already");
@@ -3253,7 +3282,6 @@ void NewGVN::verifyMemoryCongruency() const {
32533282
return ReachableEdges.count(
32543283
{FirstMP->getIncomingBlock(U), FirstMP->getBlock()}) &&
32553284
isa<MemoryDef>(U);
3256-
32573285
};
32583286
// All arguments should in the same class, ignoring unreachable arguments
32593287
auto FilteredPhiArgs =

llvm/test/Transforms/NewGVN/todo-pr36335-phi-undef.ll renamed to llvm/test/Transforms/NewGVN/pr36335-phi-undef.ll

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
22
; RUN: opt -passes=newgvn -S %s | FileCheck %s
33

4-
; TODO: NewGVN currently miscomiles the function below. PR36335.
5-
64
declare void @foo(i32)
75

86
define void @main(i1 %c1, i1 %c2, i32 %x) {
@@ -11,7 +9,8 @@ define void @main(i1 %c1, i1 %c2, i32 %x) {
119
; CHECK-NEXT: br i1 [[C1:%.*]], label [[L:%.*]], label [[END:%.*]]
1210
; CHECK: L:
1311
; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[X:%.*]], -1
14-
; CHECK-NEXT: call void @foo(i32 [[XOR]])
12+
; CHECK-NEXT: [[NEG:%.*]] = xor i32 [[XOR]], -1
13+
; CHECK-NEXT: call void @foo(i32 [[NEG]])
1514
; CHECK-NEXT: br label [[L]]
1615
; CHECK: end:
1716
; CHECK-NEXT: ret void

llvm/test/Transforms/NewGVN/simp-to-self.ll

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@ define void @fn1(i1 %bc) {
1212
; CHECK-NEXT: [[LV:%.*]] = load i32, ptr @a, align 4
1313
; CHECK-NEXT: [[BF_CLEAR:%.*]] = and i32 [[LV]], -131072
1414
; CHECK-NEXT: [[BF_SET:%.*]] = or i32 1, [[BF_CLEAR]]
15+
; CHECK-NEXT: [[BF_CLEAR_1:%.*]] = and i32 [[BF_SET]], -131072
16+
; CHECK-NEXT: [[BF_SET_1:%.*]] = or i32 1, [[BF_CLEAR_1]]
1517
; CHECK-NEXT: br i1 [[BC]], label [[FOR_COND]], label [[EXIT:%.*]]
1618
; CHECK: exit:
17-
; CHECK-NEXT: store i32 [[BF_SET]], ptr @a, align 4
19+
; CHECK-NEXT: store i32 [[BF_SET_1]], ptr @a, align 4
1820
; CHECK-NEXT: ret void
1921
;
2022
entry:

0 commit comments

Comments
 (0)