objectsize:
authorNuno Lopes <nunoplopes@sapo.pt>
Wed, 9 May 2012 21:30:57 +0000 (21:30 +0000)
committerNuno Lopes <nunoplopes@sapo.pt>
Wed, 9 May 2012 21:30:57 +0000 (21:30 +0000)
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

lib/Transforms/InstCombine/InstCombineCalls.cpp
test/Transforms/InstCombine/objsize.ll

index 5fdb57bc7aa0aaf3c607be5a1dd9f1bd4c6598a6..82487a020f11fd5767c18aae79f777fa4f9bb01e 100644 (file)
@@ -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<GlobalVariable>(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<AllocaInst>(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<ConstantInt>(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<ConstantInt>(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<ConstantInt>(Arg1)) {
+      if (ConstantInt *CI2 = dyn_cast<ConstantInt>(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<ConstantInt>(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<GEPOperator>(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<GlobalVariable>(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<GlobalVariable>(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<AllocaInst>(Op1)) {
-      // Get alloca size.
-      if (AI->getAllocatedType()->isSized()) {
-        Size = TD->getTypeAllocSize(AI->getAllocatedType());
-        if (AI->isArrayAllocation()) {
-          const ConstantInt *C = dyn_cast<ConstantInt>(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<ConstantInt>(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<ConstantInt>(Arg1))
-        if (ConstantInt *CI2 = dyn_cast<ConstantInt>(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
index 522e25f6f3313021cb76aa2c9816485c306095fc..524a28f614b0da538ade914c462b43d93df3a12f 100644 (file)
@@ -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
-}