From 939724cd020b6172435a207e0e4d59dcab90048b Mon Sep 17 00:00:00 2001 From: Oliver Stannard Date: Mon, 12 Oct 2015 13:20:52 +0000 Subject: [PATCH] GlobalOpt does not treat externally_initialized globals correctly GlobalOpt currently merges stores into the initialisers of internal, externally_initialized globals, but should not do so as the value of the global may change between the initialiser and any code in the module being run. llvm-svn: 250035 --- llvm/lib/Transforms/IPO/GlobalOpt.cpp | 2 +- llvm/lib/Transforms/Utils/GlobalStatus.cpp | 4 ++ .../GlobalOpt/externally-initialized.ll | 37 +++++++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 llvm/test/Transforms/GlobalOpt/externally-initialized.ll diff --git a/llvm/lib/Transforms/IPO/GlobalOpt.cpp b/llvm/lib/Transforms/IPO/GlobalOpt.cpp index a4b8408b1bf7..46a209f23207 100644 --- a/llvm/lib/Transforms/IPO/GlobalOpt.cpp +++ b/llvm/lib/Transforms/IPO/GlobalOpt.cpp @@ -1804,7 +1804,7 @@ bool GlobalOpt::ProcessInternalGlobal(GlobalVariable *GV, GVI = FirstNewGV; // Don't skip the newly produced globals! return true; } - } else if (GS.StoredType == GlobalStatus::StoredOnce) { + } else if (GS.StoredType == GlobalStatus::StoredOnce && GS.StoredOnceValue) { // If the initial value for the global was an undef value, and if only // one other value was stored into it, we can just change the // initializer to be the stored value, then delete all stores to the diff --git a/llvm/lib/Transforms/Utils/GlobalStatus.cpp b/llvm/lib/Transforms/Utils/GlobalStatus.cpp index 44b7d25d519a..3893a752503b 100644 --- a/llvm/lib/Transforms/Utils/GlobalStatus.cpp +++ b/llvm/lib/Transforms/Utils/GlobalStatus.cpp @@ -49,6 +49,10 @@ bool llvm::isSafeToDestroyConstant(const Constant *C) { static bool analyzeGlobalAux(const Value *V, GlobalStatus &GS, SmallPtrSetImpl &PhiUsers) { + if (const GlobalVariable *GV = dyn_cast(V)) + if (GV->isExternallyInitialized()) + GS.StoredType = GlobalStatus::StoredOnce; + for (const Use &U : V->uses()) { const User *UR = U.getUser(); if (const ConstantExpr *CE = dyn_cast(UR)) { diff --git a/llvm/test/Transforms/GlobalOpt/externally-initialized.ll b/llvm/test/Transforms/GlobalOpt/externally-initialized.ll new file mode 100644 index 000000000000..c01ba10f49c9 --- /dev/null +++ b/llvm/test/Transforms/GlobalOpt/externally-initialized.ll @@ -0,0 +1,37 @@ +; RUN: opt < %s -S -globalopt | FileCheck %s + +; This global is externally_initialized, which may modify the value between +; it's static initializer and any code in this module being run, so the only +; write to it cannot be merged into the static initialiser. +; CHECK: @a = internal unnamed_addr externally_initialized global i32 undef +@a = internal externally_initialized global i32 undef + +; This global is stored to by the external initialization, so cannot be +; constant-propagated and removed, despite the fact that there are no writes +; to it. +; CHECK: @b = internal unnamed_addr externally_initialized global i32 undef +@b = internal externally_initialized global i32 undef + + +define void @foo() { +; CHECK-LABEL: foo +entry: +; CHECK: store i32 42, i32* @a + store i32 42, i32* @a + ret void +} +define i32 @bar() { +; CHECK-LABEL: bar +entry: +; CHECK: %val = load i32, i32* @a + %val = load i32, i32* @a + ret i32 %val +} + +define i32 @baz() { +; CHECK-LABEL: baz +entry: +; CHECK: %val = load i32, i32* @b + %val = load i32, i32* @b + ret i32 %val +}