The array-size operand to a new-expression is not necessarily a size_t.

It can be larger, it can be smaller, it can be signed, whatever.  Handle
all the crazy cases with grace and spirit.

llvm-svn: 131378
This commit is contained in:
John McCall 2011-05-15 07:14:44 +00:00
parent 8cd0a7e263
commit 036f2f6b35
3 changed files with 215 additions and 146 deletions

View File

@ -476,172 +476,242 @@ static CharUnits CalculateCookiePadding(CodeGenFunction &CGF,
return CGF.CGM.getCXXABI().GetArrayCookieSize(E);
}
static llvm::Value *EmitCXXNewAllocSize(ASTContext &Context,
CodeGenFunction &CGF,
const CXXNewExpr *E,
llvm::Value *&NumElements,
llvm::Value *&SizeWithoutCookie) {
QualType ElemType = E->getAllocatedType();
static llvm::Value *EmitCXXNewAllocSize(CodeGenFunction &CGF,
const CXXNewExpr *e,
llvm::Value *&numElements,
llvm::Value *&sizeWithoutCookie) {
QualType type = e->getAllocatedType();
const llvm::IntegerType *SizeTy =
cast<llvm::IntegerType>(CGF.ConvertType(CGF.getContext().getSizeType()));
CharUnits TypeSize = CGF.getContext().getTypeSizeInChars(ElemType);
if (!E->isArray()) {
SizeWithoutCookie = llvm::ConstantInt::get(SizeTy, TypeSize.getQuantity());
return SizeWithoutCookie;
if (!e->isArray()) {
CharUnits typeSize = CGF.getContext().getTypeSizeInChars(type);
sizeWithoutCookie
= llvm::ConstantInt::get(CGF.SizeTy, typeSize.getQuantity());
return sizeWithoutCookie;
}
// The width of size_t.
unsigned sizeWidth = CGF.SizeTy->getBitWidth();
// Figure out the cookie size.
CharUnits CookieSize = CalculateCookiePadding(CGF, E);
llvm::APInt cookieSize(sizeWidth,
CalculateCookiePadding(CGF, e).getQuantity());
// Emit the array size expression.
// We multiply the size of all dimensions for NumElements.
// e.g for 'int[2][3]', ElemType is 'int' and NumElements is 6.
NumElements = CGF.EmitScalarExpr(E->getArraySize());
assert(NumElements->getType() == SizeTy && "element count not a size_t");
numElements = CGF.EmitScalarExpr(e->getArraySize());
assert(isa<llvm::IntegerType>(numElements->getType()));
uint64_t ArraySizeMultiplier = 1;
// The number of elements can be have an arbitrary integer type;
// essentially, we need to multiply it by a constant factor, add a
// cookie size, and verify that the result is representable as a
// size_t. That's just a gloss, though, and it's wrong in one
// important way: if the count is negative, it's an error even if
// the cookie size would bring the total size >= 0.
bool isSigned = e->getArraySize()->getType()->isSignedIntegerType();
const llvm::IntegerType *numElementsType
= cast<llvm::IntegerType>(numElements->getType());
unsigned numElementsWidth = numElementsType->getBitWidth();
// Compute the constant factor.
llvm::APInt arraySizeMultiplier(sizeWidth, 1);
while (const ConstantArrayType *CAT
= CGF.getContext().getAsConstantArrayType(ElemType)) {
ElemType = CAT->getElementType();
ArraySizeMultiplier *= CAT->getSize().getZExtValue();
= CGF.getContext().getAsConstantArrayType(type)) {
type = CAT->getElementType();
arraySizeMultiplier *= CAT->getSize();
}
llvm::Value *Size;
CharUnits typeSize = CGF.getContext().getTypeSizeInChars(type);
llvm::APInt typeSizeMultiplier(sizeWidth, typeSize.getQuantity());
typeSizeMultiplier *= arraySizeMultiplier;
// This will be a size_t.
llvm::Value *size;
// If someone is doing 'new int[42]' there is no need to do a dynamic check.
// Don't bloat the -O0 code.
if (llvm::ConstantInt *NumElementsC =
dyn_cast<llvm::ConstantInt>(NumElements)) {
llvm::APInt NEC = NumElementsC->getValue();
unsigned SizeWidth = NEC.getBitWidth();
if (llvm::ConstantInt *numElementsC =
dyn_cast<llvm::ConstantInt>(numElements)) {
const llvm::APInt &count = numElementsC->getValue();
// Determine if there is an overflow here by doing an extended multiply.
NEC = NEC.zext(SizeWidth*2);
llvm::APInt SC(SizeWidth*2, TypeSize.getQuantity());
SC *= NEC;
bool hasAnyOverflow = false;
if (!CookieSize.isZero()) {
// Save the current size without a cookie. We don't care if an
// overflow's already happened because SizeWithoutCookie isn't
// used if the allocator returns null or throws, as it should
// always do on an overflow.
llvm::APInt SWC = SC.trunc(SizeWidth);
SizeWithoutCookie = llvm::ConstantInt::get(SizeTy, SWC);
// If 'count' was a negative number, it's an overflow.
if (isSigned && count.isNegative())
hasAnyOverflow = true;
// Add the cookie size.
SC += llvm::APInt(SizeWidth*2, CookieSize.getQuantity());
// We want to do all this arithmetic in size_t. If numElements is
// wider than that, check whether it's already too big, and if so,
// overflow.
else if (numElementsWidth > sizeWidth &&
numElementsWidth - sizeWidth > count.countLeadingZeros())
hasAnyOverflow = true;
// Okay, compute a count at the right width.
llvm::APInt adjustedCount = count.zextOrTrunc(sizeWidth);
// Scale numElements by that. This might overflow, but we don't
// care because it only overflows if allocationSize does, too, and
// if that overflows then we shouldn't use this.
numElements = llvm::ConstantInt::get(CGF.SizeTy,
adjustedCount * arraySizeMultiplier);
// Compute the size before cookie, and track whether it overflowed.
bool overflow;
llvm::APInt allocationSize
= adjustedCount.umul_ov(typeSizeMultiplier, overflow);
hasAnyOverflow |= overflow;
// Add in the cookie, and check whether it's overflowed.
if (cookieSize != 0) {
// Save the current size without a cookie. This shouldn't be
// used if there was overflow.
sizeWithoutCookie = llvm::ConstantInt::get(CGF.SizeTy, allocationSize);
allocationSize = allocationSize.uadd_ov(cookieSize, overflow);
hasAnyOverflow |= overflow;
}
// On overflow, produce a -1 so operator new will fail.
if (hasAnyOverflow) {
size = llvm::Constant::getAllOnesValue(CGF.SizeTy);
} else {
size = llvm::ConstantInt::get(CGF.SizeTy, allocationSize);
}
// Otherwise, we might need to use the overflow intrinsics.
} else {
// There are up to four conditions we need to test for:
// 1) if isSigned, we need to check whether numElements is negative;
// 2) if numElementsWidth > sizeWidth, we need to check whether
// numElements is larger than something representable in size_t;
// 3) we need to compute
// sizeWithoutCookie := numElements * typeSizeMultiplier
// and check whether it overflows; and
// 4) if we need a cookie, we need to compute
// size := sizeWithoutCookie + cookieSize
// and check whether it overflows.
llvm::Value *hasOverflow = 0;
// If numElementsWidth > sizeWidth, then one way or another, we're
// going to have to do a comparison for (2), and this happens to
// take care of (1), too.
if (numElementsWidth > sizeWidth) {
llvm::APInt threshold(numElementsWidth, 1);
threshold <<= sizeWidth;
llvm::Value *thresholdV
= llvm::ConstantInt::get(numElementsType, threshold);
hasOverflow = CGF.Builder.CreateICmpUGE(numElements, thresholdV);
numElements = CGF.Builder.CreateTrunc(numElements, CGF.SizeTy);
// Otherwise, if we're signed, we want to sext up to size_t.
} else if (isSigned) {
if (numElementsWidth < sizeWidth)
numElements = CGF.Builder.CreateSExt(numElements, CGF.SizeTy);
// If there's a non-1 type size multiplier, then we can do the
// signedness check at the same time as we do the multiply
// because a negative number times anything will cause an
// unsigned overflow. Otherwise, we have to do it here.
if (typeSizeMultiplier == 1)
hasOverflow = CGF.Builder.CreateICmpSLT(numElements,
llvm::ConstantInt::get(CGF.SizeTy, 0));
// Otherwise, zext up to size_t if necessary.
} else if (numElementsWidth < sizeWidth) {
numElements = CGF.Builder.CreateZExt(numElements, CGF.SizeTy);
}
assert(numElements->getType() == CGF.SizeTy);
size = numElements;
// Multiply by the type size if necessary. This multiplier
// includes all the factors for nested arrays.
//
// This step also causes numElements to be scaled up by the
// nested-array factor if necessary. Overflow on this computation
// can be ignored because the result shouldn't be used if
// allocation fails.
if (typeSizeMultiplier != 1) {
const llvm::Type *intrinsicTypes[] = { CGF.SizeTy };
llvm::Value *umul_with_overflow
= CGF.CGM.getIntrinsic(llvm::Intrinsic::umul_with_overflow,
intrinsicTypes, 1);
llvm::Value *tsmV =
llvm::ConstantInt::get(CGF.SizeTy, typeSizeMultiplier);
llvm::Value *result =
CGF.Builder.CreateCall2(umul_with_overflow, size, tsmV);
llvm::Value *overflowed = CGF.Builder.CreateExtractValue(result, 1);
if (hasOverflow)
hasOverflow = CGF.Builder.CreateOr(hasOverflow, overflowed);
else
hasOverflow = overflowed;
size = CGF.Builder.CreateExtractValue(result, 0);
// Also scale up numElements by the array size multiplier.
if (arraySizeMultiplier != 1) {
// If the base element type size is 1, then we can re-use the
// multiply we just did.
if (typeSize.isOne()) {
assert(arraySizeMultiplier == typeSizeMultiplier);
numElements = size;
// Otherwise we need a separate multiply.
} else {
llvm::Value *asmV =
llvm::ConstantInt::get(CGF.SizeTy, arraySizeMultiplier);
numElements = CGF.Builder.CreateMul(numElements, asmV);
}
}
} else {
// numElements doesn't need to be scaled.
assert(arraySizeMultiplier == 1);
}
if (SC.countLeadingZeros() >= SizeWidth) {
SC = SC.trunc(SizeWidth);
Size = llvm::ConstantInt::get(SizeTy, SC);
} else {
// On overflow, produce a -1 so operator new throws.
Size = llvm::Constant::getAllOnesValue(SizeTy);
// Add in the cookie size if necessary.
if (cookieSize != 0) {
sizeWithoutCookie = size;
const llvm::Type *intrinsicTypes[] = { CGF.SizeTy };
llvm::Value *uadd_with_overflow
= CGF.CGM.getIntrinsic(llvm::Intrinsic::uadd_with_overflow,
intrinsicTypes, 1);
llvm::Value *cookieSizeV = llvm::ConstantInt::get(CGF.SizeTy, cookieSize);
llvm::Value *result =
CGF.Builder.CreateCall2(uadd_with_overflow, size, cookieSizeV);
llvm::Value *overflowed = CGF.Builder.CreateExtractValue(result, 1);
if (hasOverflow)
hasOverflow = CGF.Builder.CreateOr(hasOverflow, overflowed);
else
hasOverflow = overflowed;
size = CGF.Builder.CreateExtractValue(result, 0);
}
// Scale NumElements while we're at it.
uint64_t N = NEC.getZExtValue() * ArraySizeMultiplier;
NumElements = llvm::ConstantInt::get(SizeTy, N);
// Otherwise, we don't need to do an overflow-checked multiplication if
// we're multiplying by one.
} else if (TypeSize.isOne()) {
assert(ArraySizeMultiplier == 1);
Size = NumElements;
// If we need a cookie, add its size in with an overflow check.
// This is maybe a little paranoid.
if (!CookieSize.isZero()) {
SizeWithoutCookie = Size;
llvm::Value *CookieSizeV
= llvm::ConstantInt::get(SizeTy, CookieSize.getQuantity());
const llvm::Type *Types[] = { SizeTy };
llvm::Value *UAddF
= CGF.CGM.getIntrinsic(llvm::Intrinsic::uadd_with_overflow, Types, 1);
llvm::Value *AddRes
= CGF.Builder.CreateCall2(UAddF, Size, CookieSizeV);
Size = CGF.Builder.CreateExtractValue(AddRes, 0);
llvm::Value *DidOverflow = CGF.Builder.CreateExtractValue(AddRes, 1);
Size = CGF.Builder.CreateSelect(DidOverflow,
llvm::ConstantInt::get(SizeTy, -1),
Size);
}
// Otherwise use the int.umul.with.overflow intrinsic.
} else {
llvm::Value *OutermostElementSize
= llvm::ConstantInt::get(SizeTy, TypeSize.getQuantity());
llvm::Value *NumOutermostElements = NumElements;
// Scale NumElements by the array size multiplier. This might
// overflow, but only if the multiplication below also overflows,
// in which case this multiplication isn't used.
if (ArraySizeMultiplier != 1)
NumElements = CGF.Builder.CreateMul(NumElements,
llvm::ConstantInt::get(SizeTy, ArraySizeMultiplier));
// The requested size of the outermost array is non-constant.
// Multiply that by the static size of the elements of that array;
// on unsigned overflow, set the size to -1 to trigger an
// exception from the allocation routine. This is sufficient to
// prevent buffer overruns from the allocator returning a
// seemingly valid pointer to insufficient space. This idea comes
// originally from MSVC, and GCC has an open bug requesting
// similar behavior:
// http://gcc.gnu.org/bugzilla/show_bug.cgi?id=19351
//
// This will not be sufficient for C++0x, which requires a
// specific exception class (std::bad_array_new_length).
// That will require ABI support that has not yet been specified.
const llvm::Type *Types[] = { SizeTy };
llvm::Value *UMulF
= CGF.CGM.getIntrinsic(llvm::Intrinsic::umul_with_overflow, Types, 1);
llvm::Value *MulRes = CGF.Builder.CreateCall2(UMulF, NumOutermostElements,
OutermostElementSize);
// The overflow bit.
llvm::Value *DidOverflow = CGF.Builder.CreateExtractValue(MulRes, 1);
// The result of the multiplication.
Size = CGF.Builder.CreateExtractValue(MulRes, 0);
// If we have a cookie, we need to add that size in, too.
if (!CookieSize.isZero()) {
SizeWithoutCookie = Size;
llvm::Value *CookieSizeV
= llvm::ConstantInt::get(SizeTy, CookieSize.getQuantity());
llvm::Value *UAddF
= CGF.CGM.getIntrinsic(llvm::Intrinsic::uadd_with_overflow, Types, 1);
llvm::Value *AddRes
= CGF.Builder.CreateCall2(UAddF, SizeWithoutCookie, CookieSizeV);
Size = CGF.Builder.CreateExtractValue(AddRes, 0);
llvm::Value *AddDidOverflow = CGF.Builder.CreateExtractValue(AddRes, 1);
DidOverflow = CGF.Builder.CreateOr(DidOverflow, AddDidOverflow);
}
Size = CGF.Builder.CreateSelect(DidOverflow,
llvm::ConstantInt::get(SizeTy, -1),
Size);
// If we had any possibility of dynamic overflow, make a select to
// overwrite 'size' with an all-ones value, which should cause
// operator new to throw.
if (hasOverflow)
size = CGF.Builder.CreateSelect(hasOverflow,
llvm::Constant::getAllOnesValue(CGF.SizeTy),
size);
}
if (CookieSize.isZero())
SizeWithoutCookie = Size;
if (cookieSize == 0)
sizeWithoutCookie = size;
else
assert(SizeWithoutCookie && "didn't set SizeWithoutCookie?");
assert(sizeWithoutCookie && "didn't set sizeWithoutCookie?");
return Size;
return size;
}
static void StoreAnyExprIntoOneUnit(CodeGenFunction &CGF, const CXXNewExpr *E,
@ -969,8 +1039,7 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) {
llvm::Value *numElements = 0;
llvm::Value *allocSizeWithoutCookie = 0;
llvm::Value *allocSize =
EmitCXXNewAllocSize(getContext(), *this, E, numElements,
allocSizeWithoutCookie);
EmitCXXNewAllocSize(*this, E, numElements, allocSizeWithoutCookie);
allocatorArgs.add(RValue::get(allocSize), sizeType);

View File

@ -951,8 +951,8 @@ Sema::BuildCXXNew(SourceLocation StartLoc, bool UseGlobal,
}
}
ArraySize = ImpCastExprToType(ArraySize, Context.getSizeType(),
CK_IntegralCast).take();
// Note that we do *not* convert the argument in any way. It can
// be signed, larger than size_t, whatever.
}
FunctionDecl *OperatorNew = 0;

View File

@ -136,8 +136,8 @@ namespace test3 {
void d(int n) {
// CHECK: define void @_ZN5test31dEi(
// CHECK: [[N:%.*]] = load i32*
// CHECK: [[NE:%.*]] = mul i32 [[N]], 20
// CHECK: @llvm.umul.with.overflow.i32(i32 [[N]], i32 80)
// CHECK: [[NE:%.*]] = mul i32 [[N]], 20
// CHECK: @llvm.uadd.with.overflow.i32(i32 {{.*}}, i32 8)
// CHECK: [[SZ:%.*]] = select
// CHECK: call noalias i8* @_Znam(i32 [[SZ]])
@ -208,8 +208,8 @@ namespace test4 {
void d(int n) {
// CHECK: define void @_ZN5test41dEi(
// CHECK: [[N:%.*]] = load i32*
// CHECK: [[NE:%.*]] = mul i32 [[N]], 20
// CHECK: @llvm.umul.with.overflow.i32(i32 [[N]], i32 80)
// CHECK: [[NE:%.*]] = mul i32 [[N]], 20
// CHECK: @llvm.uadd.with.overflow.i32(i32 {{.*}}, i32 8)
// CHECK: [[SZ:%.*]] = select
// CHECK: call noalias i8* @_Znam(i32 [[SZ]])