Skip to content

Commit 49e8593

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 564fd62 commit 49e8593

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
@@ -160,12 +160,12 @@ static bool isLeakCheckerRoot(GlobalVariable *GV) {
160160
/// Given a value that is stored to a global but never read, determine whether
161161
/// it's safe to remove the store and the chain of computation that feeds the
162162
/// store.
163-
static bool IsSafeComputationToRemove(
163+
static bool isSafeComputationToRemove(
164164
Value *V, function_ref<TargetLibraryInfo &(Function &)> GetTLI) {
165165
do {
166166
if (isa<Constant>(V))
167167
return true;
168-
if (!V->hasOneUse())
168+
if (V->hasNUsesOrMore(1))
169169
return false;
170170
if (isa<LoadInst>(V) || isa<InvokeInst>(V) || isa<Argument>(V) ||
171171
isa<GlobalValue>(V))
@@ -187,11 +187,21 @@ static bool IsSafeComputationToRemove(
187187
} while (true);
188188
}
189189

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

205215
bool Changed = false;
206216

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

247-
for (int i = 0, e = Dead.size(); i != e; ++i) {
248-
if (IsSafeComputationToRemove(Dead[i].first, GetTLI)) {
249-
Dead[i].second->eraseFromParent();
250-
Instruction *I = Dead[i].first;
251-
do {
252-
if (isAllocationFn(I, GetTLI))
253-
break;
254-
Instruction *J = dyn_cast<Instruction>(I->getOperand(0));
255-
if (!J)
256-
break;
257-
I->eraseFromParent();
258-
I = J;
259-
} while (true);
260-
I->eraseFromParent();
240+
// Finally, erase all writes and remove computation chains if they are safe
241+
// to remove.
242+
for (auto [WriteInst, V] : Writes) {
243+
if (isa<Constant>(V) || isa<Instruction>(V))
244+
WriteInst->eraseFromParent();
245+
246+
if (auto *Inst = dyn_cast<Instruction>(V)) {
247+
if (isSafeComputationToRemove(V, GetTLI))
248+
RecursivelyDeleteTriviallyDeadInstructions(V);
261249
Changed = true;
262250
}
263251
}
@@ -872,7 +860,7 @@ static bool OptimizeAwayTrappingUsesOfLoads(
872860
// nor is the global.
873861
if (AllNonStoreUsesGone) {
874862
if (isLeakCheckerRoot(GV)) {
875-
Changed |= CleanupPointerRootUsers(GV, GetTLI);
863+
Changed |= cleanupPointerRootUsers(GV, GetTLI);
876864
} else {
877865
Changed = true;
878866
CleanupConstantGlobalUsers(GV, DL);
@@ -1494,7 +1482,7 @@ processInternalGlobal(GlobalVariable *GV, const GlobalStatus &GS,
14941482

14951483
if (isLeakCheckerRoot(GV)) {
14961484
// Delete any constant stores to the global.
1497-
Changed = CleanupPointerRootUsers(GV, GetTLI);
1485+
Changed = cleanupPointerRootUsers(GV, GetTLI);
14981486
} else {
14991487
// Delete any stores we can find to the global. We may not be able to
15001488
// 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)