blob: 1b229254d6dcf16636826f489e14534ed73a813f [file] [log] [blame]
//===- ExactCheckOpt.cpp -------------------------------------------------- --//
//
// The LLVM Compiler Infrastructure
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This pass tries to lower bounds checks and load/store checks to exact
// checks, that is checks whose bounds information can be determined easily,
// say, allocations inside a function or global variables. Therefore SAFECode
// does not need to register stuffs in the meta-data.
//
//===----------------------------------------------------------------------===//
#define DEBUG_TYPE "exactcheck-opt"
#include "llvm/ADT/Statistic.h"
#include "safecode/AllocatorInfo.h"
#include "safecode/OptimizeChecks.h"
#include "safecode/Utility.h"
#include <queue>
namespace llvm {
static RegisterPass<ExactCheckOpt> X ("exactcheck-opt", "Exact check optimization", true);
// Pass Statistics
namespace {
STATISTIC (ExactChecks , "The number of checks lowered to exactcheck");
}
char ExactCheckOpt::ID = 0;
//
// Method: runOnModule()
//
// Description:
// This method is the entry point for the transform pass.
//
// Inputs:
// M - The LLVM module to transform.
//
// Return value:
// true - The module was modified.
// false - The module was not modified.
//
bool
ExactCheckOpt::runOnModule(Module & M) {
//
// Add a prototype for the exactcheck function.
//
Type * VoidTy = Type::getVoidTy (M.getContext());
Type * VoidPtrTy = getVoidPtrType (M.getContext());
Type * Int32Type = IntegerType::getInt32Ty(M.getContext());
ExactCheck2 = cast<Function>(M.getOrInsertFunction ("exactcheck2",
VoidPtrTy,
VoidPtrTy,
VoidPtrTy,
Int32Type,
NULL));
FastLSCheck = cast<Function>(M.getOrInsertFunction ("fastlscheck",
VoidTy,
VoidPtrTy,
VoidPtrTy,
Int32Type,
NULL));
//
// Scan through all the intrinsics and process those that perform run-time
// checks.
//
for (unsigned index = 0; index < numChecks; ++index) {
//
// Clear the list of calls to intrinsics that must be removed.
//
checkingIntrinsicsToBeRemoved.clear();
//
// Scan through all uses of this run-time checking function and process
// each call to it.
//
Function * F = M.getFunction (RuntimeChecks[index].name);
if (F) {
for (Value::use_iterator UI = F->use_begin(), E = F->use_end();
UI != E;
++UI) {
if (CallInst * CI = dyn_cast<CallInst>(*UI)) {
visitCheckingIntrinsic(CI, RuntimeChecks[index]);
}
}
}
//
// Update statistics if anything has changed. We don't want to update
// the statistics variable if nothing has happened because we don't want
// it to appear in the output if it is zero.
//
if (checkingIntrinsicsToBeRemoved.size())
ExactChecks += checkingIntrinsicsToBeRemoved.size();
//
// Remove checking intrinsics that have been optimized
//
for (std::vector<CallInst*>::const_iterator i = checkingIntrinsicsToBeRemoved.begin(), e = checkingIntrinsicsToBeRemoved.end(); i != e; ++i) {
(*i)->eraseFromParent();
}
}
//
// Conservatively assume that we have changed something in the module.
//
return true;
}
//
// Function: findObject()
//
// Description:
// Find the singular memory object to which this pointer points (if such a
// singular object exists and is easy to find).
//
static Value *
findObject (Value * obj) {
std::set<Value *> exploredObjects;
std::set<Value *> objects;
std::queue<Value *> queue;
queue.push(obj);
while (!queue.empty()) {
Value * o = queue.front();
queue.pop();
if (exploredObjects.count(o)) continue;
exploredObjects.insert(o);
if (isa<CastInst>(o)) {
queue.push(cast<CastInst>(o)->getOperand(0));
} else if (isa<GetElementPtrInst>(o)) {
queue.push(cast<GetElementPtrInst>(o)->getPointerOperand());
} else if (isa<PHINode>(o)) {
PHINode * p = cast<PHINode>(o);
for(unsigned int i = 0; i < p->getNumIncomingValues(); ++i) {
queue.push(p->getIncomingValue(i));
}
} else {
objects.insert(o);
}
}
return objects.size() == 1 ? *(objects.begin()) : NULL;
}
//
// Function: visitCheckingIntrinsic()
//
// Description:
// Attempts to rewrite an extensive check into an efficient, accurate array
// bounds check which will not use meta-data information.
//
// Inputs:
// CI - A pointer to the instruction that performs a run-time check.
// Info - A reference to a structure containing information on the
// run-time check.
//
// Return value:
// true - Successfully rewrite the check into an exact check.
// false - Cannot perform the optimization.
//
bool
ExactCheckOpt::visitCheckingIntrinsic (CallInst * CI, const struct CheckInfo & Info) {
//
// Get the pointer that is checked by this run-time check.
//
Value * CheckPtr = Info.getCheckedPointer(CI)->stripPointerCasts();
//
// Try to find the source of the pointer.
//
Value * BasePtr = findObject (CheckPtr);
if (!BasePtr) return false;
//
// Do not use exactchecks on global variables that are defined in other
// compilation units.
//
if (GlobalValue * GV = dyn_cast<GlobalValue>(BasePtr)) {
if (GV->isDeclaration()) {
return false;
}
}
//
// If the call is to a memory checking function, then we cannot blindly
// convert a check that operates on a heap object; the heap object might be
// deallocated between the time it was allocated and the time of the check.
// Other checks can be converted since they don't try to detect dangling
// pointers.
//
// So, if this is a memory check, make sure that the object cannot be freed
// before the check. Global variables and stack allocations cannot be freed.
//
if ((!(Info.isMemcheck)) ||
((isa<AllocaInst>(BasePtr)) || isa<GlobalVariable>(BasePtr))) {
//
// Attempt to get the size of the pointer. If a size is returned, we know
// that the base pointer points to the beginning of an object, and we can
// do a run-time check without a lookup.
//
AllocatorInfoPass & AIP = getAnalysis<AllocatorInfoPass>();
if (Value * Size = AIP.getObjectSize(BasePtr)) {
rewriteToExactCheck(Info.isMemcheck, CI, BasePtr, CheckPtr, Size);
return true;
}
}
//
// We were not able to insert a call to exactcheck().
//
return false;
}
//
// Function: rewriteToExactCheck()
//
// Description:
// Rewrite a check into an exact check
//
// Inputs:
// isMemCheck - Flags if we are replacing a load/store check.
// BasePointer - An LLVM Value representing the base of the object to check.
// Result - An LLVM Value representing the pointer to check.
// Bounds - An LLVM Value representing the bounds of the check.
//
void
ExactCheckOpt::rewriteToExactCheck(bool isMemCheck, CallInst * CI,
Value * BasePointer,
Value * ResultPointer, Value * Bounds) {
// The LLVM type for a void *
Type *VoidPtrType = getVoidPtrType(CI->getContext());
Type * Int32Type = IntegerType::getInt32Ty(CI->getContext());
//
// For readability, make sure that both the base pointer and the result
// pointer have names.
//
if (!(BasePointer->hasName())) BasePointer->setName("base");
if (!(ResultPointer->hasName())) ResultPointer->setName("result");
//
// Cast the operands to the correct type.
//
BasePointer = castTo (BasePointer, VoidPtrType,
BasePointer->getName()+".ec.casted",
CI);
ResultPointer = castTo (ResultPointer, VoidPtrType,
ResultPointer->getName()+".ec.casted",
CI);
Value * CastBounds = Bounds;
if (Bounds->getType() != Int32Type) {
LLVMContext & Context = Int32Type->getContext();
CastBounds = CastInst::CreateIntegerCast (CastBounds,
Type::getInt32Ty(Context),
false,
CastBounds->getName(),
CI);
}
//
// Create the call to exactcheck2().
//
std::vector<Value *> args(1, BasePointer);
args.push_back(ResultPointer);
args.push_back(CastBounds);
Function * Check = (isMemCheck) ? FastLSCheck : ExactCheck2;
CallInst * ExactCheckCI = CallInst::Create (Check, args, "", CI);
//
// Copy the debug metadata from the original check to the exactcheck.
//
if (MDNode * MD = CI->getMetadata ("dbg"))
ExactCheckCI->setMetadata ("dbg", MD);
//
// boundscheck / exactcheck return an out of bound pointer when REWRITE_OOB is
// enabled. We need to replace all uses to make the optimization correct, but
// we don't need do anything for load / store checks.
//
// We can test the condition above by simply testing the return types of the
// checking functions.
//
if (ExactCheckCI->getType() == CI->getType()) {
CI->replaceAllUsesWith(ExactCheckCI);
}
checkingIntrinsicsToBeRemoved.push_back(CI);
}
}