mirror of
				https://github.com/c64scene-ar/llvm-6502.git
				synced 2025-11-03 14:21:30 +00:00 
			
		
		
		
	git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@158210 91177308-0d34-0410-b5e6-96231b3b80d8
		
			
				
	
	
		
			569 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			569 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
//===- BoundsChecking.cpp - Instrumentation for run-time bounds checking --===//
 | 
						|
//
 | 
						|
//                     The LLVM Compiler Infrastructure
 | 
						|
//
 | 
						|
// This file is distributed under the University of Illinois Open Source
 | 
						|
// License. See LICENSE.TXT for details.
 | 
						|
//
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
//
 | 
						|
// This file implements a pass that instruments the code to perform run-time
 | 
						|
// bounds checking on loads, stores, and other memory intrinsics.
 | 
						|
//
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
 | 
						|
#define DEBUG_TYPE "bounds-checking"
 | 
						|
#include "llvm/Transforms/Scalar.h"
 | 
						|
#include "llvm/ADT/DenseMap.h"
 | 
						|
#include "llvm/ADT/Statistic.h"
 | 
						|
#include "llvm/Analysis/LoopInfo.h"
 | 
						|
#include "llvm/Analysis/ScalarEvolution.h"
 | 
						|
#include "llvm/Analysis/ScalarEvolutionExpander.h"
 | 
						|
#include "llvm/Analysis/ScalarEvolutionExpressions.h"
 | 
						|
#include "llvm/Support/CommandLine.h"
 | 
						|
#include "llvm/Support/Debug.h"
 | 
						|
#include "llvm/Support/InstIterator.h"
 | 
						|
#include "llvm/Support/IRBuilder.h"
 | 
						|
#include "llvm/Support/raw_ostream.h"
 | 
						|
#include "llvm/Support/TargetFolder.h"
 | 
						|
#include "llvm/Target/TargetData.h"
 | 
						|
#include "llvm/Transforms/Utils/Local.h"
 | 
						|
#include "llvm/GlobalVariable.h"
 | 
						|
#include "llvm/Instructions.h"
 | 
						|
#include "llvm/Intrinsics.h"
 | 
						|
#include "llvm/Metadata.h"
 | 
						|
#include "llvm/Operator.h"
 | 
						|
#include "llvm/Pass.h"
 | 
						|
using namespace llvm;
 | 
						|
 | 
						|
static cl::opt<bool> ManyTrapBB("bounds-checking-multiple-traps",
 | 
						|
                                cl::desc("Use one trap block per assertion"));
 | 
						|
 | 
						|
STATISTIC(ChecksAdded, "Bounds checks added");
 | 
						|
STATISTIC(ChecksSkipped, "Bounds checks skipped");
 | 
						|
STATISTIC(ChecksUnable, "Bounds checks unable to add");
 | 
						|
STATISTIC(ChecksUnableInterproc, "Bounds checks unable to add (interprocedural)");
 | 
						|
STATISTIC(ChecksUnableLoad, "Bounds checks unable to add (LoadInst)");
 | 
						|
 | 
						|
typedef IRBuilder<true, TargetFolder> BuilderTy;
 | 
						|
 | 
						|
namespace {
 | 
						|
  // FIXME: can use unions here to save space
 | 
						|
  struct CacheData {
 | 
						|
    APInt Offset;
 | 
						|
    Value *OffsetValue;
 | 
						|
    APInt Size;
 | 
						|
    Value *SizeValue;
 | 
						|
    bool ReturnVal;
 | 
						|
    CacheData() {}
 | 
						|
    CacheData(APInt Off, Value *OffVal, APInt Sz, Value *SzVal, bool Ret) :
 | 
						|
      Offset(Off), OffsetValue(OffVal), Size(Sz), SizeValue(SzVal),
 | 
						|
      ReturnVal(Ret) {}
 | 
						|
  };
 | 
						|
  typedef DenseMap<Value*, CacheData> CacheMapTy;
 | 
						|
  typedef SmallPtrSet<Value*, 8> PtrSetTy;
 | 
						|
 | 
						|
  struct BoundsChecking : public FunctionPass {
 | 
						|
    static char ID;
 | 
						|
 | 
						|
    BoundsChecking(unsigned _Penalty = 5) : FunctionPass(ID), Penalty(_Penalty){
 | 
						|
      initializeBoundsCheckingPass(*PassRegistry::getPassRegistry());
 | 
						|
    }
 | 
						|
 | 
						|
    virtual bool runOnFunction(Function &F);
 | 
						|
 | 
						|
    virtual void getAnalysisUsage(AnalysisUsage &AU) const {
 | 
						|
      AU.addRequired<TargetData>();
 | 
						|
      AU.addRequired<LoopInfo>();
 | 
						|
      AU.addRequired<ScalarEvolution>();
 | 
						|
    }
 | 
						|
 | 
						|
  private:
 | 
						|
    const TargetData *TD;
 | 
						|
    LoopInfo *LI;
 | 
						|
    ScalarEvolution *SE;
 | 
						|
    BuilderTy *Builder;
 | 
						|
    Function *Fn;
 | 
						|
    BasicBlock *TrapBB;
 | 
						|
    unsigned Penalty;
 | 
						|
    CacheMapTy CacheMap;
 | 
						|
    PtrSetTy SeenPtrs;
 | 
						|
 | 
						|
    BasicBlock *getTrapBB();
 | 
						|
    void emitBranchToTrap(Value *Cmp = 0);
 | 
						|
    bool computeAllocSize(Value *Ptr, APInt &Offset, Value* &OffsetValue,
 | 
						|
                          APInt &Size, Value* &SizeValue);
 | 
						|
    bool instrument(Value *Ptr, Value *Val);
 | 
						|
 };
 | 
						|
}
 | 
						|
 | 
						|
