llvm-6502/lib/Transforms/Scalar/BoundsChecking.cpp
Nuno Lopes 42d80c7a5b BoundsChecking: optimize out the check for offset < 0 if size is known to be >= 0 (signed).
(LLVM optimizers cannot do this optimization by themselves)

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@159668 91177308-0d34-0410-b5e6-96231b3b80d8
2012-07-03 17:30:18 +00:00

210 lines
7.1 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/IRBuilder.h"
#include "llvm/Intrinsics.h"
#include "llvm/Pass.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Analysis/MemoryBuiltins.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/InstIterator.h"
#include "llvm/Support/TargetFolder.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Target/TargetData.h"
using namespace llvm;
static cl::opt<bool> SingleTrapBB("bounds-checking-single-trap",
cl::desc("Use one trap block per function"));
STATISTIC(ChecksAdded, "Bounds checks added");
STATISTIC(ChecksSkipped, "Bounds checks skipped");
STATISTIC(ChecksUnable, "Bounds checks unable to add");
typedef IRBuilder<true, TargetFolder> BuilderTy;
namespace {
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>();
}
private:
const TargetData *TD;
ObjectSizeOffsetEvaluator *ObjSizeEval;
BuilderTy *Builder;
Instruction *Inst;
BasicBlock *TrapBB;
unsigned Penalty;
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(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 && SingleTrapBB)
return TrapBB;
Function *Fn = Inst->getParent()->getParent();
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();
TrapCall->setDebugLoc(Inst->getDebugLoc());
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) {
// check if the comparison is always false
ConstantInt *C = dyn_cast_or_null<ConstantInt>(Cmp);
if (C) {
++ChecksSkipped;
if (!C->getZExtValue())
return;
else
Cmp = 0; // unconditional branch
}
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);
}
/// 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");
SizeOffsetEvalType SizeOffset = ObjSizeEval->compute(Ptr);
if (!ObjSizeEval->bothKnown(SizeOffset)) {
++ChecksUnable;
return false;
}
Value *Size = SizeOffset.first;
Value *Offset = SizeOffset.second;
ConstantInt *SizeCI = dyn_cast<ConstantInt>(Size);
IntegerType *IntTy = TD->getIntPtrType(Inst->getContext());
Value *NeededSizeVal = ConstantInt::get(IntTy, NeededSize);
// 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)
//
// optimization: if Size >= 0 (signed), skip 1st check
// FIXME: add NSW/NUW here? -- we dont care if the subtraction overflows
Value *ObjSize = Builder->CreateSub(Size, Offset);
Value *Cmp2 = Builder->CreateICmpULT(Size, Offset);
Value *Cmp3 = Builder->CreateICmpULT(ObjSize, NeededSizeVal);
Value *Or = Builder->CreateOr(Cmp2, Cmp3);
if (!SizeCI || SizeCI->getValue().slt(0)) {
Value *Cmp1 = Builder->CreateICmpSLT(Offset, ConstantInt::get(IntTy, 0));
Or = Builder->CreateOr(Cmp1, Or);
}
emitBranchToTrap(Or);
++ChecksAdded;
return true;
}
bool BoundsChecking::runOnFunction(Function &F) {
TD = &getAnalysis<TargetData>();
TrapBB = 0;
BuilderTy TheBuilder(F.getContext(), TargetFolder(TD));
Builder = &TheBuilder;
ObjectSizeOffsetEvaluator TheObjSizeEval(TD, F.getContext());
ObjSizeEval = &TheObjSizeEval;
// 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) {
Inst = *i;
Builder->SetInsertPoint(Inst);
if (LoadInst *LI = dyn_cast<LoadInst>(Inst)) {
MadeChange |= instrument(LI->getPointerOperand(), LI);
} else if (StoreInst *SI = dyn_cast<StoreInst>(Inst)) {
MadeChange |= instrument(SI->getPointerOperand(), SI->getValueOperand());
} else if (AtomicCmpXchgInst *AI = dyn_cast<AtomicCmpXchgInst>(Inst)) {
MadeChange |= instrument(AI->getPointerOperand(),AI->getCompareOperand());
} else if (AtomicRMWInst *AI = dyn_cast<AtomicRMWInst>(Inst)) {
MadeChange |= instrument(AI->getPointerOperand(), AI->getValOperand());
} else {
llvm_unreachable("unknown Instruction type");
}
}
return MadeChange;
}
FunctionPass *llvm::createBoundsCheckingPass(unsigned Penalty) {
return new BoundsChecking(Penalty);
}