diff --git a/llvm/lib/Transforms/IPO/GlobalOpt.cpp b/llvm/lib/Transforms/IPO/GlobalOpt.cpp --- a/llvm/lib/Transforms/IPO/GlobalOpt.cpp +++ b/llvm/lib/Transforms/IPO/GlobalOpt.cpp @@ -720,22 +720,55 @@ /// Return true if all uses of any loads from GV will trap if the loaded value /// is null. Note that this also permits comparisons of the loaded value /// against null, as a special case. -static bool AllUsesOfLoadedValueWillTrapIfNull(const GlobalVariable *GV) { - for (const User *U : GV->users()) - if (const LoadInst *LI = dyn_cast(U)) { - SmallPtrSet PHIs; - if (!AllUsesOfValueWillTrapIfNull(LI, PHIs)) +static bool allUsesOfLoadedValueWillTrapIfNull(const GlobalVariable *GV) { + SmallVector Worklist; + Worklist.push_back(GV); + while (!Worklist.empty()) { + const Value *P = Worklist.pop_back_val(); + for (auto *U : P->users()) { + if (auto *LI = dyn_cast(U)) { + SmallPtrSet PHIs; + if (!AllUsesOfValueWillTrapIfNull(LI, PHIs)) + return false; + } else if (auto *SI = dyn_cast(U)) { + // Ignore stores to the global. + if (SI->getPointerOperand() != P) + return false; + } else if (auto *CE = dyn_cast(U)) { + if (CE->stripPointerCasts() != GV) + return false; + // Check further the ConstantExpr. + Worklist.push_back(CE); + } else { + // We don't know or understand this user, bail out. return false; - } else if (isa(U)) { - // Ignore stores to the global. - } else { - // We don't know or understand this user, bail out. - //cerr << "UNKNOWN USER OF GLOBAL!: " << *U; - return false; + } } + } + return true; } +/// Get all the loads/store uses for global variable \p GV. +static void allUsesOfLoadAndStores(GlobalVariable *GV, + SmallVector &Uses) { + SmallVector Worklist; + Worklist.push_back(GV); + while (!Worklist.empty()) { + auto *P = Worklist.pop_back_val(); + for (auto *U : P->users()) { + if (auto *CE = dyn_cast(U)) { + Worklist.push_back(CE); + continue; + } + + assert((isa(U) || isa(U)) && + "Expect only load or store instructions"); + Uses.push_back(U); + } + } +} + static bool OptimizeAwayTrappingUsesOfValue(Value *V, Constant *NewV) { bool Changed = false; for (auto UI = V->user_begin(), E = V->user_end(); UI != E; ) { @@ -947,9 +980,11 @@ GV->getName()+".init", GV->getThreadLocalMode()); bool InitBoolUsed = false; - // Loop over all uses of GV, processing them in turn. - while (!GV->use_empty()) { - if (StoreInst *SI = dyn_cast(GV->user_back())) { + // Loop over all instruction uses of GV, processing them in turn. + SmallVector Guses; + allUsesOfLoadAndStores(GV, Guses); + for (auto *U : Guses) { + if (StoreInst *SI = dyn_cast(U)) { // The global is initialized when the store to it occurs. If the stored // value is null value, the global bool is set to false, otherwise true. new StoreInst(ConstantInt::getBool( @@ -961,7 +996,7 @@ continue; } - LoadInst *LI = cast(GV->user_back()); + LoadInst *LI = cast(U); while (!LI->use_empty()) { Use &LoadUse = *LI->use_begin(); ICmpInst *ICI = dyn_cast(LoadUse.getUser()); @@ -1019,33 +1054,47 @@ return NewGV; } -/// Scan the use-list of V checking to make sure that there are no complex uses -/// of V. We permit simple things like dereferencing the pointer, but not +/// Scan the use-list of GV checking to make sure that there are no complex uses +/// of GV. We permit simple things like dereferencing the pointer, but not /// storing through the address, unless it is to the specified global. static bool -valueIsOnlyUsedLocallyOrStoredToOneGlobal(const Instruction *V, +valueIsOnlyUsedLocallyOrStoredToOneGlobal(const CallInst *CI, const GlobalVariable *GV) { - for (const User *U : V->users()) { - const Instruction *Inst = cast(U); + SmallPtrSet Visited; + SmallVector Worklist; + Worklist.push_back(CI); - if (isa(Inst) || isa(Inst)) { - continue; // Fine, ignore. - } + while (!Worklist.empty()) { + const Value *V = Worklist.pop_back_val(); + if (!Visited.insert(V).second) + continue; - if (const StoreInst *SI = dyn_cast(Inst)) { - if (SI->getOperand(0) == V && SI->getOperand(1) != GV) - return false; // Storing the pointer itself... bad. - continue; // Otherwise, storing through it, or storing into GV... fine. - } + for (const Use &VUse : V->uses()) { + const User *U = VUse.getUser(); + if (isa(U) || isa(U)) + continue; // Fine, ignore. - if (const BitCastInst *BCI = dyn_cast(Inst)) { - if (!valueIsOnlyUsedLocallyOrStoredToOneGlobal(BCI, GV)) - return false; - continue; - } + if (auto *SI = dyn_cast(U)) { + if (SI->getValueOperand() == V && + SI->getPointerOperand()->stripPointerCasts() != GV) + return false; // Storing the pointer not into GV... bad. + continue; // Otherwise, storing through it, or storing into GV... fine. + } - return false; + if (auto *BCI = dyn_cast(U)) { + Worklist.push_back(BCI); + continue; + } + + if (auto *GEPI = dyn_cast(U)) { + Worklist.push_back(GEPI); + continue; + } + + return false; + } } + return true; } @@ -1066,12 +1115,12 @@ // been reached). To do this, we check to see if all uses of the global // would trap if the global were null: this proves that they must all // happen after the malloc. - if (!AllUsesOfLoadedValueWillTrapIfNull(GV)) + if (!allUsesOfLoadedValueWillTrapIfNull(GV)) return false; // We can't optimize this if the malloc itself is used in a complex way, // for example, being stored into multiple globals. This allows the - // malloc to be stored into the specified global, loaded icmp'd. + // malloc to be stored into the specified global, loaded, gep, icmp'd. // These are all things we could transform to using the global for. if (!valueIsOnlyUsedLocallyOrStoredToOneGlobal(CI, GV)) return false; diff --git a/llvm/lib/Transforms/Utils/GlobalStatus.cpp b/llvm/lib/Transforms/Utils/GlobalStatus.cpp --- a/llvm/lib/Transforms/Utils/GlobalStatus.cpp +++ b/llvm/lib/Transforms/Utils/GlobalStatus.cpp @@ -105,8 +105,10 @@ // value, not an aggregate), keep more specific information about // stores. if (GS.StoredType != GlobalStatus::Stored) { - if (const GlobalVariable *GV = - dyn_cast(SI->getOperand(1))) { + const Value *Ptr = SI->getPointerOperand(); + if (isa(Ptr)) + Ptr = Ptr->stripPointerCasts(); + if (const GlobalVariable *GV = dyn_cast(Ptr)) { Value *StoredVal = SI->getOperand(0); if (Constant *C = dyn_cast(StoredVal)) { diff --git a/llvm/test/Transforms/GlobalOpt/new-promote.ll b/llvm/test/Transforms/GlobalOpt/new-promote.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/GlobalOpt/new-promote.ll @@ -0,0 +1,41 @@ +; RUN: opt -globalopt -S < %s | FileCheck %s +; RUN: opt -passes=globalopt -S < %s | FileCheck %s + +%s = type { i32 } +@g = internal global %s* null, align 8 + +; Test code pattern for: +; class s { int a; s() { a = 1;} }; +; g = new s(); +; + +define internal void @f() { +; CHECK-LABEL: @f( +; CHECK-NEXT: ret void +; + %1 = tail call i8* @_Znwm(i64 4) + %2 = bitcast i8* %1 to %s* + %3 = getelementptr inbounds %s, %s* %2, i64 0, i32 0 + store i32 1, i32* %3, align 4 + store i8* %1, i8** bitcast (%s** @g to i8**), align 8 + ret void +} + +define dso_local signext i32 @main() { +; CHECK-LABEL: @main( +; CHECK-NEXT: entry: +; CHECK-NEXT: call fastcc void @f() +; CHECK-NEXT: ret i32 1 +; +entry: + call void @f() + %0 = load %s*, %s** @g, align 4 + %1 = getelementptr inbounds %s, %s* %0, i64 0, i32 0 + %2 = load i32, i32* %1, align 4 + ret i32 %2 +} + +declare nonnull i8* @_Znwm(i64) + +declare signext i32 @printf(i8*, ...) +