char BoundsChecking::ID = 0;
 | 
						|
INITIALIZE_PASS_BEGIN(BoundsChecking, "bounds-checking",
 | 
						|
                      "Run-time bounds checking", false, false)
 | 
						|
INITIALIZE_PASS_DEPENDENCY(ScalarEvolution)
 | 
						|
INITIALIZE_PASS_END(BoundsChecking, "bounds-checking",
 | 
						|
                      "Run-time bounds checking", false, false)
 | 
						|
 | 
						|
 | 
						|
/// getTrapBB - create a basic block that traps. All overflowing conditions
 | 
						|
/// branch to this block. There's only one trap block per function.
 | 
						|
BasicBlock *BoundsChecking::getTrapBB() {
 | 
						|
  if (TrapBB && !ManyTrapBB)
 | 
						|
    return TrapBB;
 | 
						|
 | 
						|
  BasicBlock::iterator PrevInsertPoint = Builder->GetInsertPoint();
 | 
						|
  TrapBB = BasicBlock::Create(Fn->getContext(), "trap", Fn);
 | 
						|
  Builder->SetInsertPoint(TrapBB);
 | 
						|
 | 
						|
  llvm::Value *F = Intrinsic::getDeclaration(Fn->getParent(), Intrinsic::trap);
 | 
						|
  CallInst *TrapCall = Builder->CreateCall(F);
 | 
						|
  TrapCall->setDoesNotReturn();
 | 
						|
  TrapCall->setDoesNotThrow();
 | 
						|
  Builder->CreateUnreachable();
 | 
						|
 | 
						|
  Builder->SetInsertPoint(PrevInsertPoint);
 | 
						|
  return TrapBB;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/// emitBranchToTrap - emit a branch instruction to a trap block.
 | 
						|
/// If Cmp is non-null, perform a jump only if its value evaluates to true.
 | 
						|
void BoundsChecking::emitBranchToTrap(Value *Cmp) {
 | 
						|
  Instruction *Inst = Builder->GetInsertPoint();
 | 
						|
  BasicBlock *OldBB = Inst->getParent();
 | 
						|
  BasicBlock *Cont = OldBB->splitBasicBlock(Inst);
 | 
						|
  OldBB->getTerminator()->eraseFromParent();
 | 
						|
 | 
						|
  if (Cmp)
 | 
						|
    BranchInst::Create(getTrapBB(), Cont, Cmp, OldBB);
 | 
						|
  else
 | 
						|
    BranchInst::Create(getTrapBB(), OldBB);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
#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;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/// instrument - adds run-time bounds checks to memory accessing instructions.
 | 
						|
/// Ptr is the pointer that will be read/written, and InstVal is either the
 | 
						|
/// result from the load or the value being stored. It is used to determine the
 | 
						|
/// size of memory block that is touched.
 | 
						|
/// Returns true if any change was made to the IR, false otherwise.
 | 
						|
bool BoundsChecking::instrument(Value *Ptr, Value *InstVal) {
 | 
						|
  uint64_t NeededSize = TD->getTypeStoreSize(InstVal->getType());
 | 
						|
  DEBUG(dbgs() << "Instrument " << *Ptr << " for " << Twine(NeededSize)
 | 
						|
              << " bytes\n");
 | 
						|
 | 
						|
  IntegerType *IntTy = TD->getIntPtrType(Fn->getContext());
 | 
						|
  unsigned IntTyBits = IntTy->getBitWidth();
 | 
						|
 | 
						|
  APInt Offset(IntTyBits, 0), Size(IntTyBits, 0);
 | 
						|
  Value *OffsetValue = 0, *SizeValue = 0;
 | 
						|
 | 
						|
  if (!computeAllocSize(Ptr, Offset, OffsetValue, Size, SizeValue)) {
 | 
						|
    DEBUG(dbgs() << "computeAllocSize failed:\n" << *Ptr << "\n");
 | 
						|
 | 
						|
    // erase everything that was computed in this iteration from the cache, so
 | 
						|
    // that no dangling references are left behind. We could be a bit smarter if
 | 
						|
    // we kept a dependency graph. It's probably not worth the complexity,
 | 
						|
    // though.
 | 
						|
    for (PtrSetTy::iterator I=SeenPtrs.begin(), E=SeenPtrs.end(); I != E; ++I)
 | 
						|
      CacheMap.erase(*I);
 | 
						|
    SeenPtrs.clear();
 | 
						|
 | 
						|
    ++ChecksUnable;
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  // three checks are required to ensure safety:
 | 
						|
  // . Offset >= 0  (since the offset is given from the base ptr)
 | 
						|
  // . Size >= Offset  (unsigned)
 | 
						|
  // . Size - Offset >= NeededSize  (unsigned)
 | 
						|
  if (!OffsetValue && !SizeValue) {
 | 
						|
    if (Offset.slt(0) || Size.ult(Offset) || (Size - Offset).ult(NeededSize)) {
 | 
						|
      // Out of bounds
 | 
						|
      emitBranchToTrap();
 | 
						|
      ++ChecksAdded;
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    // in bounds
 | 
						|
    ++ChecksSkipped;
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  // emit check for offset < 0
 | 
						|
  Value *CmpOffset = 0;
 | 
						|
  if (OffsetValue)
 | 
						|
    CmpOffset = Builder->CreateICmpSLT(OffsetValue, ConstantInt::get(IntTy, 0));
 | 
						|
  else if (Offset.slt(0)) {
 | 
						|
    // offset proved to be negative
 | 
						|
    emitBranchToTrap();
 | 
						|
    ++ChecksAdded;
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
 | 
						|
  // we couldn't determine statically if the memory access is safe; emit a
 | 
						|
  // run-time check
 | 
						|
  GET_VALUE(OffsetValue, Offset);
 | 
						|
  GET_VALUE(SizeValue, Size);
 | 
						|
 | 
						|
  Value *NeededSizeVal = ConstantInt::get(IntTy, NeededSize);
 | 
						|
  // FIXME: add NSW/NUW here?  -- we dont care if the subtraction overflows
 | 
						|
  Value *ObjSize = Builder->CreateSub(SizeValue, OffsetValue);
 | 
						|
  Value *Cmp1 = Builder->CreateICmpULT(SizeValue, OffsetValue);
 | 
						|
  Value *Cmp2 = Builder->CreateICmpULT(ObjSize, NeededSizeVal);
 | 
						|
  Value *Or = Builder->CreateOr(Cmp1, Cmp2);
 | 
						|
  if (CmpOffset)
 | 
						|
    Or = Builder->CreateOr(CmpOffset, Or);
 | 
						|
  emitBranchToTrap(Or);
 | 
						|
 | 
						|
  ++ChecksAdded;
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
bool BoundsChecking::runOnFunction(Function &F) {
 | 
						|
  TD = &getAnalysis<TargetData>();
 | 
						|
  LI = &getAnalysis<LoopInfo>();
 | 
						|
  SE = &getAnalysis<ScalarEvolution>();
 | 
						|
 | 
						|
  TrapBB = 0;
 | 
						|
  Fn = &F;
 | 
						|
  BuilderTy TheBuilder(F.getContext(), TargetFolder(TD));
 | 
						|
  Builder = &TheBuilder;
 | 
						|
 | 
						|
  // check HANDLE_MEMORY_INST in include/llvm/Instruction.def for memory
 | 
						|
  // touching instructions
 | 
						|
  std::vector<Instruction*> WorkList;
 | 
						|
  for (inst_iterator i = inst_begin(F), e = inst_end(F); i != e; ++i) {
 | 
						|
    Instruction *I = &*i;
 | 
						|
    if (isa<LoadInst>(I) || isa<StoreInst>(I) || isa<AtomicCmpXchgInst>(I) ||
 | 
						|
        isa<AtomicRMWInst>(I))
 | 
						|
        WorkList.push_back(I);
 | 
						|
  }
 | 
						|
 | 
						|
  bool MadeChange = false;
 | 
						|
  for (std::vector<Instruction*>::iterator i = WorkList.begin(),
 | 
						|
       e = WorkList.end(); i != e; ++i) {
 | 
						|
    Instruction *I = *i;
 | 
						|
 | 
						|
    Builder->SetInsertPoint(I);
 | 
						|
    if (LoadInst *LI = dyn_cast<LoadInst>(I)) {
 | 
						|
      MadeChange |= instrument(LI->getPointerOperand(), LI);
 | 
						|
    } else if (StoreInst *SI = dyn_cast<StoreInst>(I)) {
 | 
						|
      MadeChange |= instrument(SI->getPointerOperand(), SI->getValueOperand());
 | 
						|
    } else if (AtomicCmpXchgInst *AI = dyn_cast<AtomicCmpXchgInst>(I)) {
 | 
						|
      MadeChange |= instrument(AI->getPointerOperand(),AI->getCompareOperand());
 | 
						|
    } else if (AtomicRMWInst *AI = dyn_cast<AtomicRMWInst>(I)) {
 | 
						|
      MadeChange |= instrument(AI->getPointerOperand(), AI->getValOperand());
 | 
						|
    } else {
 | 
						|
      llvm_unreachable("unknown Instruction type");
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return MadeChange;
 | 
						|
}
 | 
						|
 | 
						|
FunctionPass *llvm::createBoundsCheckingPass(unsigned Penalty) {
 | 
						|
  return new BoundsChecking(Penalty);
 | 
						|
}
 |