Skip to content

Commit 32e2649

Browse files
Revert "[GlobalOpt] Revert valgrind hacks"
This reverts commit dbc16ed.
1 parent 3f4c1e1 commit 32e2649

File tree

5 files changed

+239
-9
lines changed

5 files changed

+239
-9
lines changed

llvm/lib/Transforms/IPO/GlobalOpt.cpp

Lines changed: 184 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,175 @@ static cl::opt<int> ColdCCRelFreq(
106106
"entry frequency, for a call site to be considered cold for enabling"
107107
"coldcc"));
108108

109+
/// Is this global variable possibly used by a leak checker as a root? If so,
110+
/// we might not really want to eliminate the stores to it.
111+
static bool isLeakCheckerRoot(GlobalVariable *GV) {
112+
// A global variable is a root if it is a pointer, or could plausibly contain
113+
// a pointer. There are two challenges; one is that we could have a struct
114+
// the has an inner member which is a pointer. We recurse through the type to
115+
// detect these (up to a point). The other is that we may actually be a union
116+
// of a pointer and another type, and so our LLVM type is an integer which
117+
// gets converted into a pointer, or our type is an [i8 x #] with a pointer
118+
// potentially contained here.
119+
120+
if (GV->hasPrivateLinkage())
121+
return false;
122+
123+
SmallVector<Type *, 4> Types;
124+
Types.push_back(GV->getValueType());
125+
126+
unsigned Limit = 20;
127+
do {
128+
Type *Ty = Types.pop_back_val();
129+
switch (Ty->getTypeID()) {
130+
default: break;
131+
case Type::PointerTyID:
132+
return true;
133+
case Type::FixedVectorTyID:
134+
case Type::ScalableVectorTyID:
135+
if (cast<VectorType>(Ty)->getElementType()->isPointerTy())
136+
return true;
137+
break;
138+
case Type::ArrayTyID:
139+
Types.push_back(cast<ArrayType>(Ty)->getElementType());
140+
break;
141+
case Type::StructTyID: {
142+
StructType *STy = cast<StructType>(Ty);
143+
if (STy->isOpaque()) return true;
144+
for (StructType::element_iterator I = STy->element_begin(),
145+
E = STy->element_end(); I != E; ++I) {
146+
Type *InnerTy = *I;
147+
if (isa<PointerType>(InnerTy)) return true;
148+
if (isa<StructType>(InnerTy) || isa<ArrayType>(InnerTy) ||
149+
isa<VectorType>(InnerTy))
150+
Types.push_back(InnerTy);
151+
}
152+
break;
153+
}
154+
}
155+
if (--Limit == 0) return true;
156+
} while (!Types.empty());
157+
return false;
158+
}
159+
160+
/// Given a value that is stored to a global but never read, determine whether
161+
/// it's safe to remove the store and the chain of computation that feeds the
162+
/// store.
163+
static bool IsSafeComputationToRemove(
164+
Value *V, function_ref<TargetLibraryInfo &(Function &)> GetTLI) {
165+
do {
166+
if (isa<Constant>(V))
167+
return true;
168+
if (!V->hasOneUse())
169+
return false;
170+
if (isa<LoadInst>(V) || isa<InvokeInst>(V) || isa<Argument>(V) ||
171+
isa<GlobalValue>(V))
172+
return false;
173+
if (isAllocationFn(V, GetTLI))
174+
return true;
175+
176+
Instruction *I = cast<Instruction>(V);
177+
if (I->mayHaveSideEffects())
178+
return false;
179+
if (GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(I)) {
180+
if (!GEP->hasAllConstantIndices())
181+
return false;
182+
} else if (I->getNumOperands() != 1) {
183+
return false;
184+
}
185+
186+
V = I->getOperand(0);
187+
} while (true);
188+
}
189+
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+
// Constants can't be pointers to dynamically allocated memory.
212+
for (Value::user_iterator UI = GV->user_begin(), E = GV->user_end();
213+
UI != E;) {
214+
User *U = *UI++;
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>(MemSrc)) {
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 (CE->use_empty()) {
243+
CE->destroyConstant();
244+
Changed = true;
245+
}
246+
} else if (Constant *C = dyn_cast<Constant>(U)) {
247+
if (isSafeToDestroyConstant(C)) {
248+
C->destroyConstant();
249+
// This could have invalidated UI, start over from scratch.
250+
Dead.clear();
251+
CleanupPointerRootUsers(GV, GetTLI);
252+
return true;
253+
}
254+
}
255+
}
256+
257+
for (int i = 0, e = Dead.size(); i != e; ++i) {
258+
if (IsSafeComputationToRemove(Dead[i].first, GetTLI)) {
259+
Dead[i].second->eraseFromParent();
260+
Instruction *I = Dead[i].first;
261+
do {
262+
if (isAllocationFn(I, GetTLI))
263+
break;
264+
Instruction *J = dyn_cast<Instruction>(I->getOperand(0));
265+
if (!J)
266+
break;
267+
I->eraseFromParent();
268+
I = J;
269+
} while (true);
270+
I->eraseFromParent();
271+
Changed = true;
272+
}
273+
}
274+
275+
return Changed;
276+
}
277+
109278
/// We just marked GV constant. Loop over all users of the global, cleaning up
110279
/// the obvious ones. This is largely just a quick scan over the use list to
111280
/// clean up the easy and obvious cruft. This returns true if it made a change.
@@ -654,8 +823,12 @@ static bool OptimizeAwayTrappingUsesOfLoads(
654823
// If we nuked all of the loads, then none of the stores are needed either,
655824
// nor is the global.
656825
if (AllNonStoreUsesGone) {
657-
Changed = true;
658-
CleanupConstantGlobalUsers(GV, nullptr, DL, GetTLI);
826+
if (isLeakCheckerRoot(GV)) {
827+
Changed |= CleanupPointerRootUsers(GV, GetTLI);
828+
} else {
829+
Changed = true;
830+
CleanupConstantGlobalUsers(GV, nullptr, DL, GetTLI);
831+
}
659832
if (GV->use_empty()) {
660833
LLVM_DEBUG(dbgs() << " *** GLOBAL NOW DEAD!\n");
661834
Changed = true;
@@ -1824,10 +1997,15 @@ processInternalGlobal(GlobalVariable *GV, const GlobalStatus &GS,
18241997
if (!GS.IsLoaded) {
18251998
LLVM_DEBUG(dbgs() << "GLOBAL NEVER LOADED: " << *GV << "\n");
18261999

1827-
// Delete any stores we can find to the global. We may not be able to
1828-
// make it completely dead though.
1829-
Changed =
1830-
CleanupConstantGlobalUsers(GV, GV->getInitializer(), DL, GetTLI);
2000+
if (isLeakCheckerRoot(GV)) {
2001+
// Delete any constant stores to the global.
2002+
Changed = CleanupPointerRootUsers(GV, GetTLI);
2003+
} else {
2004+
// Delete any stores we can find to the global. We may not be able to
2005+
// make it completely dead though.
2006+
Changed =
2007+
CleanupConstantGlobalUsers(GV, GV->getInitializer(), DL, GetTLI);
2008+
}
18312009

18322010
// If the global is dead now, delete it.
18332011
if (GV->use_empty()) {

llvm/test/ThinLTO/X86/import-constant.ll

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,11 @@
3232
; IMPORT-NEXT: @_ZL3Obj.llvm.{{.*}} = available_externally hidden constant %struct.S { i32 4, i32 8, i32* @val }
3333
; IMPORT-NEXT: @val = available_externally global i32 42
3434

35+
; OPT: @outer = internal unnamed_addr global %struct.Q zeroinitializer
36+
3537
; OPT: define dso_local i32 @main()
3638
; OPT-NEXT: entry:
39+
; OPT-NEXT: store %struct.S* null, %struct.S** getelementptr inbounds (%struct.Q, %struct.Q* @outer, i64 0, i32 0)
3740
; OPT-NEXT: ret i32 12
3841

3942
; NOREFS: @outer = internal local_unnamed_addr global %struct.Q zeroinitializer

llvm/test/Transforms/GlobalOpt/2009-11-16-BrokenPerformHeapAllocSRoA.ll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ define void @test() nounwind ssp {
1717
%2 = sext i32 %1 to i64 ; <i64> [#uses=1]
1818
%3 = mul i64 %2, ptrtoint (%struct.strchartype* getelementptr (%struct.strchartype, %struct.strchartype* null, i64 1) to i64) ; <i64> [#uses=1]
1919
%4 = tail call i8* @malloc(i64 %3) ; <i8*> [#uses=1]
20-
; CHECK: call i8* @malloc(i64
20+
; CHECK-NOT: call i8* @malloc(i64
2121
%5 = bitcast i8* %4 to %struct.strchartype* ; <%struct.strchartype*> [#uses=1]
2222
store %struct.strchartype* %5, %struct.strchartype** @chartypes, align 8
2323
ret void
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
; RUN: opt -globalopt -S -o - < %s | FileCheck %s
2+
3+
@glbl = internal global i8* null
4+
5+
define void @test1a() {
6+
; CHECK-LABEL: @test1a(
7+
; CHECK-NOT: store
8+
; CHECK-NEXT: ret void
9+
store i8* null, i8** @glbl
10+
ret void
11+
}
12+
13+
define void @test1b(i8* %p) {
14+
; CHECK-LABEL: @test1b(
15+
; CHECK-NEXT: store
16+
; CHECK-NEXT: ret void
17+
store i8* %p, i8** @glbl
18+
ret void
19+
}
20+
21+
define void @test2() {
22+
; CHECK-LABEL: @test2(
23+
; CHECK: alloca i8
24+
%txt = alloca i8
25+
call void @foo2(i8* %txt)
26+
%call2 = call i8* @strdup(i8* %txt)
27+
store i8* %call2, i8** @glbl
28+
ret void
29+
}
30+
declare i8* @strdup(i8*)
31+
declare void @foo2(i8*)
32+
33+
define void @test3() uwtable personality i32 (i32, i64, i8*, i8*)* @__gxx_personality_v0 {
34+
; CHECK-LABEL: @test3(
35+
; CHECK-NOT: bb1:
36+
; CHECK-NOT: bb2:
37+
; CHECK: invoke
38+
%ptr = invoke i8* @_Znwm(i64 1)
39+
to label %bb1 unwind label %bb2
40+
bb1:
41+
store i8* %ptr, i8** @glbl
42+
unreachable
43+
bb2:
44+
%tmp1 = landingpad { i8*, i32 }
45+
cleanup
46+
resume { i8*, i32 } %tmp1
47+
}
48+
declare i32 @__gxx_personality_v0(i32, i64, i8*, i8*)
49+
declare i8* @_Znwm(i64)

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
; RUN: opt < %s -globalopt -instcombine -S | FileCheck %s
1+
; RUN: opt < %s -globalopt -S | FileCheck %s
22

33
; When removing the store to @global in @foo, the pass would incorrectly return
44
; false. This was caught by the pass return status check that is hidden under
55
; EXPENSIVE_CHECKS.
66

7-
; CHECK-NOT: @global = internal unnamed_addr global i16* null, align 1
7+
; CHECK: @global = internal unnamed_addr global i16* null, align 1
88

99
; CHECK-LABEL: @foo
1010
; CHECK-NEXT: entry:

0 commit comments

Comments
 (0)