Skip to content

Commit 7e9338b

Browse files
committed
[GlobalOpt] Handle operators separately when removing GV users
This patch fixes the functionality of CleanupPointerRootUsers by separately handling cases where the global variable is wrapped within an operator. This patch additionally fixes some function names to conform with the LLVM style guide. Fixes #64680. Change-Id: I972dc76758ced16cefcb08474b756ec89e0c047f
1 parent 1d5d189 commit 7e9338b

File tree

4 files changed

+41
-64
lines changed

4 files changed

+41
-64
lines changed

llvm/lib/Transforms/IPO/GlobalOpt.cpp

Lines changed: 41 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -158,12 +158,12 @@ static bool isLeakCheckerRoot(GlobalVariable *GV) {
158158
/// Given a value that is stored to a global but never read, determine whether
159159
/// it's safe to remove the store and the chain of computation that feeds the
160160
/// store.
161-
static bool IsSafeComputationToRemove(
161+
static bool isSafeComputationToRemove(
162162
Value *V, function_ref<TargetLibraryInfo &(Function &)> GetTLI) {
163163
do {
164164
if (isa<Constant>(V))
165165
return true;
166-
if (!V->hasOneUse())
166+
if (V->hasNUsesOrMore(1))
167167
return false;
168168
if (isa<LoadInst>(V) || isa<InvokeInst>(V) || isa<Argument>(V) ||
169169
isa<GlobalValue>(V))
@@ -185,11 +185,21 @@ static bool IsSafeComputationToRemove(
185185
} while (true);
186186
}
187187

188-
/// This GV is a pointer root. Loop over all users of the global and clean up
189-
/// any that obviously don't assign the global a value that isn't dynamically
190-
/// allocated.
188+
/**
189+
* Cleans up pointer root users of a global variable.
190+
*
191+
* This function iterates over all users of the global variable and collects
192+
* all stores and memtransfer instructions. It then erases all writes and
193+
* removes computation chains if they are safe to remove. Finally, it removes
194+
* dead constant users of the global variable.
195+
*
196+
* @param GV The global variable to clean up.
197+
* @param GetTLI A function reference to obtain the TargetLibraryInfo for a
198+
* given function.
199+
* @return True if any changes were made, false otherwise.
200+
*/
191201
static bool
192-
CleanupPointerRootUsers(GlobalVariable *GV,
202+
cleanupPointerRootUsers(GlobalVariable *GV,
193203
function_ref<TargetLibraryInfo &(Function &)> GetTLI) {
194204
// A brief explanation of leak checkers. The goal is to find bugs where
195205
// pointers are forgotten, causing an accumulating growth in memory
@@ -202,60 +212,38 @@ CleanupPointerRootUsers(GlobalVariable *GV,
202212

203213
bool Changed = false;
204214

205-
// If Dead[n].first is the only use of a malloc result, we can delete its
206-
// chain of computation and the store to the global in Dead[n].second.
207-
SmallVector<std::pair<Instruction *, Instruction *>, 32> Dead;
208-
215+
// Iterate over all users of the global and collect all
216+
// stores, memtransfer and memset instructions.
217+
SmallVector<std::pair<Instruction *, Value *>> Writes;
209218
SmallVector<User *> Worklist(GV->users());
210-
// Constants can't be pointers to dynamically allocated memory.
211219
while (!Worklist.empty()) {
212220
User *U = Worklist.pop_back_val();
213-
if (StoreInst *SI = dyn_cast<StoreInst>(U)) {
214-
Value *V = SI->getValueOperand();
215-
if (isa<Constant>(V)) {
216-
Changed = true;
217-
SI->eraseFromParent();
218-
} else if (Instruction *I = dyn_cast<Instruction>(V)) {
219-
if (I->hasOneUse())
220-
Dead.push_back(std::make_pair(I, SI));
221+
if (auto *SI = dyn_cast<StoreInst>(U)) {
222+
Writes.push_back({SI, SI->getValueOperand()});
223+
} else if (auto *CE = dyn_cast<ConstantExpr>(U)) {
224+
if (isa<GEPOperator>(CE)) {
225+
append_range(Worklist, CE->users());
221226
}
222-
} else if (MemSetInst *MSI = dyn_cast<MemSetInst>(U)) {
223-
if (isa<Constant>(MSI->getValue())) {
224-
Changed = true;
225-
MSI->eraseFromParent();
226-
} else if (Instruction *I = dyn_cast<Instruction>(MSI->getValue())) {
227-
if (I->hasOneUse())
228-
Dead.push_back(std::make_pair(I, MSI));
227+
} else if (auto *MTI = dyn_cast<MemTransferInst>(U)) {
228+
if (MTI->getRawDest() == GV) {
229+
Writes.push_back({MTI, MTI->getSource()});
229230
}
230-
} else if (MemTransferInst *MTI = dyn_cast<MemTransferInst>(U)) {
231-
GlobalVariable *MemSrc = dyn_cast<GlobalVariable>(MTI->getSource());
232-
if (MemSrc && MemSrc->isConstant()) {
233-
Changed = true;
234-
MTI->eraseFromParent();
235-
} else if (Instruction *I = dyn_cast<Instruction>(MTI->getSource())) {
236-
if (I->hasOneUse())
237-
Dead.push_back(std::make_pair(I, MTI));
231+
} else if (auto *MSI = dyn_cast<MemSetInst>(U)) {
232+
if (MSI->getRawDest() == GV) {
233+
Writes.push_back({MSI, MSI->getValue()});
238234
}
239-
} else if (ConstantExpr *CE = dyn_cast<ConstantExpr>(U)) {
240-
if (isa<GEPOperator>(CE))
241-
append_range(Worklist, CE->users());
242235
}
243236
}
244237

245-
for (int i = 0, e = Dead.size(); i != e; ++i) {
246-
if (IsSafeComputationToRemove(Dead[i].first, GetTLI)) {
247-
Dead[i].second->eraseFromParent();
248-
Instruction *I = Dead[i].first;
249-
do {
250-
if (isAllocationFn(I, GetTLI))
251-
break;
252-
Instruction *J = dyn_cast<Instruction>(I->getOperand(0));
253-
if (!J)
254-
break;
255-
I->eraseFromParent();
256-
I = J;
257-
} while (true);
258-
I->eraseFromParent();
238+
// Finally, erase all writes and remove computation chains if they are safe
239+
// to remove.
240+
for (auto [WriteInst, V] : Writes) {
241+
if (isa<Constant>(V) || isa<Instruction>(V))
242+
WriteInst->eraseFromParent();
243+
244+
if (auto *Inst = dyn_cast<Instruction>(V)) {
245+
if (isSafeComputationToRemove(V, GetTLI))
246+
RecursivelyDeleteTriviallyDeadInstructions(V);
259247
Changed = true;
260248
}
261249
}
@@ -870,7 +858,7 @@ static bool OptimizeAwayTrappingUsesOfLoads(
870858
// nor is the global.
871859
if (AllNonStoreUsesGone) {
872860
if (isLeakCheckerRoot(GV)) {
873-
Changed |= CleanupPointerRootUsers(GV, GetTLI);
861+
Changed |= cleanupPointerRootUsers(GV, GetTLI);
874862
} else {
875863
Changed = true;
876864
CleanupConstantGlobalUsers(GV, DL);
@@ -1496,7 +1484,7 @@ processInternalGlobal(GlobalVariable *GV, const GlobalStatus &GS,
14961484

14971485
if (isLeakCheckerRoot(GV)) {
14981486
// Delete any constant stores to the global.
1499-
Changed = CleanupPointerRootUsers(GV, GetTLI);
1487+
Changed = cleanupPointerRootUsers(GV, GetTLI);
15001488
} else {
15011489
// Delete any stores we can find to the global. We may not be able to
15021490
// make it completely dead though.

llvm/test/Transforms/GlobalOpt/cleanup-pointer-root-users-gep-constexpr.ll

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,6 @@ define i32 @load_gv_from_op_remove_store(ptr %p) local_unnamed_addr {
9999
; CHECK-NEXT: call void @fn0()
100100
; CHECK-NEXT: br label [[IF_END]]
101101
; CHECK: if.end:
102-
; CHECK-NEXT: store ptr [[E]], ptr getelementptr inbounds ([3 x ptr], ptr @b, i64 0, i64 2), align 16
103102
; CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr @a, align 4
104103
; CHECK-NEXT: [[INC:%.*]] = add nsw i32 [[TMP1]], 1
105104
; CHECK-NEXT: store i32 [[INC]], ptr @a, align 4

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

llvm/test/Transforms/GlobalOpt/pr54572.ll

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,10 @@
66

77
declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg)
88

9-
;.
10-
; CHECK: @[[B:[a-zA-Z0-9_$"\\.-]+]] = internal unnamed_addr global ptr null
11-
; CHECK: @[[C:[a-zA-Z0-9_$"\\.-]+]] = internal unnamed_addr constant [2 x ptr] zeroinitializer
12-
;.
139
define void @test() {
1410
; CHECK-LABEL: @test(
15-
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr @b, ptr getelementptr inbounds ([2 x ptr], ptr @c, i64 0, i64 1), i64 8, i1 false)
1611
; CHECK-NEXT: ret void
1712
;
1813
call void @llvm.memcpy.p0.p0.i64(ptr @b, ptr getelementptr inbounds ([2 x ptr], ptr @c, i64 0, i64 1), i64 8, i1 false)
1914
ret void
2015
}
21-
;.
22-
; CHECK: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
23-
;.

0 commit comments

Comments
 (0)