fix PR8710 - teach global opt that some constantexprs are too complex to

put in a global variable's initializer.


git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@121100 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Chris Lattner 2010-12-07 01:59:32 +00:00
parent 1fe045279b
commit fb431099c5
2 changed files with 108 additions and 5 deletions

View File

@ -2046,14 +2046,82 @@ static GlobalVariable *InstallGlobalCtors(GlobalVariable *GCL,
}
static Constant *getVal(DenseMap<Value*, Constant*> &ComputedValues,
Value *V) {
static Constant *getVal(DenseMap<Value*, Constant*> &ComputedValues, Value *V) {
if (Constant *CV = dyn_cast<Constant>(V)) return CV;
Constant *R = ComputedValues[V];
assert(R && "Reference to an uncomputed value!");
return R;
}
static inline bool
isSimpleEnoughValueToCommit(Constant *C,
SmallPtrSet<Constant*, 8> &SimpleConstants);
/// isSimpleEnoughValueToCommit - Return true if the specified constant can be
/// handled by the code generator. We don't want to generate something like:
/// void *X = &X/42;
/// because the code generator doesn't have a relocation that can handle that.
///
/// This function should be called if C was not found (but just got inserted)
/// in SimpleConstants to avoid having to rescan the same constants all the
/// time.
static bool isSimpleEnoughValueToCommitHelper(Constant *C,
SmallPtrSet<Constant*, 8> &SimpleConstants) {
// Simple integer, undef, constant aggregate zero, global addresses, etc are
// all supported.
if (C->getNumOperands() == 0 || isa<BlockAddress>(C) ||
isa<GlobalValue>(C))
return true;
// Aggregate values are safe if all their elements are.
if (isa<ConstantArray>(C) || isa<ConstantStruct>(C) ||
isa<ConstantVector>(C)) {
for (unsigned i = 0, e = C->getNumOperands(); i != e; ++i) {
Constant *Op = cast<Constant>(C->getOperand(i));
if (!isSimpleEnoughValueToCommit(Op, SimpleConstants))
return false;
}
return true;
}
// We don't know exactly what relocations are allowed in constant expressions,
// so we allow &global+constantoffset, which is safe and uniformly supported
// across targets.
ConstantExpr *CE = cast<ConstantExpr>(C);
switch (CE->getOpcode()) {
case Instruction::BitCast:
case Instruction::IntToPtr:
case Instruction::PtrToInt:
// These casts are always fine if the casted value is.
return isSimpleEnoughValueToCommit(CE->getOperand(0), SimpleConstants);
// GEP is fine if it is simple + constant offset.
case Instruction::GetElementPtr:
for (unsigned i = 1, e = CE->getNumOperands(); i != e; ++i)
if (!isa<ConstantInt>(CE->getOperand(i)))
return false;
return isSimpleEnoughValueToCommit(CE->getOperand(0), SimpleConstants);
case Instruction::Add:
// We allow simple+cst.
if (!isa<ConstantInt>(CE->getOperand(1)))
return false;
return isSimpleEnoughValueToCommit(CE->getOperand(0), SimpleConstants);
}
return false;
}
static inline bool
isSimpleEnoughValueToCommit(Constant *C,
SmallPtrSet<Constant*, 8> &SimpleConstants) {
// If we already checked this constant, we win.
if (!SimpleConstants.insert(C)) return true;
// Check the constant.
return isSimpleEnoughValueToCommitHelper(C, SimpleConstants);
}
/// isSimpleEnoughPointerToCommit - Return true if this constant is simple
/// enough for us to understand. In particular, if it is a cast of something,
/// we punt. We basically just support direct accesses to globals and GEP's of
@ -2219,7 +2287,8 @@ static bool EvaluateFunction(Function *F, Constant *&RetVal,
const SmallVectorImpl<Constant*> &ActualArgs,
std::vector<Function*> &CallStack,
DenseMap<Constant*, Constant*> &MutatedMemory,
std::vector<GlobalVariable*> &AllocaTmps) {
std::vector<GlobalVariable*> &AllocaTmps,
SmallPtrSet<Constant*, 8> &SimpleConstants) {
// Check to see if this function is already executing (recursion). If so,
// bail out. TODO: we might want to accept limited recursion.
if (std::find(CallStack.begin(), CallStack.end(), F) != CallStack.end())
@ -2254,7 +2323,13 @@ static bool EvaluateFunction(Function *F, Constant *&RetVal,
if (!isSimpleEnoughPointerToCommit(Ptr))
// If this is too complex for us to commit, reject it.
return false;
Constant *Val = getVal(Values, SI->getOperand(0));
// If this might be too difficult for the backend to handle (e.g. the addr
// of one global variable divided by another) then we can't commit it.
if (!isSimpleEnoughValueToCommit(Val, SimpleConstants))
return false;
MutatedMemory[Ptr] = Val;
} else if (BinaryOperator *BO = dyn_cast<BinaryOperator>(CurInst)) {
InstResult = ConstantExpr::get(BO->getOpcode(),
@ -2331,7 +2406,7 @@ static bool EvaluateFunction(Function *F, Constant *&RetVal,
Constant *RetVal;
// Execute the call, if successful, use the return value.
if (!EvaluateFunction(Callee, RetVal, Formals, CallStack,
MutatedMemory, AllocaTmps))
MutatedMemory, AllocaTmps, SimpleConstants))
return false;
InstResult = RetVal;
}
@ -2417,11 +2492,16 @@ static bool EvaluateStaticConstructor(Function *F) {
/// unbounded.
std::vector<Function*> CallStack;
/// SimpleConstants - These are constants we have checked and know to be
/// simple enough to live in a static initializer of a global.
SmallPtrSet<Constant*, 8> SimpleConstants;
// Call the function.
Constant *RetValDummy;
bool EvalSuccess = EvaluateFunction(F, RetValDummy,
SmallVector<Constant*, 0>(), CallStack,
MutatedMemory, AllocaTmps);
MutatedMemory, AllocaTmps,
SimpleConstants);
if (EvalSuccess) {
// We succeeded at evaluation: commit the result.
DEBUG(dbgs() << "FULLY EVALUATED GLOBAL CTOR FUNCTION '"

View File

@ -0,0 +1,23 @@
; RUN: opt -globalopt %s -S | FileCheck %s
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"
target triple = "x86_64-apple-darwin10.0.0"
%0 = type { i32, void ()* }
%struct.foo = type { i32* }
@G = global i32 0, align 4
@H = global i32 0, align 4
@X = global %struct.foo zeroinitializer, align 8
@llvm.global_ctors = appending global [1 x %0] [%0 { i32 65535, void ()* @init }]
; PR8710 - GlobalOpt shouldn't change the global's initializer to have this
; arbitrary constant expression, the code generator can't handle it.
define internal void @init() {
entry:
%tmp = getelementptr inbounds %struct.foo* @X, i32 0, i32 0
store i32* inttoptr (i64 sdiv (i64 ptrtoint (i32* @G to i64), i64 ptrtoint (i32* @H to i64)) to i32*), i32** %tmp, align 8
ret void
}
; CHECK: @init
; CHECK: store i32*