diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -894,6 +894,11 @@ * No global value in the expression can be a declaration, since that would require a relocation, which is not possible. +* If either the alias or the aliasee may be replaced by a symbol outside the + module at link time or runtime, any optimization cannot replace the alias with + the aliasee, since the behavior may be different. The alias may be used as a + name guaranteed to point to the content in the current module. + .. _langref_ifunc: IFuncs 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 @@ -2253,6 +2253,13 @@ for (GlobalValue *GV : Used.used()) Used.compilerUsedErase(GV); + // Return whether GV is explicitly or implicitly dso_local and not replaceable + // by another definition in the current linkage unit. + auto IsModuleLocal = [](GlobalValue &GV) { + return !GlobalValue::isInterposableLinkage(GV.getLinkage()) && + (GV.isDSOLocal() || GV.isImplicitDSOLocal()); + }; + for (GlobalAlias &J : llvm::make_early_inc_range(M.aliases())) { // Aliases without names cannot be referenced outside this module. if (!J.hasName() && !J.isDeclaration() && !J.hasLocalLinkage()) @@ -2264,18 +2271,20 @@ } // If the alias can change at link time, nothing can be done - bail out. - if (J.isInterposable()) + if (!IsModuleLocal(J)) continue; Constant *Aliasee = J.getAliasee(); GlobalValue *Target = dyn_cast(Aliasee->stripPointerCasts()); // We can't trivially replace the alias with the aliasee if the aliasee is // non-trivial in some way. We also can't replace the alias with the aliasee - // if the aliasee is interposable because aliases point to the local - // definition. + // if the aliasee may be preemptible at runtime. On ELF, a non-preemptible + // alias can be used to access the definition as if preemption did not + // happen. // TODO: Try to handle non-zero GEPs of local aliasees. - if (!Target || Target->isInterposable()) + if (!Target || !IsModuleLocal(*Target)) continue; + Target->removeDeadConstantUsers(); // Make all users of the alias use the aliasee instead. diff --git a/llvm/test/Transforms/GlobalOpt/2009-02-15-ResolveAlias.ll b/llvm/test/Transforms/GlobalOpt/2009-02-15-ResolveAlias.ll --- a/llvm/test/Transforms/GlobalOpt/2009-02-15-ResolveAlias.ll +++ b/llvm/test/Transforms/GlobalOpt/2009-02-15-ResolveAlias.ll @@ -2,13 +2,13 @@ define internal void @f() { ; CHECK-NOT: @f( -; CHECK: define void @a +; CHECK: define dso_local void @a ret void } -@a = alias void (), void ()* @f +@a = dso_local alias void (), void ()* @f -define void @g() { +define hidden void @g() { call void() @a() ret void } diff --git a/llvm/test/Transforms/GlobalOpt/alias-resolve.ll b/llvm/test/Transforms/GlobalOpt/alias-resolve.ll --- a/llvm/test/Transforms/GlobalOpt/alias-resolve.ll +++ b/llvm/test/Transforms/GlobalOpt/alias-resolve.ll @@ -1,16 +1,19 @@ ; RUN: opt < %s -passes=globalopt -S | FileCheck %s @foo1 = alias void (), void ()* @foo2 +;; foo2 is dso_local and non-weak. Resolved. ; CHECK: @foo1 = alias void (), void ()* @bar2 -@foo2 = alias void(), void()* @bar1 -; CHECK: @foo2 = alias void (), void ()* @bar2 +@foo2 = dso_local alias void(), void()* @bar1 +;; bar1 is dso_local and non-weak. Resolved. +; CHECK: @foo2 = dso_local alias void (), void ()* @bar2 -@bar1 = alias void (), void ()* @bar2 -; CHECK: @bar1 = alias void (), void ()* @bar2 +@bar1 = dso_local alias void (), void ()* @bar2 +; CHECK: @bar1 = dso_local alias void (), void ()* @bar2 -@weak1 = weak alias void (), void ()* @bar2 -; CHECK: @weak1 = weak alias void (), void ()* @bar2 +@weak1 = weak dso_local alias void (), void ()* @bar2 +;; weak1 may be replaced with another definition in the linkage unit. Not resolved. +; CHECK: @weak1 = weak dso_local alias void (), void ()* @bar2 @bar4 = private unnamed_addr constant [2 x i8*] zeroinitializer @foo4 = weak_odr unnamed_addr alias i8*, getelementptr inbounds ([2 x i8*], [2 x i8*]* @bar4, i32 0, i32 1) @@ -19,10 +22,10 @@ @priva = private alias void (), void ()* @bar5 ; CHECK: @priva = private alias void (), void ()* @bar5 -define void @bar2() { +define dso_local void @bar2() { ret void } -; CHECK: define void @bar2() +; CHECK: define dso_local void @bar2() define weak void @bar5() { ret void @@ -32,27 +35,32 @@ define void @baz() { entry: call void @foo1() -; CHECK: call void @bar2() +;; foo1 is dso_preemptable. Not resolved. +; CHECK: call void @foo1() call void @foo2() +;; foo2 is dso_local and non-weak. Resolved. ; CHECK: call void @bar2() call void @bar1() +;; bar1 is dso_local and non-weak. Resolved. ; CHECK: call void @bar2() call void @weak1() +;; weak1 is weak. Not resolved. ; CHECK: call void @weak1() call void @priva() +;; priva has a local linkage. Resolved. ; CHECK: call void @priva() ret void } -@foo3 = alias void (), void ()* @bar3 +@foo3 = dso_local alias void (), void ()* @bar3 ; CHECK-NOT: bar3 define internal void @bar3() { ret void } -;CHECK: define void @foo3 +;CHECK: define dso_local void @foo3 diff --git a/llvm/test/Transforms/GlobalOpt/alias-used-address-space.ll b/llvm/test/Transforms/GlobalOpt/alias-used-address-space.ll --- a/llvm/test/Transforms/GlobalOpt/alias-used-address-space.ll +++ b/llvm/test/Transforms/GlobalOpt/alias-used-address-space.ll @@ -2,7 +2,7 @@ target datalayout = "p:32:32:32-p1:16:16:16" -@c = addrspace(1) global i8 42 +@c = hidden addrspace(1) global i8 42 @i = internal addrspace(1) global i8 42 diff --git a/llvm/test/Transforms/GlobalOpt/alias-used.ll b/llvm/test/Transforms/GlobalOpt/alias-used.ll --- a/llvm/test/Transforms/GlobalOpt/alias-used.ll +++ b/llvm/test/Transforms/GlobalOpt/alias-used.ll @@ -1,6 +1,6 @@ ; RUN: opt < %s -passes=globalopt -S | FileCheck %s -@c = global i8 42 +@c = dso_local global i8 42 @i = internal global i8 42 ; CHECK: @ia = internal global i8 42 @@ -30,7 +30,7 @@ @ca = internal alias i8, i8* @c ; CHECK: @ca = internal alias i8, i8* @c -define void @f() { +define hidden void @f() { ret void }