blob: 0f538c357f49c4a482f762fe6e67f186c21013cc [file] [log] [blame]
//===- OptimizeImpliedFastLSChecks.cpp - Remove implied fast l/s checks ---===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This pass removes fast load/store checks that are implied by other
// fast load/store checks. It works by traversing a dominator tree to find
// out which checks must always happen before other checks.
//
// In particular it can remove fast load/store checks where an identical one
// already dominates it. It can also remove cases where the only difference
// between the checks is the object being referred to (i.e. when the objects are
// the same size and the access offsets and sizes are equal).
//
//===----------------------------------------------------------------------===//
#define DEBUG_TYPE "optimize-implied-fast-ls-checks"
#include "CommonMemorySafetyPasses.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Analysis/Dominators.h"
#include "llvm/Analysis/MSCInfo.h"
#include "llvm/Analysis/ScalarEvolution.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Module.h"
#include "llvm/Transforms/Instrumentation.h"
#include "llvm/Pass.h"
using namespace llvm;
STATISTIC(FastMemoryChecksRemoved, "Fast load/store checks removed");
namespace {
struct AccessData {
Value *AccessSize, *ObjSize;
const SCEV *Offset;
AccessData(Value *AccessSize, Value *ObjSize, const SCEV *Offset):
AccessSize(AccessSize), ObjSize(ObjSize), Offset(Offset) { }
bool operator<(const AccessData &Other) const {
if (AccessSize != Other.AccessSize)
return AccessSize < Other.AccessSize;
if (ObjSize != Other.ObjSize)
return ObjSize < Other.ObjSize;
return Offset < Other.Offset;
}
bool operator==(const AccessData &Other) const {
return AccessSize == Other.AccessSize && ObjSize == Other.ObjSize &&
Offset == Other.Offset;
}
};
class OptimizeImpliedFastLSChecks : public FunctionPass {
MSCInfo *MSCI;
ScalarEvolution *SE;
// The access data objects of all fast load/store checks that dominate the
// basic block that is being worked on in exploreNode.
SmallSet <AccessData, 16> PreviousChecks;
// The checks scheduled for removal.
SmallVector <CallInst*, 16> ToRemove;
void exploreNode(DomTreeNode* Node);
public:
static char ID;
OptimizeImpliedFastLSChecks(): FunctionPass(ID) { }
virtual bool runOnFunction(Function &F);
virtual void getAnalysisUsage(AnalysisUsage &AU) const {
AU.addRequired<DominatorTree>();
AU.addPreserved<DominatorTree>();
AU.addRequired<MSCInfo>();
AU.addRequired<ScalarEvolution>();
AU.setPreservesCFG();
}
virtual const char *getPassName() const {
return "OptimizeImpliedFastLSChecks";
}
};
} // end anon namespace
char OptimizeImpliedFastLSChecks::ID = 0;
INITIALIZE_PASS(OptimizeImpliedFastLSChecks, "optimize-implied-fast-ls-checks",
"Remove implied fast load/store checks where possible.", false,
false)
FunctionPass *llvm::createOptimizeImpliedFastLSChecksPass() {
return new OptimizeImpliedFastLSChecks();
}
bool OptimizeImpliedFastLSChecks::runOnFunction(Function &F) {
DominatorTree *DT = &getAnalysis<DominatorTree>();
MSCI = &getAnalysis<MSCInfo>();
SE = &getAnalysis<ScalarEvolution>();
// Go through the function in dominance order to find the checks to remove.
exploreNode(DT->getRootNode());
assert(PreviousChecks.empty() && "This should be empty");
// Erase the checks scheduled for removal.
for (size_t i = 0, N = ToRemove.size(); i != N; ++i) {
ToRemove[i]->eraseFromParent();
++FastMemoryChecksRemoved;
}
// Return true iff anything was changed (any checks were removed).
bool modified = !ToRemove.empty();
ToRemove.clear();
return modified;
}
/// exploreNode - recursively explore the basic blocks that are dominated by
/// the current basic block (referred to by the dominator tree node).
///
/// Side effects:
/// * Previously unseen checks will be added to PreviousChecks before the
/// recursive calls. The initial state will be restored before returning.
/// * Checks scheduled for removal will be added to ToRemove.
///
void OptimizeImpliedFastLSChecks::exploreNode(DomTreeNode* Node) {
// The list of previously seen checks in this basic block.
SmallVector <AccessData, 4> LocalChecks;
// Iterate over all fast load/store checks in this basic block.
// Remove the ones that are implied by dominating checks.
// Add the rest to the set of previous checks.
BasicBlock *BB = Node->getBlock();
for (BasicBlock::iterator I = BB->begin(), E = BB->end(); I != E; ++I) {
CallInst *CI = dyn_cast<CallInst>(I);
if (!CI)
continue;
CheckInfoType *Info = MSCI->getCheckInfo(CI->getCalledFunction());
if (!Info || !Info->isFastMemoryCheck())
continue;
Value *AccessPtr = CI->getArgOperand(Info->PtrArgNo);
Value *AccessSize = CI->getArgOperand(Info->SizeArgNo);
Value *ObjPtr = CI->getArgOperand(Info->ObjArgNo);
Value *ObjSize = CI->getArgOperand(Info->ObjSizeArgNo);
// Create an analysis expression of the offset from the object.
// It will hopefully get rid of the reference to the object itself.
const SCEV *Offset = SE->getMinusSCEV(SE->getSCEV(AccessPtr),
SE->getSCEV(ObjPtr));
AccessData Access(AccessSize, ObjSize, Offset);
if (PreviousChecks.count(Access)) {
// An equivalent check has been seen before so this one can be removed.
ToRemove.push_back(CI);
} else {
// Previously unseen kind of check - record it for future reference.
PreviousChecks.insert(Access);
LocalChecks.push_back(Access);
}
}
// Recursively call this function on basic blocks that are directly dominated.
const std::vector <DomTreeNode*> &Children = Node->getChildren();
for (size_t i = 0, N = Children.size(); i != N; ++i)
exploreNode(Children[i]);
// Restore PreviousChecks to the state at the beginning of the call.
for (size_t i = 0, N = LocalChecks.size(); i != N; ++i)
PreviousChecks.erase(LocalChecks[i]);
}