Skip to content

Commit 51dad71

Browse files
authored
[GlobalOpt] Handle operators separately when removing GV users (#84694)
Refactor globalopt by eliminating redundant code. Fix #64680.
1 parent dfb6c76 commit 51dad71

File tree

4 files changed

+42
-159
lines changed

4 files changed

+42
-159
lines changed

Diff for: llvm/lib/Transforms/IPO/GlobalOpt.cpp

+32-153
Original file line numberDiff line numberDiff line change
@@ -114,55 +114,6 @@ static cl::opt<int> ColdCCRelFreq(
114114
"entry frequency, for a call site to be considered cold for enabling "
115115
"coldcc"));
116116

117-
/// Is this global variable possibly used by a leak checker as a root? If so,
118-
/// we might not really want to eliminate the stores to it.
119-
static bool isLeakCheckerRoot(GlobalVariable *GV) {
120-
// A global variable is a root if it is a pointer, or could plausibly contain
121-
// a pointer. There are two challenges; one is that we could have a struct
122-
// the has an inner member which is a pointer. We recurse through the type to
123-
// detect these (up to a point). The other is that we may actually be a union
124-
// of a pointer and another type, and so our LLVM type is an integer which
125-
// gets converted into a pointer, or our type is an [i8 x #] with a pointer
126-
// potentially contained here.
127-
128-
if (GV->hasPrivateLinkage())
129-
return false;
130-
131-
SmallVector<Type *, 4> Types;
132-
Types.push_back(GV->getValueType());
133-
134-
unsigned Limit = 20;
135-
do {
136-
Type *Ty = Types.pop_back_val();
137-
switch (Ty->getTypeID()) {
138-
default: break;
139-
case Type::PointerTyID:
140-
return true;
141-
case Type::FixedVectorTyID:
142-
case Type::ScalableVectorTyID:
143-
if (cast<VectorType>(Ty)->getElementType()->isPointerTy())
144-
return true;
145-
break;
146-
case Type::ArrayTyID:
147-
Types.push_back(cast<ArrayType>(Ty)->getElementType());
148-
break;
149-
case Type::StructTyID: {
150-
StructType *STy = cast<StructType>(Ty);
151-
if (STy->isOpaque()) return true;
152-
for (Type *InnerTy : STy->elements()) {
153-
if (isa<PointerType>(InnerTy)) return true;
154-
if (isa<StructType>(InnerTy) || isa<ArrayType>(InnerTy) ||
155-
isa<VectorType>(InnerTy))
156-
Types.push_back(InnerTy);
157-
}
158-
break;
159-
}
160-
}
161-
if (--Limit == 0) return true;
162-
} while (!Types.empty());
163-
return false;
164-
}
165-
166117
/// Given a value that is stored to a global but never read, determine whether
167118
/// it's safe to remove the store and the chain of computation that feeds the
168119
/// store.
@@ -171,7 +122,7 @@ static bool IsSafeComputationToRemove(
171122
do {
172123
if (isa<Constant>(V))
173124
return true;
174-
if (!V->hasOneUse())
125+
if (V->hasNUsesOrMore(1))
175126
return false;
176127
if (isa<LoadInst>(V) || isa<InvokeInst>(V) || isa<Argument>(V) ||
177128
isa<GlobalValue>(V))
@@ -193,90 +144,12 @@ static bool IsSafeComputationToRemove(
193144
} while (true);
194145
}
195146

196-
/// This GV is a pointer root. Loop over all users of the global and clean up
197-
/// any that obviously don't assign the global a value that isn't dynamically
198-
/// allocated.
199-
static bool
200-
CleanupPointerRootUsers(GlobalVariable *GV,
201-
function_ref<TargetLibraryInfo &(Function &)> GetTLI) {
202-
// A brief explanation of leak checkers. The goal is to find bugs where
203-
// pointers are forgotten, causing an accumulating growth in memory
204-
// usage over time. The common strategy for leak checkers is to explicitly
205-
// allow the memory pointed to by globals at exit. This is popular because it
206-
// also solves another problem where the main thread of a C++ program may shut
207-
// down before other threads that are still expecting to use those globals. To
208-
// handle that case, we expect the program may create a singleton and never
209-
// destroy it.
210-
211-
bool Changed = false;
212-
213-
// If Dead[n].first is the only use of a malloc result, we can delete its
214-
// chain of computation and the store to the global in Dead[n].second.
215-
SmallVector<std::pair<Instruction *, Instruction *>, 32> Dead;
216-
217-
SmallVector<User *> Worklist(GV->users());
218-
// Constants can't be pointers to dynamically allocated memory.
219-
while (!Worklist.empty()) {
220-
User *U = Worklist.pop_back_val();
221-
if (StoreInst *SI = dyn_cast<StoreInst>(U)) {
222-
Value *V = SI->getValueOperand();
223-
if (isa<Constant>(V)) {
224-
Changed = true;
225-
SI->eraseFromParent();
226-
} else if (Instruction *I = dyn_cast<Instruction>(V)) {
227-
if (I->hasOneUse())
228-
Dead.push_back(std::make_pair(I, SI));
229-
}
230-
} else if (MemSetInst *MSI = dyn_cast<MemSetInst>(U)) {
231-
if (isa<Constant>(MSI->getValue())) {
232-
Changed = true;
233-
MSI->eraseFromParent();
234-
} else if (Instruction *I = dyn_cast<Instruction>(MSI->getValue())) {
235-
if (I->hasOneUse())
236-
Dead.push_back(std::make_pair(I, MSI));
237-
}
238-
} else if (MemTransferInst *MTI = dyn_cast<MemTransferInst>(U)) {
239-
GlobalVariable *MemSrc = dyn_cast<GlobalVariable>(MTI->getSource());
240-
if (MemSrc && MemSrc->isConstant()) {
241-
Changed = true;
242-
MTI->eraseFromParent();
243-
} else if (Instruction *I = dyn_cast<Instruction>(MTI->getSource())) {
244-
if (I->hasOneUse())
245-
Dead.push_back(std::make_pair(I, MTI));
246-
}
247-
} else if (ConstantExpr *CE = dyn_cast<ConstantExpr>(U)) {
248-
if (isa<GEPOperator>(CE))
249-
append_range(Worklist, CE->users());
250-
}
251-
}
252-
253-
for (int i = 0, e = Dead.size(); i != e; ++i) {
254-
if (IsSafeComputationToRemove(Dead[i].first, GetTLI)) {
255-
Dead[i].second->eraseFromParent();
256-
Instruction *I = Dead[i].first;
257-
do {
258-
if (isAllocationFn(I, GetTLI))
259-
break;
260-
Instruction *J = dyn_cast<Instruction>(I->getOperand(0));
261-
if (!J)
262-
break;
263-
I->eraseFromParent();
264-
I = J;
265-
} while (true);
266-
I->eraseFromParent();
267-
Changed = true;
268-
}
269-
}
270-
271-
GV->removeDeadConstantUsers();
272-
return Changed;
273-
}
274-
275147
/// We just marked GV constant. Loop over all users of the global, cleaning up
276148
/// the obvious ones. This is largely just a quick scan over the use list to
277149
/// clean up the easy and obvious cruft. This returns true if it made a change.
278-
static bool CleanupConstantGlobalUsers(GlobalVariable *GV,
279-
const DataLayout &DL) {
150+
static bool CleanupConstantGlobalUsers(
151+
GlobalVariable *GV, const DataLayout &DL,
152+
function_ref<TargetLibraryInfo &(Function &)> GetTLI) {
280153
Constant *Init = GV->getInitializer();
281154
SmallVector<User *, 8> WorkList(GV->users());
282155
SmallPtrSet<User *, 8> Visited;
@@ -326,11 +199,30 @@ static bool CleanupConstantGlobalUsers(GlobalVariable *GV,
326199
}
327200
}
328201
} else if (StoreInst *SI = dyn_cast<StoreInst>(U)) {
329-
// Store must be unreachable or storing Init into the global.
330-
EraseFromParent(SI);
331-
} else if (MemIntrinsic *MI = dyn_cast<MemIntrinsic>(U)) { // memset/cpy/mv
332-
if (getUnderlyingObject(MI->getRawDest()) == GV)
333-
EraseFromParent(MI);
202+
auto *V = SI->getValueOperand();
203+
if (isa<Constant>(V)) {
204+
EraseFromParent(SI);
205+
} else if (isa<Instruction>(V)) {
206+
EraseFromParent(SI);
207+
if (IsSafeComputationToRemove(V, GetTLI))
208+
RecursivelyDeleteTriviallyDeadInstructions(V);
209+
} else if (isa<Argument>(V)) {
210+
if (!V->getType()->isPointerTy())
211+
EraseFromParent(SI);
212+
}
213+
} else if (auto *MSI = dyn_cast<MemSetInst>(U)) { // memset/cpy/mv
214+
if (getUnderlyingObject(MSI->getRawDest()) == GV)
215+
EraseFromParent(MSI);
216+
} else if (auto *MTI = dyn_cast<MemTransferInst>(U)) {
217+
auto *Src = MTI->getRawSource();
218+
auto *Dst = MTI->getRawDest();
219+
if (getUnderlyingObject(Dst) != GV)
220+
continue;
221+
if (isa<Instruction, Operator>(Src)) {
222+
EraseFromParent(MTI);
223+
if (IsSafeComputationToRemove(Src, GetTLI))
224+
RecursivelyDeleteTriviallyDeadInstructions(Src);
225+
}
334226
} else if (IntrinsicInst *II = dyn_cast<IntrinsicInst>(U)) {
335227
if (II->getIntrinsicID() == Intrinsic::threadlocal_address)
336228
append_range(WorkList, II->users());
@@ -878,12 +770,7 @@ static bool OptimizeAwayTrappingUsesOfLoads(
878770
// If we nuked all of the loads, then none of the stores are needed either,
879771
// nor is the global.
880772
if (AllNonStoreUsesGone) {
881-
if (isLeakCheckerRoot(GV)) {
882-
Changed |= CleanupPointerRootUsers(GV, GetTLI);
883-
} else {
884-
Changed = true;
885-
CleanupConstantGlobalUsers(GV, DL);
886-
}
773+
Changed |= CleanupConstantGlobalUsers(GV, DL, GetTLI);
887774
if (GV->use_empty()) {
888775
LLVM_DEBUG(dbgs() << " *** GLOBAL NOW DEAD!\n");
889776
Changed = true;
@@ -1497,15 +1384,7 @@ processInternalGlobal(GlobalVariable *GV, const GlobalStatus &GS,
14971384
// Delete it now.
14981385
if (!GS.IsLoaded) {
14991386
LLVM_DEBUG(dbgs() << "GLOBAL NEVER LOADED: " << *GV << "\n");
1500-
1501-
if (isLeakCheckerRoot(GV)) {
1502-
// Delete any constant stores to the global.
1503-
Changed = CleanupPointerRootUsers(GV, GetTLI);
1504-
} else {
1505-
// Delete any stores we can find to the global. We may not be able to
1506-
// make it completely dead though.
1507-
Changed = CleanupConstantGlobalUsers(GV, DL);
1508-
}
1387+
Changed = CleanupConstantGlobalUsers(GV, DL, GetTLI);
15091388

15101389
// If the global is dead now, delete it.
15111390
if (GV->use_empty()) {
@@ -1529,7 +1408,7 @@ processInternalGlobal(GlobalVariable *GV, const GlobalStatus &GS,
15291408
}
15301409

15311410
// Clean up any obviously simplifiable users now.
1532-
Changed |= CleanupConstantGlobalUsers(GV, DL);
1411+
Changed |= CleanupConstantGlobalUsers(GV, DL, GetTLI);
15331412

15341413
// If the global is dead now, just nuke it.
15351414
if (GV->use_empty()) {
@@ -1584,7 +1463,7 @@ processInternalGlobal(GlobalVariable *GV, const GlobalStatus &GS,
15841463
}
15851464

15861465
// Clean up any obviously simplifiable users now.
1587-
CleanupConstantGlobalUsers(GV, DL);
1466+
CleanupConstantGlobalUsers(GV, DL, GetTLI);
15881467

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

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

-1
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

Diff for: llvm/test/Transforms/GlobalOpt/dead-store-status.ll

-2
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

Diff for: llvm/test/Transforms/GlobalOpt/pr54572.ll

+10-3
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)