-#define GET_VALUE(Val, Int) \
- if (!Val) \
- Val = ConstantInt::get(IntTy, Int)
-
-#define RETURN(Val) \
- do { ReturnVal = Val; goto cache_and_return; } while (0)
-
-/// computeAllocSize - compute the object size and the offset within the object
-/// pointed by Ptr. OffsetValue/SizeValue will be null if they are constant, and
-/// therefore the result is given in Offset/Size variables instead.
-/// Returns true if the offset and size could be computed within the given
-/// maximum run-time penalty.
-bool BoundsChecking::computeAllocSize(Value *Ptr, APInt &Offset,
- Value* &OffsetValue, APInt &Size,
- Value* &SizeValue) {
- Ptr = Ptr->stripPointerCasts();
-
- // lookup to see if we've seen the Ptr before
- CacheMapTy::iterator CacheIt = CacheMap.find(Ptr);
- if (CacheIt != CacheMap.end()) {
- CacheData &Cache = CacheIt->second;
- Offset = Cache.Offset;
- OffsetValue = Cache.OffsetValue;
- Size = Cache.Size;
- SizeValue = Cache.SizeValue;
- return Cache.ReturnVal;
- }
-
- // record the pointers that were handled in this run, so that they can be
- // cleaned later if something fails
- SeenPtrs.insert(Ptr);
-
- IntegerType *IntTy = TD->getIntPtrType(Fn->getContext());
- unsigned IntTyBits = IntTy->getBitWidth();
- bool ReturnVal;
-
- // always generate code immediately before the instruction being processed, so
- // that the generated code dominates the same BBs
- Instruction *PrevInsertPoint = Builder->GetInsertPoint();
- if (Instruction *I = dyn_cast<Instruction>(Ptr))
- Builder->SetInsertPoint(I);
-
- // initalize with "don't know" state: offset=0 and size=uintmax
- Offset = 0;
- Size = APInt::getMaxValue(TD->getTypeSizeInBits(IntTy));
- OffsetValue = SizeValue = 0;
-
- if (GEPOperator *GEP = dyn_cast<GEPOperator>(Ptr)) {
- APInt PtrOffset(IntTyBits, 0);
- Value *PtrOffsetValue = 0;
- if (!computeAllocSize(GEP->getPointerOperand(), PtrOffset, PtrOffsetValue,
- Size, SizeValue))
- RETURN(false);
-
- if (GEP->hasAllConstantIndices()) {
- SmallVector<Value*, 8> Ops(GEP->idx_begin(), GEP->idx_end());
- Offset = TD->getIndexedOffset(GEP->getPointerOperandType(), Ops);
- // if PtrOffset is constant, return immediately
- if (!PtrOffsetValue) {
- Offset += PtrOffset;
- RETURN(true);
- }
- OffsetValue = ConstantInt::get(IntTy, Offset);
- } else if (Penalty > 1) {
- OffsetValue = EmitGEPOffset(Builder, *TD, GEP);
- GET_VALUE(PtrOffsetValue, PtrOffset);
- } else
- RETURN(false);
-
- OffsetValue = Builder->CreateAdd(PtrOffsetValue, OffsetValue);
- RETURN(true);
-
- // global variable with definitive size
- } else if (GlobalVariable *GV = dyn_cast<GlobalVariable>(Ptr)) {
- if (GV->hasDefinitiveInitializer()) {
- Constant *C = GV->getInitializer();
- Size = TD->getTypeAllocSize(C->getType());
- RETURN(true);
- }
- RETURN(false);
-
- // stack allocation
- } else if (AllocaInst *AI = dyn_cast<AllocaInst>(Ptr)) {
- if (!AI->getAllocatedType()->isSized())
- RETURN(false);
-
- Size = TD->getTypeAllocSize(AI->getAllocatedType());
- if (!AI->isArrayAllocation())
- RETURN(true); // we are done
-
- Value *ArraySize = AI->getArraySize();
- if (const ConstantInt *C = dyn_cast<ConstantInt>(ArraySize)) {
- Size *= C->getValue();
- RETURN(true);
- }
-
- if (Penalty < 2)
- RETURN(false);
-
- // VLA: compute size dynamically
- SizeValue = ConstantInt::get(ArraySize->getType(), Size);
- SizeValue = Builder->CreateMul(SizeValue, ArraySize);
- RETURN(true);
-
- // function arguments
- } else if (Argument *A = dyn_cast<Argument>(Ptr)) {
- // right now we only support byval arguments, so that no interprocedural
- // analysis is necessary
- if (!A->hasByValAttr()) {
- ++ChecksUnableInterproc;
- RETURN(false);
- }
-
- PointerType *PT = cast<PointerType>(A->getType());
- Size = TD->getTypeAllocSize(PT->getElementType());
- RETURN(true);
-
- // ptr = select(ptr1, ptr2)
- } else if (SelectInst *SI = dyn_cast<SelectInst>(Ptr)) {
- APInt OffsetTrue(IntTyBits, 0), OffsetFalse(IntTyBits, 0);
- APInt SizeTrue(IntTyBits, 0), SizeFalse(IntTyBits, 0);
- Value *OffsetValueTrue = 0, *OffsetValueFalse = 0;
- Value *SizeValueTrue = 0, *SizeValueFalse = 0;
-
- bool TrueAlloc = computeAllocSize(SI->getTrueValue(), OffsetTrue,
- OffsetValueTrue, SizeTrue, SizeValueTrue);
- bool FalseAlloc = computeAllocSize(SI->getFalseValue(), OffsetFalse,
- OffsetValueFalse, SizeFalse,
- SizeValueFalse);
- if (!TrueAlloc || !FalseAlloc)
- RETURN(false);
-
- // fold constant sizes & offsets if they are equal
- if (!OffsetValueTrue && !OffsetValueFalse && OffsetTrue == OffsetFalse)
- Offset = OffsetTrue;
- else if (Penalty > 1) {
- GET_VALUE(OffsetValueTrue, OffsetTrue);
- GET_VALUE(OffsetValueFalse, OffsetFalse);
- OffsetValue = Builder->CreateSelect(SI->getCondition(), OffsetValueTrue,
- OffsetValueFalse);
- } else
- RETURN(false);
-
- if (!SizeValueTrue && !SizeValueFalse && SizeTrue == SizeFalse)
- Size = SizeTrue;
- else if (Penalty > 1) {
- GET_VALUE(SizeValueTrue, SizeTrue);
- GET_VALUE(SizeValueFalse, SizeFalse);
- SizeValue = Builder->CreateSelect(SI->getCondition(), SizeValueTrue,
- SizeValueFalse);
- } else
- RETURN(false);
- RETURN(true);
-
- // call allocation function
- } else if (CallInst *CI = dyn_cast<CallInst>(Ptr)) {
- SmallVector<unsigned, 4> Args;
-
- if (MDNode *MD = CI->getMetadata("alloc_size")) {
- for (unsigned i = 0, e = MD->getNumOperands(); i != e; ++i)
- Args.push_back(cast<ConstantInt>(MD->getOperand(i))->getZExtValue());
-
- } else if (Function *Callee = CI->getCalledFunction()) {
- FunctionType *FTy = Callee->getFunctionType();
-
- // alloc(size)
- if (FTy->getNumParams() == 1 && FTy->getParamType(0)->isIntegerTy()) {
- if ((Callee->getName() == "malloc" ||
- Callee->getName() == "valloc" ||
- Callee->getName() == "_Znwj" || // operator new(unsigned int)
- Callee->getName() == "_Znwm" || // operator new(unsigned long)
- Callee->getName() == "_Znaj" || // operator new[](unsigned int)
- Callee->getName() == "_Znam")) {
- Args.push_back(0);
- }
- } else if (FTy->getNumParams() == 2) {
- // alloc(_, x)
- if (FTy->getParamType(1)->isIntegerTy() &&
- ((Callee->getName() == "realloc" ||
- Callee->getName() == "reallocf"))) {
- Args.push_back(1);
-
- // alloc(x, y)
- } else if (FTy->getParamType(0)->isIntegerTy() &&
- FTy->getParamType(1)->isIntegerTy() &&
- Callee->getName() == "calloc") {
- Args.push_back(0);
- Args.push_back(1);
- }
- } else if (FTy->getNumParams() == 3) {
- // alloc(_, _, x)
- if (FTy->getParamType(2)->isIntegerTy() &&
- Callee->getName() == "posix_memalign") {
- Args.push_back(2);
- }
- }
- }
-
- if (Args.empty())
- RETURN(false);
-
- // check if all arguments are constant. if so, the object size is also const
- bool AllConst = true;
- for (SmallVectorImpl<unsigned>::iterator I = Args.begin(), E = Args.end();
- I != E; ++I) {
- if (!isa<ConstantInt>(CI->getArgOperand(*I))) {
- AllConst = false;
- break;
- }
- }
-
- if (AllConst) {
- Size = 1;
- for (SmallVectorImpl<unsigned>::iterator I = Args.begin(), E = Args.end();
- I != E; ++I) {
- ConstantInt *Arg = cast<ConstantInt>(CI->getArgOperand(*I));
- Size *= Arg->getValue().zextOrSelf(IntTyBits);
- }
- RETURN(true);
- }
-
- if (Penalty < 2)
- RETURN(false);
-
- // not all arguments are constant, so create a sequence of multiplications
- for (SmallVectorImpl<unsigned>::iterator I = Args.begin(), E = Args.end();
- I != E; ++I) {
- Value *Arg = Builder->CreateZExt(CI->getArgOperand(*I), IntTy);
- if (!SizeValue) {
- SizeValue = Arg;
- continue;
- }
- SizeValue = Builder->CreateMul(SizeValue, Arg);
- }
- RETURN(true);
-
- // TODO: handle more standard functions (+ wchar cousins):
- // - strdup / strndup
- // - strcpy / strncpy
- // - strcat / strncat
- // - memcpy / memmove
- // - strcat / strncat
- // - memset
-
- } else if (PHINode *PHI = dyn_cast<PHINode>(Ptr)) {
- // create 2 PHIs: one for offset and another for size
- PHINode *OffsetPHI = Builder->CreatePHI(IntTy, PHI->getNumIncomingValues());
- PHINode *SizePHI = Builder->CreatePHI(IntTy, PHI->getNumIncomingValues());
-
- // insert right away in the cache to handle recursive PHIs
- CacheMap[Ptr] = CacheData(APInt(), OffsetPHI, APInt(), SizePHI, true);
-
- // compute offset/size for each PHI incoming pointer
- for (unsigned i = 0, e = PHI->getNumIncomingValues(); i != e; ++i) {
- Builder->SetInsertPoint(PHI->getIncomingBlock(i)->getFirstInsertionPt());
-
- APInt PhiOffset(IntTyBits, 0), PhiSize(IntTyBits, 0);
- Value *PhiOffsetValue = 0, *PhiSizeValue = 0;
-
- if (!computeAllocSize(PHI->getIncomingValue(i), PhiOffset, PhiOffsetValue,
- PhiSize, PhiSizeValue)) {
- OffsetPHI->replaceAllUsesWith(UndefValue::get(IntTy));
- OffsetPHI->eraseFromParent();
- SizePHI->replaceAllUsesWith(UndefValue::get(IntTy));
- SizePHI->eraseFromParent();
- RETURN(false);
- }
-
- GET_VALUE(PhiOffsetValue, PhiOffset);
- GET_VALUE(PhiSizeValue, PhiSize);
-
- OffsetPHI->addIncoming(PhiOffsetValue, PHI->getIncomingBlock(i));
- SizePHI->addIncoming(PhiSizeValue, PHI->getIncomingBlock(i));
- }
-
- OffsetValue = OffsetPHI;
- SizeValue = SizePHI;
- RETURN(true);
-
- } else if (isa<UndefValue>(Ptr) || isa<ConstantPointerNull>(Ptr)) {
- Size = 0;
- RETURN(true);
-
- } else if (isa<LoadInst>(Ptr)) {
- ++ChecksUnableLoad;
- RETURN(false);
- }
-
- DEBUG(dbgs() << "computeAllocSize unhandled value:\n" << *Ptr << "\n");
- RETURN(false);
-
-cache_and_return:
- // cache the result and return
- CacheMap[Ptr] = CacheData(Offset, OffsetValue, Size, SizeValue, ReturnVal);
-
- // non-computable results can be safely cached
- if (!ReturnVal)
- SeenPtrs.erase(Ptr);
-
- Builder->SetInsertPoint(PrevInsertPoint);
- return ReturnVal;
-}
-
-