diff --git a/lib/Transforms/IPO/GlobalOpt.cpp b/lib/Transforms/IPO/GlobalOpt.cpp index 3129559fb99..cf15580b039 100644 --- a/lib/Transforms/IPO/GlobalOpt.cpp +++ b/lib/Transforms/IPO/GlobalOpt.cpp @@ -50,6 +50,7 @@ STATISTIC(NumSubstitute,"Number of globals with initializers stored into them"); STATISTIC(NumDeleted , "Number of globals deleted"); STATISTIC(NumFnDeleted , "Number of functions deleted"); STATISTIC(NumGlobUses , "Number of global uses devirtualized"); +STATISTIC(NumLocalized , "Number of globals localized"); STATISTIC(NumShrunkToBool , "Number of global vars shrunk to booleans"); STATISTIC(NumFastCallFns , "Number of functions converted to fastcc"); STATISTIC(NumCtorsEvaluated, "Number of static ctors evaluated"); @@ -136,12 +137,24 @@ struct GlobalStatus { /// ever stored to this global, keep track of what value it is. Value *StoredOnceValue; + /// AccessingFunction/HasMultipleAccessingFunctions - These start out + /// null/false. When the first accessing function is noticed, it is recorded. + /// When a second different accessing function is noticed, + /// HasMultipleAccessingFunctions is set to true. + const Function *AccessingFunction; + bool HasMultipleAccessingFunctions; + + /// HasNonInstructionUser - Set to true if this global has a user that is not + /// an instruction (e.g. a constant expr or GV initializer). + bool HasNonInstructionUser; + /// AtomicOrdering - Set to the strongest atomic ordering requirement. AtomicOrdering Ordering; - GlobalStatus() - : isCompared(false), isLoaded(false), StoredType(NotStored), - StoredOnceValue(0), Ordering(NotAtomic) {} + GlobalStatus() : isCompared(false), isLoaded(false), StoredType(NotStored), + StoredOnceValue(0), AccessingFunction(0), + HasMultipleAccessingFunctions(false), + HasNonInstructionUser(false), Ordering(NotAtomic) {} }; } @@ -182,12 +195,21 @@ static bool AnalyzeGlobal(const Value *V, GlobalStatus &GS, ++UI) { const User *U = *UI; if (const ConstantExpr *CE = dyn_cast(U)) { + GS.HasNonInstructionUser = true; + // If the result of the constantexpr isn't pointer type, then we won't // know to expect it in various places. Just reject early. if (!isa(CE->getType())) return true; if (AnalyzeGlobal(CE, GS, PHIUsers)) return true; } else if (const Instruction *I = dyn_cast(U)) { + if (!GS.HasMultipleAccessingFunctions) { + const Function *F = I->getParent()->getParent(); + if (GS.AccessingFunction == 0) + GS.AccessingFunction = F; + else if (GS.AccessingFunction != F) + GS.HasMultipleAccessingFunctions = true; + } if (const LoadInst *LI = dyn_cast(I)) { GS.isLoaded = true; // Don't hack on volatile loads. @@ -264,10 +286,12 @@ static bool AnalyzeGlobal(const Value *V, GlobalStatus &GS, return true; // Any other non-load instruction might take address! } } else if (const Constant *C = dyn_cast(U)) { + GS.HasNonInstructionUser = true; // We might have a dead and dangling constant hanging off of here. if (!SafeToDestroyConstant(C)) return true; } else { + GS.HasNonInstructionUser = true; // Otherwise must be some other user. return true; } @@ -1914,6 +1938,35 @@ bool GlobalOpt::ProcessGlobal(GlobalVariable *GV, bool GlobalOpt::ProcessInternalGlobal(GlobalVariable *GV, Module::global_iterator &GVI, const GlobalStatus &GS) { + // If this is a first class global and has only one accessing function + // and this function is main (which we know is not recursive), we replace + // the global with a local alloca in this function. + // + // NOTE: It doesn't make sense to promote non single-value types since we + // are just replacing static memory to stack memory. + // + // If the global is in different address space, don't bring it to stack. + if (!GS.HasMultipleAccessingFunctions && + GS.AccessingFunction && !GS.HasNonInstructionUser && + GV->getType()->getElementType()->isSingleValueType() && + GS.AccessingFunction->getName() == "main" && + GS.AccessingFunction->hasExternalLinkage() && + GV->getType()->getAddressSpace() == 0) { + DEBUG(dbgs() << "LOCALIZING GLOBAL: " << *GV); + Instruction &FirstI = const_cast(*GS.AccessingFunction + ->getEntryBlock().begin()); + Type *ElemTy = GV->getType()->getElementType(); + // FIXME: Pass Global's alignment when globals have alignment + AllocaInst *Alloca = new AllocaInst(ElemTy, NULL, GV->getName(), &FirstI); + if (!isa(GV->getInitializer())) + new StoreInst(GV->getInitializer(), Alloca, &FirstI); + + GV->replaceAllUsesWith(Alloca); + GV->eraseFromParent(); + ++NumLocalized; + return true; + } + // If the global is never loaded (but may be stored to), it is dead. // Delete it now. if (!GS.isLoaded) { diff --git a/test/Transforms/GlobalOpt/metadata.ll b/test/Transforms/GlobalOpt/metadata.ll new file mode 100644 index 00000000000..ecf3f94196b --- /dev/null +++ b/test/Transforms/GlobalOpt/metadata.ll @@ -0,0 +1,26 @@ +; RUN: opt -S -globalopt < %s | FileCheck %s + +; PR6112 - When globalopt does RAUW(@G, %G), the metadata reference should drop +; to null. Function local metadata that references @G from a different function +; to that containing %G should likewise drop to null. +@G = internal global i8** null + +define i32 @main(i32 %argc, i8** %argv) { +; CHECK-LABEL: @main( +; CHECK: %G = alloca + store i8** %argv, i8*** @G + ret i32 0 +} + +define void @foo(i32 %x) { + call void @llvm.foo(metadata !{i8*** @G, i32 %x}) +; CHECK: call void @llvm.foo(metadata !{null, i32 %x}) + ret void +} + +declare void @llvm.foo(metadata) nounwind readnone + +!named = !{!0} + +!0 = metadata !{i8*** @G} +; CHECK: !0 = metadata !{null}