Skip to content

Commit b71057a

Browse files
committed
[GlobalOpt] Refactor CleanupPointerRootUsers
Currently the algorithm combines removal of stores and store operands to GV in a single loop. This patch refines the current algorithm to account for certain edge cases. Fixes #64680.
1 parent a53401e commit b71057a

File tree

3 files changed

+97
-47
lines changed

3 files changed

+97
-47
lines changed

llvm/lib/Transforms/IPO/GlobalOpt.cpp

Lines changed: 35 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -201,61 +201,51 @@ CleanupPointerRootUsers(GlobalVariable *GV,
201201

202202
bool Changed = false;
203203

204-
// If Dead[n].first is the only use of a malloc result, we can delete its
205-
// chain of computation and the store to the global in Dead[n].second.
206-
SmallVector<std::pair<Instruction *, Instruction *>, 32> Dead;
207-
204+
// Collect all stores to GV.
205+
SmallVector<Instruction *, 8> Writes;
208206
SmallVector<User *> Worklist(GV->users());
209-
// Constants can't be pointers to dynamically allocated memory.
210207
while (!Worklist.empty()) {
211208
User *U = Worklist.pop_back_val();
212-
if (StoreInst *SI = dyn_cast<StoreInst>(U)) {
213-
Value *V = SI->getValueOperand();
214-
if (isa<Constant>(V)) {
215-
Changed = true;
216-
SI->eraseFromParent();
217-
} else if (Instruction *I = dyn_cast<Instruction>(V)) {
218-
if (I->hasOneUse())
219-
Dead.push_back(std::make_pair(I, SI));
220-
}
221-
} else if (MemSetInst *MSI = dyn_cast<MemSetInst>(U)) {
222-
if (isa<Constant>(MSI->getValue())) {
223-
Changed = true;
224-
MSI->eraseFromParent();
225-
} else if (Instruction *I = dyn_cast<Instruction>(MSI->getValue())) {
226-
if (I->hasOneUse())
227-
Dead.push_back(std::make_pair(I, MSI));
228-
}
229-
} else if (MemTransferInst *MTI = dyn_cast<MemTransferInst>(U)) {
230-
GlobalVariable *MemSrc = dyn_cast<GlobalVariable>(MTI->getSource());
231-
if (MemSrc && MemSrc->isConstant()) {
232-
Changed = true;
233-
MTI->eraseFromParent();
234-
} else if (Instruction *I = dyn_cast<Instruction>(MTI->getSource())) {
235-
if (I->hasOneUse())
236-
Dead.push_back(std::make_pair(I, MTI));
237-
}
238-
} else if (ConstantExpr *CE = dyn_cast<ConstantExpr>(U)) {
209+
if (auto *SI = dyn_cast<StoreInst>(U)) {
210+
Writes.push_back(SI);
211+
} else if (auto *CE = dyn_cast<ConstantExpr>(U)) {
239212
if (isa<GEPOperator>(CE))
240213
append_range(Worklist, CE->users());
241214
}
242215
}
243216

244-
for (int i = 0, e = Dead.size(); i != e; ++i) {
245-
if (IsSafeComputationToRemove(Dead[i].first, GetTLI)) {
246-
Dead[i].second->eraseFromParent();
247-
Instruction *I = Dead[i].first;
248-
do {
249-
if (isAllocationFn(I, GetTLI))
250-
break;
251-
Instruction *J = dyn_cast<Instruction>(I->getOperand(0));
252-
if (!J)
253-
break;
254-
I->eraseFromParent();
255-
I = J;
256-
} while (true);
217+
auto RemoveComputationChain = [&GetTLI](Instruction *I) {
218+
do {
219+
if (isAllocationFn(I, GetTLI))
220+
break;
221+
Instruction *J = dyn_cast<Instruction>(I->getOperand(0));
222+
if (!J)
223+
break;
257224
I->eraseFromParent();
225+
I = J;
226+
} while (true);
227+
I->eraseFromParent();
228+
};
229+
230+
// Remove the store if it's value operand is a constant or a computation
231+
// chain. If the value operand is a computation chain, determine if it is safe
232+
// to remove it before removing the store. Remove both the store and the
233+
// computation chain if it is safe to do so.
234+
while (!Writes.empty()) {
235+
auto *Write = Writes.pop_back_val();
236+
auto *V = Write->getOperand(0);
237+
if (isa<Constant>(V)) {
258238
Changed = true;
239+
Write->eraseFromParent();
240+
} else if (isa<Instruction>(V)) {
241+
if (IsSafeComputationToRemove(V, GetTLI)) {
242+
Changed = true;
243+
Write->eraseFromParent();
244+
RemoveComputationChain(cast<Instruction>(V));
245+
} else {
246+
Changed = true;
247+
Write->eraseFromParent();
248+
}
259249
}
260250
}
261251

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
2+
; RUN: opt -passes=globalopt < %s -S | FileCheck %s
3+
4+
@a = internal unnamed_addr global i32 0, align 4
5+
@b = internal unnamed_addr global [3 x ptr] zeroinitializer, align 16
6+
7+
define i32 @main() local_unnamed_addr {
8+
; CHECK-LABEL: define i32 @main() local_unnamed_addr {
9+
; CHECK-NEXT: entry:
10+
; CHECK-NEXT: [[E:%.*]] = alloca i32, align 4
11+
; CHECK-NEXT: [[DOTPR:%.*]] = load i32, ptr @a, align 4
12+
; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i32 [[DOTPR]], 3
13+
; CHECK-NEXT: br i1 [[CMP1]], label [[FOR_BODY:%.*]], label [[FOR_END:%.*]]
14+
; CHECK: for.body:
15+
; CHECK-NEXT: store i32 8, ptr [[E]], align 4
16+
; CHECK-NEXT: call void @bar20_()
17+
; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[E]], align 4
18+
; CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq i32 [[TMP0]], 0
19+
; CHECK-NEXT: br i1 [[TOBOOL_NOT]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
20+
; CHECK: if.then:
21+
; CHECK-NEXT: call void @foo()
22+
; CHECK-NEXT: br label [[IF_END]]
23+
; CHECK: if.end:
24+
; CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr @a, align 4
25+
; CHECK-NEXT: [[INC:%.*]] = add nsw i32 [[TMP1]], 1
26+
; CHECK-NEXT: store i32 [[INC]], ptr @a, align 4
27+
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[TMP1]], 2
28+
; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY]], label [[FOR_END]]
29+
; CHECK: for.end:
30+
; CHECK-NEXT: ret i32 0
31+
;
32+
entry:
33+
%e = alloca i32, align 4
34+
%.pr = load i32, ptr @a, align 4
35+
%cmp1 = icmp slt i32 %.pr, 3
36+
br i1 %cmp1, label %for.body, label %for.end
37+
38+
for.body: ; preds = %entry, %if.end
39+
store i32 8, ptr %e, align 4
40+
call void @bar20_()
41+
%0 = load i32, ptr %e, align 4
42+
%tobool.not = icmp eq i32 %0, 0
43+
br i1 %tobool.not, label %if.then, label %if.end
44+
45+
if.then: ; preds = %for.body
46+
call void @foo()
47+
br label %if.end
48+
49+
if.end: ; preds = %if.then, %for.body
50+
store ptr %e, ptr getelementptr inbounds ([3 x ptr], ptr @b, i64 0, i64 2), align 16
51+
%1 = load i32, ptr @a, align 4
52+
%inc = add nsw i32 %1, 1
53+
store i32 %inc, ptr @a, align 4
54+
%cmp = icmp slt i32 %1, 2
55+
br i1 %cmp, label %for.body, label %for.end
56+
57+
for.end: ; preds = %if.end, %entry
58+
ret i32 0
59+
}
60+
61+
declare void @bar20_() local_unnamed_addr
62+
declare void @foo() local_unnamed_addr

llvm/test/Transforms/GlobalOpt/dead-store-status.ll

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
; false. This was caught by the pass return status check that is hidden under
55
; EXPENSIVE_CHECKS.
66

7-
; CHECK: @global = internal unnamed_addr global ptr null, align 1
8-
97
; CHECK-LABEL: @foo
108
; CHECK-NEXT: entry:
119
; CHECK-NEXT: ret i16 undef

0 commit comments

Comments
 (0)