From e3305b17502c2a34152d4f50607b685eb2cadd21 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Wed, 9 May 2012 21:30:57 +0000 Subject: [PATCH] objectsize: refactor code a bit to enable future changes to support run-time information add support to compute allocation sizes at run-time if penalty > 1 (e.g., malloc(x), calloc(x, y), and VLAs) git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@156515 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../InstCombine/InstCombineCalls.cpp | 151 +++++++++++------- test/Transforms/InstCombine/objsize.ll | 12 +- 2 files changed, 97 insertions(+), 66 deletions(-) diff --git a/lib/Transforms/InstCombine/InstCombineCalls.cpp b/lib/Transforms/InstCombine/InstCombineCalls.cpp index 5fdb57bc7aa..82487a020f1 100644 --- a/lib/Transforms/InstCombine/InstCombineCalls.cpp +++ b/lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -165,6 +165,72 @@ Instruction *InstCombiner::SimplifyMemSet(MemSetInst *MI) { return 0; } +/// computeAllocSize - compute the object size allocated by an allocation +/// site. Returns 0 if the size is not constant (in SizeValue), 1 if the size +/// is constant (in Size), and 2 if the size could not be determined within the +/// given maximum Penalty that the computation would incurr at run-time. +static int computeAllocSize(Value *Alloc, uint64_t &Size, Value* &SizeValue, + uint64_t Penalty, TargetData *TD, + InstCombiner::BuilderTy *Builder) { + if (GlobalVariable *GV = dyn_cast(Alloc)) { + if (GV->hasUniqueInitializer()) { + Constant *C = GV->getInitializer(); + Size = TD->getTypeAllocSize(C->getType()); + return 1; + } + // Can't determine size of the GV. + return 2; + + } else if (AllocaInst *AI = dyn_cast(Alloc)) { + if (!AI->getAllocatedType()->isSized()) + return 2; + + Size = TD->getTypeAllocSize(AI->getAllocatedType()); + if (!AI->isArrayAllocation()) + return 1; // we are done + + Value *ArraySize = AI->getArraySize(); + if (const ConstantInt *C = dyn_cast(ArraySize)) { + Size *= C->getZExtValue(); + return 1; + } + + if (Penalty < 2) + return 2; + + SizeValue = Builder->CreateMul(Builder->getInt64(Size), ArraySize); + return 0; + + } else if (CallInst *MI = extractMallocCall(Alloc)) { + SizeValue = MI->getArgOperand(0); + if (ConstantInt *CI = dyn_cast(SizeValue)) { + Size = CI->getZExtValue(); + return 1; + } + return 0; + + } else if (CallInst *MI = extractCallocCall(Alloc)) { + Value *Arg1 = MI->getArgOperand(0); + Value *Arg2 = MI->getArgOperand(1); + if (ConstantInt *CI1 = dyn_cast(Arg1)) { + if (ConstantInt *CI2 = dyn_cast(Arg2)) { + Size = (CI1->getValue() * CI2->getValue()).getZExtValue(); + return 1; + } + } + + if (Penalty < 2) + return 2; + + SizeValue = Builder->CreateMul(Arg1, Arg2); + return 0; + } + + DEBUG(errs() << "computeAllocSize failed:\n"); + DEBUG(Alloc->dump()); + return 2; +} + /// visitCallInst - CallInst simplification. This mostly only handles folding /// of intrinsic instructions. For normal calls, it allows visitCallSite to do /// the heavy lifting. @@ -250,13 +316,14 @@ Instruction *InstCombiner::visitCallInst(CallInst &CI) { if (!TD) return 0; Type *ReturnTy = CI.getType(); - uint64_t DontKnow = II->getArgOperand(1) == Builder->getTrue() ? 0 : -1ULL; + uint64_t Penalty = cast(II->getArgOperand(2))->getZExtValue(); // Get to the real allocated thing and offset as fast as possible. Value *Op1 = II->getArgOperand(0)->stripPointerCasts(); uint64_t Offset = 0; - uint64_t Size = -1ULL; + Value *OffsetValue; + bool ConstOffset = true; // Try to look through constant GEPs. if (GEPOperator *GEP = dyn_cast(Op1)) { @@ -270,66 +337,40 @@ Instruction *InstCombiner::visitCallInst(CallInst &CI) { Offset = TD->getIndexedOffset(GEP->getPointerOperandType(), Ops); Op1 = GEP->getPointerOperand()->stripPointerCasts(); - - // Make sure we're not a constant offset from an external - // global. - if (GlobalVariable *GV = dyn_cast(Op1)) - if (!GV->hasDefinitiveInitializer()) return 0; } - // If we've stripped down to a single global variable that we - // can know the size of then just return that. - if (GlobalVariable *GV = dyn_cast(Op1)) { - if (GV->hasDefinitiveInitializer()) { - Constant *C = GV->getInitializer(); - Size = TD->getTypeAllocSize(C->getType()); - } else { - // Can't determine size of the GV. - Constant *RetVal = ConstantInt::get(ReturnTy, DontKnow); - return ReplaceInstUsesWith(CI, RetVal); - } - } else if (AllocaInst *AI = dyn_cast(Op1)) { - // Get alloca size. - if (AI->getAllocatedType()->isSized()) { - Size = TD->getTypeAllocSize(AI->getAllocatedType()); - if (AI->isArrayAllocation()) { - const ConstantInt *C = dyn_cast(AI->getArraySize()); - if (!C) return 0; - Size *= C->getZExtValue(); - } - } - } else if (CallInst *MI = extractMallocCall(Op1)) { - // Get allocation size. - Value *Arg = MI->getArgOperand(0); - if (ConstantInt *CI = dyn_cast(Arg)) - Size = CI->getZExtValue(); - - } else if (CallInst *MI = extractCallocCall(Op1)) { - // Get allocation size. - Value *Arg1 = MI->getArgOperand(0); - Value *Arg2 = MI->getArgOperand(1); - if (ConstantInt *CI1 = dyn_cast(Arg1)) - if (ConstantInt *CI2 = dyn_cast(Arg2)) { - bool overflow; - APInt SizeAP = CI1->getValue().umul_ov(CI2->getValue(), overflow); - if (!overflow) - Size = SizeAP.getZExtValue(); - else - return ReplaceInstUsesWith(CI, ConstantInt::get(ReturnTy, DontKnow)); - } - } + uint64_t Size; + Value *SizeValue; + int ConstAlloc = computeAllocSize(Op1, Size, SizeValue, Penalty, TD, + Builder); // Do not return "I don't know" here. Later optimization passes could // make it possible to evaluate objectsize to a constant. - if (Size == -1ULL) + if (ConstAlloc == 2) return 0; - if (Size < Offset) { - // Out of bound reference? Negative index normalized to large - // index? Just return "I don't know". - return ReplaceInstUsesWith(CI, ConstantInt::get(ReturnTy, DontKnow)); - } - return ReplaceInstUsesWith(CI, ConstantInt::get(ReturnTy, Size-Offset)); + if (ConstOffset && ConstAlloc) { + if (Size < Offset) { + // Out of bounds + return ReplaceInstUsesWith(CI, ConstantInt::get(ReturnTy, 0)); + } + return ReplaceInstUsesWith(CI, ConstantInt::get(ReturnTy, Size-Offset)); + + } else if (Penalty >= 2) { + if (ConstOffset) + OffsetValue = Builder->getInt64(Offset); + if (ConstAlloc) + SizeValue = Builder->getInt64(Size); + + Value *Val = Builder->CreateSub(SizeValue, OffsetValue); + Val = Builder->CreateTrunc(Val, ReturnTy); + // return 0 if there's an overflow + Value *Cmp = Builder->CreateICmpULT(SizeValue, OffsetValue); + Val = Builder->CreateSelect(Cmp, ConstantInt::get(ReturnTy, 0), Val); + return ReplaceInstUsesWith(CI, Val); + + } else + return 0; } case Intrinsic::bswap: // bswap(bswap(x)) -> x diff --git a/test/Transforms/InstCombine/objsize.ll b/test/Transforms/InstCombine/objsize.ll index 522e25f6f33..524a28f614b 100644 --- a/test/Transforms/InstCombine/objsize.ll +++ b/test/Transforms/InstCombine/objsize.ll @@ -42,7 +42,7 @@ define i32 @f() nounwind { define i1 @baz() nounwind { ; CHECK: @baz -; CHECK-NEXT: ret i1 true +; CHECK-NEXT: objectsize %1 = tail call i32 @llvm.objectsize.i32(i8* getelementptr inbounds ([0 x i8]* @window, i32 0, i32 0), i1 false, i32 0) %2 = icmp eq i32 %1, -1 ret i1 %2 @@ -168,13 +168,3 @@ define i32 @test8() { ; CHECK-NEXT: ret i32 30 ret i32 %objsize } - -; test for overflow in calloc -define i32 @test9() { -; CHECK: @test9 - %alloc = call noalias i8* @calloc(i32 100000000, i32 100000000) nounwind - %gep = getelementptr inbounds i8* %alloc, i32 2 - %objsize = call i32 @llvm.objectsize.i32(i8* %gep, i1 true, i32 0) nounwind readonly -; CHECK-NEXT: ret i32 0 - ret i32 %objsize -} -- 2.34.1