diff --git a/llvm/lib/Transforms/IPO/FunctionSpecialization.cpp b/llvm/lib/Transforms/IPO/FunctionSpecialization.cpp --- a/llvm/lib/Transforms/IPO/FunctionSpecialization.cpp +++ b/llvm/lib/Transforms/IPO/FunctionSpecialization.cpp @@ -53,6 +53,7 @@ #include "llvm/Analysis/TargetTransformInfo.h" #include "llvm/Analysis/ValueLattice.h" #include "llvm/Analysis/ValueLatticeUtils.h" +#include "llvm/Analysis/ValueTracking.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/Transforms/Scalar/SCCP.h" #include "llvm/Transforms/Utils/Cloning.h" @@ -732,22 +733,19 @@ if (isa(V)) return nullptr; - // TrackValueOfGlobalVariable only tracks scalar global variables. - if (auto *GV = dyn_cast(V)) { - // Check if we want to specialize on the address of non-constant - // global values. - if (!GV->isConstant() && !SpecializeOnAddress) - return nullptr; - - if (!GV->getValueType()->isSingleValueType()) - return nullptr; - } - // Select for possible specialisation values that are constants or // are deduced to be constants or constant ranges with a single element. Constant *C = dyn_cast(V); if (!C) C = Solver.getConstantOrNull(V); + + // Don't specialize on (anything derived from) the address of a non-constant + // global variable, unless explicitly enabled. + if (C && C->getType()->isPointerTy() && !C->isNullValue()) + if (auto *GV = dyn_cast(getUnderlyingObject(C)); + GV && !(GV->isConstant() || SpecializeOnAddress)) + return nullptr; + return C; } diff --git a/llvm/test/Transforms/FunctionSpecialization/compiler-crash-promote-alloca.ll b/llvm/test/Transforms/FunctionSpecialization/compiler-crash-promote-alloca.ll --- a/llvm/test/Transforms/FunctionSpecialization/compiler-crash-promote-alloca.ll +++ b/llvm/test/Transforms/FunctionSpecialization/compiler-crash-promote-alloca.ll @@ -4,7 +4,7 @@ ; while attempting to promote the alloca in `entry`. ; CHECK: bar.1 -@block = internal global [8 x i8] zeroinitializer, align 1 +@block = internal constant [8 x i8] zeroinitializer, align 1 define dso_local void @entry() { %1 = alloca i32, align 4 diff --git a/llvm/test/Transforms/FunctionSpecialization/global-var-constants.ll b/llvm/test/Transforms/FunctionSpecialization/global-var-constants.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/FunctionSpecialization/global-var-constants.ll @@ -0,0 +1,78 @@ +; RUN: opt -S --passes=ipsccp,deadargelim --force-specialization < %s | FileCheck %s --check-prefix=NO-GLOBALS +; RUN: opt -S --passes=ipsccp,deadargelim --force-specialization --funcspec-on-address < %s | FileCheck %s --check-prefix=GLOBALS +@G = global [10 x i32] zeroinitializer, align 4 + +define internal i32 @f(ptr %p) noinline { +entry: + %0 = load i32, ptr %p, align 4 + store i32 0, ptr %p, align 4 + ret i32 %0 +} + +define internal i32 @g(i32 %x, i32 %y, ptr %p) noinline { +entry: + %cmp = icmp sgt i32 %x, %y + br i1 %cmp, label %if.then, label %if.else + +if.then: + br label %if.end + +if.else: + br label %if.end + +if.end: + %x.addr.0 = phi i32 [ %x, %if.then ], [ 11, %if.else ] + %p.addr.0 = phi ptr [ @G, %if.then ], [ %p, %if.else ] + %call = call i32 @f(ptr %p.addr.0) + %add = add nsw i32 %call, %x.addr.0 + ret i32 %add +} + +define i32 @h0(ptr %p) { +entry: + %call = call i32 @g(i32 2, i32 1, ptr %p) + ret i32 %call +} + +define i32 @h1() { +entry: + %call = call i32 @f(ptr @G) + ret i32 %call +} + +define i32 @h2() { +entry: + %call = call i32 @f(ptr getelementptr inbounds (i32, ptr @G, i64 1)) + ret i32 %call +} + +; Check if specialisation on the address of a non-const global variable +; is not allowed, then it is not performed. + +; NO-GLOBALS-LABEL: define internal i32 @g() +; NO-GLOBALS: call i32 @f(ptr @G) + +; NO-GLOBALS-LABEL: define i32 @h0(ptr %p) +; NO-GLOBALS:call i32 @g() + +; NO-GLOBALS-LABEL: define i32 @h1() +; NO-GLOBALS: call i32 @f(ptr @G) + +; NO-GLOBALS-LABEL: define i32 @h2() +; NO-GLOBALS: call i32 @f(ptr getelementptr inbounds (i32, ptr @G, i64 1)) + +; Check if specialisation on the address of a non-const global variable +; is allowed, then it is performed where possible. + +; GLOBALS-LABEL: define internal i32 @g() +; GLOBALS: call i32 @f.2() + +; GLOBALS-LABEL: define i32 @h0(ptr %p) +; GLOBALS: call i32 @g() + +; GLOBALS-LABEL: define i32 @h1() +; GLOBALS: call i32 @f.2() + +; GLOBALS-LABEL: define i32 @h2() +; GLOBALS: call i32 @f.1() +