Skip to content

Commit 5b0dba1

Browse files
authored
[NewGVN] Fix caching for OpIsSafeForPhiOfOps (#98340)
The caching mechanism for 'OpIsSafeForPhiOfOps' is unsound. An operand is deemed unsafe for PhiOfOps if it depends on a phi that resides in the same block as the Phi block, i.e., where we are performing the PhiOfOps. This is to avoid having to materialize the translated subexpressions. To avoid redundant code walking, a cache is used to store these results. Note, however, that since the safety is specific to the Phi block, we cannot, in general, use the cached results for other blocks. This patch addresses this by having a cache per block instead of a single one for the entire function. closes #63335
1 parent 70f57d2 commit 5b0dba1

File tree

2 files changed

+116
-7
lines changed

2 files changed

+116
-7
lines changed

llvm/lib/Transforms/Scalar/NewGVN.cpp

+16-7
Original file line numberDiff line numberDiff line change
@@ -529,7 +529,11 @@ class NewGVN {
529529
// IR.
530530
SmallPtrSet<const Instruction *, 8> PHINodeUses;
531531

532-
DenseMap<const Value *, bool> OpSafeForPHIOfOps;
532+
// The cached results, in general, are only valid for the specific block where
533+
// they were computed. The unsigned part of the key is a unique block
534+
// identifier
535+
DenseMap<std::pair<const Value *, unsigned>, bool> OpSafeForPHIOfOps;
536+
unsigned CacheIdx;
533537

534538
// Map a temporary instruction we created to a parent block.
535539
DenseMap<const Value *, BasicBlock *> TempToBlock;
@@ -2596,19 +2600,19 @@ bool NewGVN::OpIsSafeForPHIOfOps(Value *V, const BasicBlock *PHIBlock,
25962600
if (!isa<Instruction>(I))
25972601
continue;
25982602

2599-
auto OISIt = OpSafeForPHIOfOps.find(I);
2603+
auto OISIt = OpSafeForPHIOfOps.find({I, CacheIdx});
26002604
if (OISIt != OpSafeForPHIOfOps.end())
26012605
return OISIt->second;
26022606

26032607
// Keep walking until we either dominate the phi block, or hit a phi, or run
26042608
// out of things to check.
26052609
if (DT->properlyDominates(getBlockForValue(I), PHIBlock)) {
2606-
OpSafeForPHIOfOps.insert({I, true});
2610+
OpSafeForPHIOfOps.insert({{I, CacheIdx}, true});
26072611
continue;
26082612
}
26092613
// PHI in the same block.
26102614
if (isa<PHINode>(I) && getBlockForValue(I) == PHIBlock) {
2611-
OpSafeForPHIOfOps.insert({I, false});
2615+
OpSafeForPHIOfOps.insert({{I, CacheIdx}, false});
26122616
return false;
26132617
}
26142618

@@ -2627,10 +2631,10 @@ bool NewGVN::OpIsSafeForPHIOfOps(Value *V, const BasicBlock *PHIBlock,
26272631
if (!isa<Instruction>(Op))
26282632
continue;
26292633
// Stop now if we find an unsafe operand.
2630-
auto OISIt = OpSafeForPHIOfOps.find(OrigI);
2634+
auto OISIt = OpSafeForPHIOfOps.find({OrigI, CacheIdx});
26312635
if (OISIt != OpSafeForPHIOfOps.end()) {
26322636
if (!OISIt->second) {
2633-
OpSafeForPHIOfOps.insert({I, false});
2637+
OpSafeForPHIOfOps.insert({{I, CacheIdx}, false});
26342638
return false;
26352639
}
26362640
continue;
@@ -2640,7 +2644,7 @@ bool NewGVN::OpIsSafeForPHIOfOps(Value *V, const BasicBlock *PHIBlock,
26402644
Worklist.push_back(cast<Instruction>(Op));
26412645
}
26422646
}
2643-
OpSafeForPHIOfOps.insert({V, true});
2647+
OpSafeForPHIOfOps.insert({{V, CacheIdx}, true});
26442648
return true;
26452649
}
26462650

@@ -3293,6 +3297,7 @@ void NewGVN::verifyIterationSettled(Function &F) {
32933297
TouchedInstructions.set();
32943298
TouchedInstructions.reset(0);
32953299
OpSafeForPHIOfOps.clear();
3300+
CacheIdx = 0;
32963301
iterateTouchedInstructions();
32973302
DenseSet<std::pair<const CongruenceClass *, const CongruenceClass *>>
32983303
EqualClasses;
@@ -3396,6 +3401,8 @@ void NewGVN::iterateTouchedInstructions() {
33963401
<< " because it is unreachable\n");
33973402
continue;
33983403
}
3404+
// Use the appropriate cache for "OpIsSafeForPHIOfOps".
3405+
CacheIdx = RPOOrdering.lookup(DT->getNode(CurrBlock)) - 1;
33993406
updateProcessedCount(CurrBlock);
34003407
}
34013408
// Reset after processing (because we may mark ourselves as touched when
@@ -3475,6 +3482,8 @@ bool NewGVN::runGVN() {
34753482
LLVM_DEBUG(dbgs() << "Block " << getBlockName(&F.getEntryBlock())
34763483
<< " marked reachable\n");
34773484
ReachableBlocks.insert(&F.getEntryBlock());
3485+
// Use index corresponding to entry block.
3486+
CacheIdx = 0;
34783487

34793488
iterateTouchedInstructions();
34803489
verifyMemoryCongruency();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
2+
; RUN: opt -S -passes=newgvn < %s | FileCheck %s
3+
4+
define i32 @unsafe(i1 %arg, i32 %arg1) {
5+
; CHECK-LABEL: define i32 @unsafe(
6+
; CHECK-SAME: i1 [[ARG:%.*]], i32 [[ARG1:%.*]]) {
7+
; CHECK-NEXT: [[BB:.*:]]
8+
; CHECK-NEXT: br label %[[BB4:.*]]
9+
; CHECK: [[BB4]]:
10+
; CHECK-NEXT: br i1 [[ARG]], label %[[BB6:.*]], label %[[BB5:.*]]
11+
; CHECK: [[BB5]]:
12+
; CHECK-NEXT: br label %[[BB6]]
13+
; CHECK: [[BB6]]:
14+
; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ [[ARG1]], %[[BB5]] ], [ 0, %[[BB4]] ]
15+
; CHECK-NEXT: [[SREM:%.*]] = srem i32 [[ARG1]], [[PHI]]
16+
; CHECK-NEXT: store i32 [[SREM]], ptr null, align 4
17+
; CHECK-NEXT: br i1 [[ARG]], label %[[BB8:.*]], label %[[BB7:.*]]
18+
; CHECK: [[BB7]]:
19+
; CHECK-NEXT: br i1 true, label %[[BB8]], label %[[BB5]]
20+
; CHECK: [[BB8]]:
21+
; CHECK-NEXT: [[PHIOFOPS:%.*]] = phi i32 [ [[ARG1]], %[[BB6]] ], [ 0, %[[BB7]] ]
22+
; CHECK-NEXT: [[PHI9:%.*]] = phi i32 [ 0, %[[BB7]] ], [ 1, %[[BB6]] ]
23+
; CHECK-NEXT: store i32 [[PHIOFOPS]], ptr null, align 4
24+
; CHECK-NEXT: br label %[[BB4]]
25+
;
26+
bb:
27+
br label %bb4
28+
29+
bb4: ; preds = %bb8, %bb
30+
br i1 %arg, label %bb6, label %bb5
31+
32+
bb5: ; preds = %bb7, %bb4
33+
br label %bb6
34+
35+
bb6: ; preds = %bb5, %bb4
36+
%phi = phi i32 [ %arg1, %bb5 ], [ 0, %bb4 ]
37+
%or = or i32 %phi, %arg1
38+
%srem = srem i32 %or, %phi
39+
store i32 %srem, ptr null, align 4
40+
br i1 %arg, label %bb8, label %bb7
41+
42+
bb7: ; preds = %bb6
43+
br i1 true, label %bb8, label %bb5
44+
45+
bb8: ; preds = %bb7, %bb6
46+
%phi9 = phi i32 [ 0, %bb7 ], [ 1, %bb6 ]
47+
%mul = mul i32 %phi9, %or
48+
store i32 %mul, ptr null, align 4
49+
br label %bb4
50+
}
51+
52+
define i32 @unsafe_load(i1 %arg) {
53+
; CHECK-LABEL: define i32 @unsafe_load(
54+
; CHECK-SAME: i1 [[ARG:%.*]]) {
55+
; CHECK-NEXT: [[BB:.*:]]
56+
; CHECK-NEXT: br label %[[BB1:.*]]
57+
; CHECK: [[BB1]]:
58+
; CHECK-NEXT: br i1 [[ARG]], label %[[BB3:.*]], label %[[BB2:.*]]
59+
; CHECK: [[BB2]]:
60+
; CHECK-NEXT: br label %[[BB3]]
61+
; CHECK: [[BB3]]:
62+
; CHECK-NEXT: [[PHI4:%.*]] = phi i32 [ 1, %[[BB2]] ], [ 0, %[[BB1]] ]
63+
; CHECK-NEXT: [[LOAD:%.*]] = load i32, ptr null, align 4
64+
; CHECK-NEXT: [[SREM:%.*]] = srem i32 [[LOAD]], [[PHI4]]
65+
; CHECK-NEXT: store i32 [[SREM]], ptr null, align 4
66+
; CHECK-NEXT: br i1 [[ARG]], label %[[BB6:.*]], label %[[BB5:.*]]
67+
; CHECK: [[BB5]]:
68+
; CHECK-NEXT: br i1 true, label %[[BB6]], label %[[BB2]]
69+
; CHECK: [[BB6]]:
70+
; CHECK-NEXT: [[PHIOFOPS:%.*]] = phi i32 [ [[LOAD]], %[[BB3]] ], [ 0, %[[BB5]] ]
71+
; CHECK-NEXT: [[PHI7:%.*]] = phi i32 [ 0, %[[BB5]] ], [ 1, %[[BB3]] ]
72+
; CHECK-NEXT: store i32 [[PHIOFOPS]], ptr null, align 4
73+
; CHECK-NEXT: br label %[[BB1]]
74+
;
75+
bb:
76+
br label %bb1
77+
78+
bb1: ; preds = %bb6, %bb
79+
br i1 %arg, label %bb3, label %bb2
80+
81+
bb2: ; preds = %bb5, %bb1
82+
%phi = phi i32 [ 1, %bb1 ], [ 0, %bb5 ]
83+
br label %bb3
84+
85+
bb3: ; preds = %bb2, %bb1
86+
%phi4 = phi i32 [ %phi, %bb2 ], [ 0, %bb1 ]
87+
%load = load i32, ptr null, align 4
88+
%srem = srem i32 %load, %phi4
89+
store i32 %srem, ptr null, align 4
90+
br i1 %arg, label %bb6, label %bb5
91+
92+
bb5: ; preds = %bb3
93+
br i1 true, label %bb6, label %bb2
94+
95+
bb6: ; preds = %bb5, %bb3
96+
%phi7 = phi i32 [ 0, %bb5 ], [ 1, %bb3 ]
97+
%mul = mul i32 %phi7, %load
98+
store i32 %mul, ptr null, align 4
99+
br label %bb1
100+
}

0 commit comments

Comments
 (0)