Skip to content

Commit 67a0a21

Browse files
committed
[GlobalOpt] Eliminate redundant cleanup functions
Remove CleanupPointerRootUsers and replace it with an extended version of CleanupConstantGlobalUsers. Fixes #64680. Change-Id: I4c3fc52890eec1a3706cba9ecb916fd344672756
1 parent 0d5c072 commit 67a0a21

File tree

4 files changed

+43
-111
lines changed

4 files changed

+43
-111
lines changed

llvm/lib/Transforms/IPO/GlobalOpt.cpp

Lines changed: 33 additions & 105 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,90 +187,12 @@ 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.
193-
static bool
194-
CleanupPointerRootUsers(GlobalVariable *GV,
195-
function_ref<TargetLibraryInfo &(Function &)> GetTLI) {
196-
// A brief explanation of leak checkers. The goal is to find bugs where
197-
// pointers are forgotten, causing an accumulating growth in memory
198-
// usage over time. The common strategy for leak checkers is to explicitly
199-
// allow the memory pointed to by globals at exit. This is popular because it
200-
// also solves another problem where the main thread of a C++ program may shut
201-
// down before other threads that are still expecting to use those globals. To
202-
// handle that case, we expect the program may create a singleton and never
203-
// destroy it.
204-
205-
bool Changed = false;
206-
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-
211-
SmallVector<User *> Worklist(GV->users());
212-
// Constants can't be pointers to dynamically allocated memory.
213-
while (!Worklist.empty()) {
214-
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-
}
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));
231-
}
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));
240-
}
241-
} else if (ConstantExpr *CE = dyn_cast<ConstantExpr>(U)) {
242-
if (isa<GEPOperator>(CE))
243-
append_range(Worklist, CE->users());
244-
}
245-
}
246-
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();
261-
Changed = true;
262-
}
263-
}
264-
265-
GV->removeDeadConstantUsers();
266-
return Changed;
267-
}
268-
269190
/// We just marked GV constant. Loop over all users of the global, cleaning up
270191
/// the obvious ones. This is largely just a quick scan over the use list to
271192
/// clean up the easy and obvious cruft. This returns true if it made a change.
272-
static bool CleanupConstantGlobalUsers(GlobalVariable *GV,
273-
const DataLayout &DL) {
193+
static bool CleanupConstantGlobalUsers(
194+
GlobalVariable *GV, const DataLayout &DL,
195+
function_ref<TargetLibraryInfo &(Function &)> GetTLI) {
274196
Constant *Init = GV->getInitializer();
275197
SmallVector<User *, 8> WorkList(GV->users());
276198
SmallPtrSet<User *, 8> Visited;
@@ -320,11 +242,30 @@ static bool CleanupConstantGlobalUsers(GlobalVariable *GV,
320242
}
321243
}
322244
} else if (StoreInst *SI = dyn_cast<StoreInst>(U)) {
323-
// Store must be unreachable or storing Init into the global.
324-
EraseFromParent(SI);
325-
} else if (MemIntrinsic *MI = dyn_cast<MemIntrinsic>(U)) { // memset/cpy/mv
326-
if (getUnderlyingObject(MI->getRawDest()) == GV)
327-
EraseFromParent(MI);
245+
auto *V = SI->getValueOperand();
246+
if (isa<Constant>(V)) {
247+
EraseFromParent(SI);
248+
} else if (isa<Instruction>(V)) {
249+
EraseFromParent(SI);
250+
if (isSafeComputationToRemove(V, GetTLI))
251+
RecursivelyDeleteTriviallyDeadInstructions(V);
252+
} else if (isa<Argument>(V)) {
253+
if (!V->getType()->isPointerTy())
254+
EraseFromParent(SI);
255+
}
256+
} else if (auto *MSI = dyn_cast<MemSetInst>(U)) { // memset/cpy/mv
257+
if (getUnderlyingObject(MSI->getRawDest()) == GV)
258+
EraseFromParent(MSI);
259+
} else if (auto *MTI = dyn_cast<MemTransferInst>(U)) {
260+
auto *Src = MTI->getRawSource();
261+
auto *Dst = MTI->getRawDest();
262+
if (getUnderlyingObject(Dst) != GV)
263+
continue;
264+
if (isa<Instruction, Operator>(Src)) {
265+
EraseFromParent(MTI);
266+
if (isSafeComputationToRemove(Src, GetTLI))
267+
RecursivelyDeleteTriviallyDeadInstructions(Src);
268+
}
328269
} else if (IntrinsicInst *II = dyn_cast<IntrinsicInst>(U)) {
329270
if (II->getIntrinsicID() == Intrinsic::threadlocal_address)
330271
append_range(WorkList, II->users());
@@ -872,12 +813,7 @@ static bool OptimizeAwayTrappingUsesOfLoads(
872813
// If we nuked all of the loads, then none of the stores are needed either,
873814
// nor is the global.
874815
if (AllNonStoreUsesGone) {
875-
if (isLeakCheckerRoot(GV)) {
876-
Changed |= CleanupPointerRootUsers(GV, GetTLI);
877-
} else {
878-
Changed = true;
879-
CleanupConstantGlobalUsers(GV, DL);
880-
}
816+
Changed |= CleanupConstantGlobalUsers(GV, DL, GetTLI);
881817
if (GV->use_empty()) {
882818
LLVM_DEBUG(dbgs() << " *** GLOBAL NOW DEAD!\n");
883819
Changed = true;
@@ -1491,15 +1427,7 @@ processInternalGlobal(GlobalVariable *GV, const GlobalStatus &GS,
14911427
// Delete it now.
14921428
if (!GS.IsLoaded) {
14931429
LLVM_DEBUG(dbgs() << "GLOBAL NEVER LOADED: " << *GV << "\n");
1494-
1495-
if (isLeakCheckerRoot(GV)) {
1496-
// Delete any constant stores to the global.
1497-
Changed = CleanupPointerRootUsers(GV, GetTLI);
1498-
} else {
1499-
// Delete any stores we can find to the global. We may not be able to
1500-
// make it completely dead though.
1501-
Changed = CleanupConstantGlobalUsers(GV, DL);
1502-
}
1430+
Changed = CleanupConstantGlobalUsers(GV, DL, GetTLI);
15031431

15041432
// If the global is dead now, delete it.
15051433
if (GV->use_empty()) {
@@ -1523,7 +1451,7 @@ processInternalGlobal(GlobalVariable *GV, const GlobalStatus &GS,
15231451
}
15241452

15251453
// Clean up any obviously simplifiable users now.
1526-
Changed |= CleanupConstantGlobalUsers(GV, DL);
1454+
Changed |= CleanupConstantGlobalUsers(GV, DL, GetTLI);
15271455

15281456
// If the global is dead now, just nuke it.
15291457
if (GV->use_empty()) {
@@ -1578,7 +1506,7 @@ processInternalGlobal(GlobalVariable *GV, const GlobalStatus &GS,
15781506
}
15791507

15801508
// Clean up any obviously simplifiable users now.
1581-
CleanupConstantGlobalUsers(GV, DL);
1509+
CleanupConstantGlobalUsers(GV, DL, GetTLI);
15821510

15831511
if (GV->use_empty()) {
15841512
LLVM_DEBUG(dbgs() << " *** Substituting initializer allowed us to "

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: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,24 @@
77
declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg)
88

99
;.
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
10+
; CHECK: @b = internal unnamed_addr global ptr null
1211
;.
1312
define void @test() {
1413
; 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)
1614
; CHECK-NEXT: ret void
1715
;
1816
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)
1917
ret void
2018
}
19+
20+
define void @neg_test(ptr %arg) {
21+
; CHECK-LABEL: @neg_test(
22+
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr @b, ptr [[ARG:%.*]], i64 8, i1 false)
23+
; CHECK-NEXT: ret void
24+
;
25+
call void @llvm.memcpy.p0.p0.i64(ptr @b, ptr %arg, i64 8, i1 false)
26+
ret void
27+
}
2128
;.
2229
; CHECK: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
2330
;.

0 commit comments

Comments
 (0)