Skip to content

Commit e16e93a

Browse files
Revert "[GlobalOpt] Handle operators separately when removing GV users" (#132971)
Reverts #84694 . Review was incomplete.
1 parent 51dad71 commit e16e93a

File tree

4 files changed

+159
-42
lines changed

4 files changed

+159
-42
lines changed

llvm/lib/Transforms/IPO/GlobalOpt.cpp

+153-32
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,55 @@ 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+
117166
/// Given a value that is stored to a global but never read, determine whether
118167
/// it's safe to remove the store and the chain of computation that feeds the
119168
/// store.
@@ -122,7 +171,7 @@ static bool IsSafeComputationToRemove(
122171
do {
123172
if (isa<Constant>(V))
124173
return true;
125-
if (V->hasNUsesOrMore(1))
174+
if (!V->hasOneUse())
126175
return false;
127176
if (isa<LoadInst>(V) || isa<InvokeInst>(V) || isa<Argument>(V) ||
128177
isa<GlobalValue>(V))
@@ -144,12 +193,90 @@ static bool IsSafeComputationToRemove(
144193
} while (true);
145194
}
146195

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+
147275
/// We just marked GV constant. Loop over all users of the global, cleaning up
148276
/// the obvious ones. This is largely just a quick scan over the use list to
149277
/// clean up the easy and obvious cruft. This returns true if it made a change.
150-
static bool CleanupConstantGlobalUsers(
151-
GlobalVariable *GV, const DataLayout &DL,
152-
function_ref<TargetLibraryInfo &(Function &)> GetTLI) {
278+
static bool CleanupConstantGlobalUsers(GlobalVariable *GV,
279+
const DataLayout &DL) {
153280
Constant *Init = GV->getInitializer();
154281
SmallVector<User *, 8> WorkList(GV->users());
155282
SmallPtrSet<User *, 8> Visited;
@@ -199,30 +326,11 @@ static bool CleanupConstantGlobalUsers(
199326
}
200327
}
201328
} else if (StoreInst *SI = dyn_cast<StoreInst>(U)) {
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-
}
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);
226334
} else if (IntrinsicInst *II = dyn_cast<IntrinsicInst>(U)) {
227335
if (II->getIntrinsicID() == Intrinsic::threadlocal_address)
228336
append_range(WorkList, II->users());
@@ -770,7 +878,12 @@ static bool OptimizeAwayTrappingUsesOfLoads(
770878
// If we nuked all of the loads, then none of the stores are needed either,
771879
// nor is the global.
772880
if (AllNonStoreUsesGone) {
773-
Changed |= CleanupConstantGlobalUsers(GV, DL, GetTLI);
881+
if (isLeakCheckerRoot(GV)) {
882+
Changed |= CleanupPointerRootUsers(GV, GetTLI);
883+
} else {
884+
Changed = true;
885+
CleanupConstantGlobalUsers(GV, DL);
886+
}
774887
if (GV->use_empty()) {
775888
LLVM_DEBUG(dbgs() << " *** GLOBAL NOW DEAD!\n");
776889
Changed = true;
@@ -1384,7 +1497,15 @@ processInternalGlobal(GlobalVariable *GV, const GlobalStatus &GS,
13841497
// Delete it now.
13851498
if (!GS.IsLoaded) {
13861499
LLVM_DEBUG(dbgs() << "GLOBAL NEVER LOADED: " << *GV << "\n");
1387-
Changed = CleanupConstantGlobalUsers(GV, DL, GetTLI);
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+
}
13881509

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

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

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

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

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

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

+1
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ 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
102103
; CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr @a, align 4
103104
; CHECK-NEXT: [[INC:%.*]] = add nsw i32 [[TMP1]], 1
104105
; CHECK-NEXT: store i32 [[INC]], ptr @a, align 4

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

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
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+
79
; CHECK-LABEL: @foo
810
; CHECK-NEXT: entry:
911
; CHECK-NEXT: ret i16 undef

llvm/test/Transforms/GlobalOpt/pr54572.ll

+3-10
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,17 @@
77
declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg)
88

99
;.
10-
; CHECK: @b = internal unnamed_addr global ptr null
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
1112
;.
1213
define void @test() {
1314
; 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)
1416
; CHECK-NEXT: ret void
1517
;
1618
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)
1719
ret void
1820
}
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-
}
2821
;.
2922
; CHECK: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
3023
;.

0 commit comments

Comments
 (0)