forked from OSchip/llvm-project
Rework constant expr and array handling for objectsize instcombining.
Fix bugs where we would compute out of bounds as in bounds, and where we couldn't know that the linker could override the size of an array. Add a few new testcases, change existing testcase to use a private global array instead of extern. llvm-svn: 95283
This commit is contained in:
parent
01676f9ff4
commit
42fa84a880
|
@ -635,43 +635,69 @@ Instruction *InstCombiner::visitCallInst(CallInst &CI) {
|
||||||
case Intrinsic::objectsize: {
|
case Intrinsic::objectsize: {
|
||||||
const Type *ReturnTy = CI.getType();
|
const Type *ReturnTy = CI.getType();
|
||||||
Value *Op1 = II->getOperand(1);
|
Value *Op1 = II->getOperand(1);
|
||||||
|
|
||||||
|
// If we've got a GEP we're going to do some calculations
|
||||||
|
size_t GEPindex = 0;
|
||||||
|
|
||||||
// If we're a constant expr then we just return the number of bytes
|
// Strip any casts we see and continue processing.
|
||||||
// left in whatever we're indexing. Since it's constant there's no
|
Op1 = Op1->stripPointerCasts();
|
||||||
// need for maximum or minimum bytes.
|
|
||||||
|
// Make sure we can reliably know the size.
|
||||||
|
if (GlobalVariable *GV = dyn_cast<GlobalVariable>(Op1))
|
||||||
|
if (!GV->hasDefinitiveInitializer()) break;
|
||||||
|
|
||||||
if (ConstantExpr *CE = dyn_cast<ConstantExpr>(Op1)) {
|
if (ConstantExpr *CE = dyn_cast<ConstantExpr>(Op1)) {
|
||||||
// If this isn't a GEP give up.
|
// If this isn't a GEP give up.
|
||||||
if (CE->getOpcode() != Instruction::GetElementPtr) return 0;
|
if (CE->getOpcode() != Instruction::GetElementPtr) break;
|
||||||
|
|
||||||
const PointerType *ObjTy =
|
// If this isn't guaranteed to be inbounds, give up.
|
||||||
reinterpret_cast<const PointerType*>(CE->getOperand(0)->getType());
|
bool OOB = false;
|
||||||
|
GEPOperator *GEPO = cast<GEPOperator>(Op1);
|
||||||
|
if (!GEPO->isInBounds()) OOB = true;
|
||||||
|
|
||||||
|
for (int i = GEPO->getNumIndices() - 1; i > 0; i--) {
|
||||||
|
if (Constant *C = dyn_cast<Constant>(GEPO->getOperand(i)))
|
||||||
|
if (C->isNullValue())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
OOB = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're guaranteed to be out of bounds just return that there's
|
||||||
|
// no room left.
|
||||||
|
if (OOB) return ReplaceInstUsesWith(CI, ConstantInt::get(ReturnTy, 0));
|
||||||
|
|
||||||
if (const ArrayType *AT = dyn_cast<ArrayType>(ObjTy->getElementType())) {
|
// Tell the later calculation that we have an offset and what
|
||||||
|
// it is.
|
||||||
|
Op1 = CE->getOperand(0);
|
||||||
|
ConstantInt *Const =
|
||||||
|
cast<ConstantInt>(CE->getOperand(CE->getNumOperands() - 1));
|
||||||
|
GEPindex = Const->getZExtValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This may be a pointer to an array. If we have an index from earlier
|
||||||
|
// use that too.
|
||||||
|
if (const PointerType *PT = dyn_cast<PointerType>(Op1->getType())) {
|
||||||
|
if (const ArrayType *AT = dyn_cast<ArrayType>(PT->getElementType())) {
|
||||||
|
|
||||||
// Deal with multi-dimensional arrays
|
// Deal with multi-dimensional arrays
|
||||||
const ArrayType *SAT = AT;
|
const ArrayType *SAT = AT;
|
||||||
while ((AT = dyn_cast<ArrayType>(AT->getElementType())))
|
while ((AT = dyn_cast<ArrayType>(AT->getElementType())))
|
||||||
SAT = AT;
|
SAT = AT;
|
||||||
|
|
||||||
size_t numElems = SAT->getNumElements();
|
|
||||||
|
|
||||||
// If numElems is 0, we don't know how large the array is so we can't
|
|
||||||
// make any determinations yet.
|
|
||||||
if (numElems == 0) break;
|
|
||||||
|
|
||||||
// We return the remaining bytes, so grab the size of an element
|
// We return the remaining bytes, so grab the size of an element
|
||||||
// in bytes.
|
// in bytes and the number of elements.
|
||||||
size_t sizeofElem = SAT->getElementType()->getPrimitiveSizeInBits() / 8;
|
if (!SAT->isSized() || !TD) break;
|
||||||
|
|
||||||
ConstantInt *Const =
|
size_t sizeofElem = TD->getTypeAllocSize(SAT->getElementType());
|
||||||
cast<ConstantInt>(CE->getOperand(CE->getNumOperands() - 1));
|
size_t numElems = SAT->getNumElements();
|
||||||
size_t indx = Const->getZExtValue();
|
size_t remSize = (numElems - GEPindex) * sizeofElem;
|
||||||
return ReplaceInstUsesWith(CI,
|
return ReplaceInstUsesWith(CI, ConstantInt::get(ReturnTy, remSize));
|
||||||
ConstantInt::get(ReturnTy,
|
|
||||||
((numElems - indx) * sizeofElem)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Add more types here.
|
// TODO: Add more types here.
|
||||||
|
// TODO: Check for type isSized() here as well.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
|
; Test a pile of objectsize bounds checking.
|
||||||
; RUN: opt < %s -instcombine -S | FileCheck %s
|
; RUN: opt < %s -instcombine -S | FileCheck %s
|
||||||
@a = common global [60 x i8] zeroinitializer, align 1 ; <[60 x i8]*>
|
; We need target data to get the sizes of the arrays and structures.
|
||||||
|
target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:128:128"
|
||||||
|
|
||||||
|
@a = private global [60 x i8] zeroinitializer, align 1 ; <[60 x i8]*>
|
||||||
@.str = private constant [8 x i8] c"abcdefg\00" ; <[8 x i8]*>
|
@.str = private constant [8 x i8] c"abcdefg\00" ; <[8 x i8]*>
|
||||||
|
|
||||||
define i32 @foo() nounwind {
|
define i32 @foo() nounwind {
|
||||||
|
@ -27,6 +31,13 @@ cond.false:
|
||||||
ret i8* %2;
|
ret i8* %2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
define i32 @f() nounwind {
|
||||||
|
; CHECK: @f
|
||||||
|
; CHECK-NEXT: ret i32 0
|
||||||
|
%1 = call i32 @llvm.objectsize.i32(i8* getelementptr ([60 x i8]* @a, i32 1, i32 0), i1 false)
|
||||||
|
ret i32 %1
|
||||||
|
}
|
||||||
|
|
||||||
@window = external global [0 x i8]
|
@window = external global [0 x i8]
|
||||||
|
|
||||||
define i1 @baz() nounwind {
|
define i1 @baz() nounwind {
|
||||||
|
@ -37,5 +48,4 @@ define i1 @baz() nounwind {
|
||||||
ret i1 %2
|
ret i1 %2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
declare i32 @llvm.objectsize.i32(i8*, i1) nounwind readonly
|
declare i32 @llvm.objectsize.i32(i8*, i1) nounwind readonly
|
Loading…
Reference in New Issue