Files
revng-revng/reachingdefinitions.cpp
T
Alessandro Di Federico ec12b5ec0d ConditionNumberingPass: track the defining BBs
`ConditionNumberingPass` now also keeps track of which basic blocks
define the value of a certain condition. This helps the conditional
reaching definition analysis to reset all the definitions associated to
that condition when they are propagated to a basic block which defines
that condition.
2016-12-04 00:28:57 +01:00

980 lines
30 KiB
C++

/// \file reachingdefinitions.cpp
/// \brief Implementation of the ReachingDefinitionsPass
//
// This file is distributed under the MIT License. See LICENSE.md for details.
//
// Standard includes
#include <array>
#include <cstdint>
#include <iomanip>
#include <map>
#include <set>
#include <unordered_map>
#include <vector>
// LLVM includes
#include "llvm/ADT/PostOrderIterator.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/Casting.h"
// Local includes
#include "datastructures.h"
#include "debug.h"
#include "ir-helpers.h"
#include "reachingdefinitions.h"
// #include "valgrind/callgrind.h"
using namespace llvm;
using std::pair;
using std::queue;
using std::set;
using std::tie;
using std::unordered_map;
using std::vector;
using IndexesVector = SmallVector<int32_t, 2>;
template<class BBI, ReachingDefinitionsResult R>
const vector<LoadInst *> &
ReachingDefinitionsImplPass<BBI, R>::getReachedLoads(Instruction *Definition) {
assert(R == ReachingDefinitionsResult::ReachedLoads);
return ReachedLoads[Definition];
}
template<class BBI, ReachingDefinitionsResult R>
const vector<Instruction *> &
ReachingDefinitionsImplPass<BBI, R>::getReachingDefinitions(LoadInst *Load) {
return ReachingDefinitions[Load];
}
template<class B, ReachingDefinitionsResult R>
unsigned
ReachingDefinitionsImplPass<B, R>::getReachingDefinitionsCount(LoadInst *Load) {
assert(R == ReachingDefinitionsResult::ReachedLoads);
return ReachingDefinitionsCount[Load];
}
using RDP = ReachingDefinitionsResult;
template class ReachingDefinitionsImplPass<BasicBlockInfo,
RDP::ReachingDefinitions>;
template class ReachingDefinitionsImplPass<BasicBlockInfo,
RDP::ReachedLoads>;
template<class BBI, ReachingDefinitionsResult R>
char ReachingDefinitionsImplPass<BBI, R>::ID = 0;
static RegisterPass<ReachingDefinitionsPass> X1("rdp",
"Reaching Definitions Pass",
true,
true);
static RegisterPass<ReachedLoadsPass> X2("rlp",
"Reaching Definitions Pass",
true,
true);
template<>
int32_t ReachingDefinitionsPass::getConditionIndex(TerminatorInst *V) {
return 0;
}
template<>
void ReachingDefinitionsPass::getAnalysisUsage(AnalysisUsage &AU) const {
AU.setPreservesAll();
}
template<>
int32_t ReachedLoadsPass::getConditionIndex(TerminatorInst *V) {
return 0;
}
template<>
void ReachedLoadsPass::getAnalysisUsage(AnalysisUsage &AU) const {
AU.setPreservesAll();
}
template class ReachingDefinitionsImplPass<ConditionalBasicBlockInfo,
RDP::ReachingDefinitions>;
template class ReachingDefinitionsImplPass<ConditionalBasicBlockInfo,
RDP::ReachedLoads>;
static RegisterPass<ConditionalReachingDefinitionsPass> Y1("crdp",
"Conditional"
" Reaching"
" Definitions Pass",
true,
true);
static RegisterPass<ConditionalReachedLoadsPass> Y2("crlp",
"Conditional"
" Reaching"
" Definitions Pass",
true,
true);
// TODO: this duplication sucks
template<>
int32_t
ConditionalReachingDefinitionsPass::getConditionIndex(TerminatorInst *T) {
auto *Branch = dyn_cast<BranchInst>(T);
if (Branch == nullptr || !Branch->isConditional())
return 0;
return getAnalysis<ConditionNumberingPass>().getConditionIndex(T);
}
template<>
void
ConditionalReachingDefinitionsPass::getAnalysisUsage(AnalysisUsage &AU) const {
AU.setPreservesAll();
AU.addRequired<ConditionNumberingPass>();
}
template<>
int32_t
ConditionalReachedLoadsPass::getConditionIndex(TerminatorInst *T) {
auto *Branch = dyn_cast<BranchInst>(T);
if (Branch == nullptr || !Branch->isConditional())
return 0;
return getAnalysis<ConditionNumberingPass>().getConditionIndex(T);
}
template<>
void
ConditionalReachedLoadsPass::getAnalysisUsage(AnalysisUsage &AU) const {
AU.setPreservesAll();
AU.addRequired<ConditionNumberingPass>();
}
static size_t combine(size_t A, size_t B) {
return (A << 1 | A >> 31) ^ B;
}
static size_t combine(size_t A, void *Ptr) {
return combine(A, reinterpret_cast<intptr_t>(Ptr));
}
static bool isSupportedOperator(unsigned Opcode) {
switch (Opcode) {
case Instruction::Xor:
case Instruction::And:
case Instruction::Or:
case Instruction::ICmp:
return true;
default:
return false;
}
}
class ConditionHash {
public:
ConditionHash(ReachingDefinitionsPass &RDP) : RDP(RDP) { }
size_t operator()(BranchInst * const& V) const;
private:
ReachingDefinitionsPass &RDP;
};
size_t ConditionHash::operator()(BranchInst * const& B) const {
Value *V = B->getCondition();
size_t Hash = 0;
queue<Value *> WorkList;
WorkList.push(V);
while (!WorkList.empty()) {
Value *V;
V = WorkList.front();
WorkList.pop();
bool IsStore = isa<StoreInst>(V);
bool IsLoad = isa<LoadInst>(V);
if (IsStore || IsLoad) {
// Load/store vs load/store
if (IsStore) {
Hash = combine(Hash, cast<StoreInst>(V)->getPointerOperand());
} else {
for (Instruction *I : RDP.getReachingDefinitions(cast<LoadInst>(V))) {
if (auto *Store = dyn_cast<StoreInst>(I))
Hash = combine(Hash, Store->getPointerOperand());
else if (auto *Load = dyn_cast<LoadInst>(I))
Hash = combine(Hash, Load->getPointerOperand());
}
}
} else if (auto *I = dyn_cast<Instruction>(V)) {
// Instruction
if (!isSupportedOperator(I->getOpcode())) {
Hash = combine(Hash, V);
} else {
Hash = combine(Hash, I->getOpcode());
Hash = combine(Hash, I->getNumOperands());
for (unsigned C = 0; C < I->getNumOperands(); C++)
WorkList.push(I->getOperand(C));
}
} else {
Hash = combine(Hash, V);
}
}
return Hash;
}
class ConditionEqualTo {
public:
ConditionEqualTo(ReachingDefinitionsPass &RDP) : RDP(RDP) { }
bool operator()(BranchInst * const& A, BranchInst * const& B) const;
private:
ReachingDefinitionsPass &RDP;
};
bool ConditionEqualTo::operator()(BranchInst * const& BA,
BranchInst * const& BB) const {
Value *A = BA->getCondition();
Value *B = BB->getCondition();
queue<pair<Value *, Value *>> WorkList;
WorkList.push({A, B});
while (!WorkList.empty()) {
Value *AV, *BV;
tie(AV, BV) = WorkList.front();
WorkList.pop();
// Early continue in case they're exactly the same value
if (AV == BV)
continue;
bool AIsStore = isa<StoreInst>(AV);
bool AIsLoad = isa<LoadInst>(AV);
bool BIsStore = isa<StoreInst>(BV);
bool BIsLoad = isa<LoadInst>(BV);
if ((AIsStore || AIsLoad) && (BIsStore || BIsLoad)) {
// Load/store vs load/store
vector<Instruction *> AStores;
if (AIsStore)
AStores.push_back(cast<StoreInst>(AV));
else
AStores = RDP.getReachingDefinitions(cast<LoadInst>(AV));
vector<Instruction *> BStores;
if (BIsStore)
BStores.push_back(cast<StoreInst>(BV));
else
BStores = RDP.getReachingDefinitions(cast<LoadInst>(BV));
if (AStores != BStores)
return false;
} else if (auto *AI = dyn_cast<Instruction>(AV)) {
// Instruction
auto *BI = dyn_cast<Instruction>(BV);
if (BI == nullptr
|| AI->getOpcode() != BI->getOpcode()
|| AI->getNumOperands() != BI->getNumOperands()
|| !isSupportedOperator(AI->getOpcode()))
return false;
for (unsigned I = 0; I < AI->getNumOperands(); I++)
WorkList.push({ AI->getOperand(I), BI->getOperand(I) });
} else {
return false;
}
}
return true;
}
static SmallSet<BasicBlock *, 2>
definingBasicBlocks(ReachingDefinitionsPass &RDP, BranchInst * const& Branch) {
SmallSet<BasicBlock *, 2> Result;
Value *A = Branch->getCondition();
queue<Value *> WorkList;
WorkList.push(A);
while (!WorkList.empty()) {
Value *AV;
AV = WorkList.front();
WorkList.pop();
bool AIsStore = isa<StoreInst>(AV);
bool AIsLoad = isa<LoadInst>(AV);
if (AIsStore || AIsLoad) {
// Load/store vs load/store
vector<Instruction *> AStores;
if (AIsStore)
Result.insert(cast<StoreInst>(AV)->getParent());
else
for (Instruction *I : RDP.getReachingDefinitions(cast<LoadInst>(AV)))
Result.insert(I->getParent());
} else if (auto *AI = dyn_cast<Instruction>(AV)) {
// Instruction
if (!isSupportedOperator(AI->getOpcode()))
return { };
for (unsigned I = 0; I < AI->getNumOperands(); I++)
WorkList.push(AI->getOperand(I));
} else if (!isa<Constant>(AV)) {
return { };
}
}
return Result;
}
char ConditionNumberingPass::ID = 0;
const IndexesVector ConditionNumberingPass::NoDefinedConditions;
static RegisterPass<ConditionNumberingPass> Z("cnp",
"Condition Numbering Pass",
true,
true);
template<typename C, typename T>
static bool pushIfAbsent(C &Container, T Element) {
auto It = std::find(Container.begin(), Container.end(), Element);
bool Result = It != Container.end();
if (!Result)
Container.push_back(Element);
return Result;
}
bool ConditionNumberingPass::runOnFunction(Function &F) {
DBG("passes", { dbg << "Starting ConditionNumberingPass\n"; });
auto &RDP = getAnalysis<ReachingDefinitionsPass>();
unordered_map<BranchInst *,
SmallVector<BranchInst *, 1>,
ConditionHash,
ConditionEqualTo> Conditions(10,
ConditionHash(RDP),
ConditionEqualTo(RDP));
// Group conditions together
for (BasicBlock &BB : F)
if (auto *Branch = dyn_cast<BranchInst>(BB.getTerminator()))
if (Branch->isConditional())
Conditions[Branch].push_back(Branch);
// Save the interesting results
uint32_t ConditionIndex = 0;
for (auto &P : Conditions) {
if (P.second.size() > 1) {
// 0 is a reserved value
ConditionIndex++;
for (BranchInst *B : P.second) {
BranchConditionNumberMap[B] = ConditionIndex;
for (BasicBlock *Definer : definingBasicBlocks(RDP, B)) {
pushIfAbsent(DefinedConditions[Definer], ConditionIndex);
}
}
DBG("cnp",
{
dbg << std::dec << ConditionIndex << ":";
for (BranchInst *B : P.second)
dbg << " " << getName(B);
auto It = P.second.begin();
if (It != P.second.end()) {
dbg << " (defined by:";
for (BasicBlock *Definer : definingBasicBlocks(RDP, *It)) {
dbg << " " << getName(Definer);
}
dbg << ")";
}
dbg << "\n";
});
}
}
DBG("passes", { dbg << "Ending ConditionNumberingPass\n"; });
return false;
}
void BasicBlockInfo::dump(std::ostream& Output) {
set<Instruction *> Printed;
for (const MemoryInstruction &MI : Reaching) {
Instruction *V = MI.I;
if (Printed.count(V) == 0) {
Printed.insert(V);
Output << " " << getName(V);
}
}
}
void BasicBlockInfo::newDefinition(StoreInst *Store, TypeSizeProvider &TSP) {
// Remove all the aliased reaching definitions
MemoryAccess TargetMA(Store, TSP);
removeDefinitions([&TargetMA] (MemoryInstruction &MI) {
return TargetMA.mayAlias(MI.MA);
});
// Add this definition
Definitions.push_back(MemoryInstruction(Store, TSP));
}
LoadDefinitionType BasicBlockInfo::newDefinition(LoadInst *Load,
TypeSizeProvider &TSP) {
LoadDefinitionType Result = NoReachingDefinitions;
// Check if it's a self-referencing load
MemoryAccess TargetMA(Load, TSP);
for (auto &MI : Definitions) {
auto *Definition = MI.I;
if (Definition == Load) {
// It's self-referencing, suppress all the matching loads
removeDefinitions([&TargetMA] (MemoryInstruction &MI) {
return isa<LoadInst>(MI.I) && TargetMA == MI.MA;
});
Result = SelfReaching;
break;
} else if (TargetMA == MI.MA) {
Result = HasReachingDefinitions;
}
}
// Add this definition
if (Result == NoReachingDefinitions)
Definitions.push_back(MemoryInstruction(Load, TSP));
return Result;
}
bool BasicBlockInfo::propagateTo(BasicBlockInfo &Target,
TypeSizeProvider &TSP) {
bool Changed = false;
for (MemoryInstruction &Definition : Definitions)
Changed |= Target.Reaching.insert(Definition).second;
return Changed;
}
vector<pair<Instruction *, MemoryAccess>>
BasicBlockInfo::getReachingDefinitions(set<LoadInst *> &WhiteList,
TypeSizeProvider &TSP) {
vector<pair<Instruction *, MemoryAccess>> Result;
for (const MemoryInstruction &MI : Reaching) {
Instruction *I = MI.I;
if (auto *Load = dyn_cast<LoadInst>(I)) {
// If it's a load check it's whitelisted
if (WhiteList.count(Load) != 0)
Result.push_back({ Load, MI.MA });
} else {
// It's a store
Result.push_back({ I, MI.MA });
}
}
freeContainer(Reaching);
assert(Reaching.size() == 0);
return Result;
}
void ConditionalBasicBlockInfo::dump(std::ostream& Output) {
set<Instruction *> Printed;
for (auto &P : Reaching) {
Instruction *I = P.first.I;
if (Printed.count(I) == 0) {
Printed.insert(I);
Output << " " << getName(I);
}
}
}
void ConditionalBasicBlockInfo::newDefinition(StoreInst *Store,
TypeSizeProvider &TSP) {
// Remove all the aliased reaching definitions
MemoryAccess TargetMA(Store, TSP);
removeDefinitions([&TargetMA] (CondDefPair &P) {
// TODO: don't erase if conditions are complementary
return TargetMA.mayAlias(P.second.MA);
});
// Perform the merge
mergeDefinition({ Conditions, MemoryInstruction(Store, TSP) },
Definitions,
TSP);
}
LoadDefinitionType
ConditionalBasicBlockInfo::newDefinition(LoadInst *Load,
TypeSizeProvider &TSP) {
LoadDefinitionType Result = NoReachingDefinitions;
// Check if it's a self-referencing load
MemoryAccess TargetMA(Load, TSP);
for (auto &P : Definitions) {
auto *Definition = P.second.I;
if (Definition == Load) {
// It's self-referencing, suppress all the matching loads
removeDefinitions([&TargetMA] (CondDefPair &P) {
// TODO: can we embed if it's a load or a store in
// MemoryInstruction?
return isa<LoadInst>(P.second.I) && P.second.MA == TargetMA;
});
Result = SelfReaching;
break;
} else if (TargetMA == P.second.MA) {
Result = HasReachingDefinitions;
}
}
// Add this definition
if (Result == NoReachingDefinitions)
mergeDefinition({ Conditions, MemoryInstruction(Load, TSP) },
Definitions,
TSP);
return Result;
}
vector<pair<Instruction *, MemoryAccess>>
ConditionalBasicBlockInfo::getReachingDefinitions(set<LoadInst *> &WhiteList,
TypeSizeProvider &TSP) {
vector<pair<Instruction *, MemoryAccess>> Result;
for (auto &P : Reaching) {
Instruction *I = P.first.I;
if (auto *Load = dyn_cast<LoadInst>(I)) {
// If it's a load check it's whitelisted
if (WhiteList.count(Load) != 0)
Result.push_back({ Load, P.first.MA });
} else {
// It's a store
Result.push_back({ I, P.first.MA });
}
}
freeContainer(Reaching);
return Result;
}
bool ConditionalBasicBlockInfo::propagateTo(ConditionalBasicBlockInfo &Target,
TypeSizeProvider &TSP) {
bool Changed = false;
// Compute a bit vector with all the conditions that are incompatible with the
// target
llvm::BitVector Banned(SeenConditions.size());
// For each set bit in the target's conditions
for (int SetBitIndex = Target.Conditions.find_first();
SetBitIndex != -1;
SetBitIndex = Target.Conditions.find_next(SetBitIndex)) {
// Consider the opposite condition as banned
int32_t BannedIndex = -Target.SeenConditions[SetBitIndex];
// Check BannedIndex is not explicitly allowed
auto BannedIt = std::find(Target.SeenConditions.begin(),
Target.SeenConditions.end(),
BannedIndex);
bool IsAllowed = BannedIt != Target.SeenConditions.end()
&& Target.Conditions[BannedIt - Target.SeenConditions.begin()];
if (!IsAllowed) {
// Look for the BannedIndex in the current block's seen conditions
auto ConditionIt = std::find(SeenConditions.begin(),
SeenConditions.end(),
BannedIndex);
// If present set the corresponding bit in Banned
if (ConditionIt != SeenConditions.end())
Banned.set(ConditionIt - SeenConditions.begin());
}
}
for (auto &Definition : Definitions) {
// Check if this definition is compatible with the target basic block
llvm::BitVector DefinitionConditions = Definition.first;
DefinitionConditions &= Banned;
if (DefinitionConditions.any())
continue;
// Translate the conditions bitvector to the context of the target BBI
BitVector Translated(Target.SeenConditions.size());
for (int I = Definition.first.find_first();
I != -1;
I = Definition.first.find_next(I)) {
// Make sure the target BBI knows about all the necessary conditinos
assert(I < static_cast<int>(SeenConditions.size()));
unsigned Index = Target.getConditionIndex(SeenConditions[I]);
// Keep the size of the new bitvector in sync
if (Target.SeenConditions.size() != Translated.size())
Translated.resize(Target.SeenConditions.size());
Translated.set(Index);
}
Changed |= Target.mergeDefinition({ Translated, Definition.second },
Target.Reaching,
TSP);
}
return Changed;
}
ConditionalBasicBlockInfo::ConditionsComparison
ConditionalBasicBlockInfo::mergeConditionBits(BitVector &Target,
BitVector &NewConditions) const {
// Find the different bits
BitVector DifferentBits = Target;
DifferentBits ^= NewConditions;
// If they are identical, quit
int FirstBit = DifferentBits.find_first();
if (FirstBit == -1)
return Identical;
// Ensure we only have two non-zero bits
int SecondBit = DifferentBits.find_next(FirstBit);
if (SecondBit == -1 || DifferentBits.find_next(SecondBit) != -1)
return Different;
// Check if the only two different bits are complementary conditions
if (SeenConditions[FirstBit] == -SeenConditions[SecondBit]) {
NewConditions.reset(FirstBit);
NewConditions.reset(SecondBit);
return Complementary;
} else {
return Different;
}
}
bool ConditionalBasicBlockInfo::mergeDefinition(CondDefPair NewDefinition,
vector<CondDefPair> &Targets,
TypeSizeProvider &TSP) const {
BitVector &NewConditionsBV = NewDefinition.first;
assert(NewConditionsBV.size() == SeenConditions.size());
bool Again = false;
bool Result = false;
do {
Again = false;
for (auto TargetIt = Targets.begin();
TargetIt != Targets.end();
TargetIt++) {
CondDefPair &Target = *TargetIt;
// Note that we copy the BitVector, since we're going to modify it
if (Target.second.I == NewDefinition.second.I) {
switch (mergeConditionBits(Target.first, NewConditionsBV)) {
case Identical:
return Result;
case Complementary:
Targets.erase(TargetIt);
Again = true;
Result = true;
break;
case Different:
break;
}
}
}
} while (Again);
Targets.push_back(NewDefinition);
return true;
}
bool ConditionalBasicBlockInfo::mergeDefinition(CondDefPair NewDefinition,
ReachingType &Targets,
TypeSizeProvider &TSP) const {
BitVector &NewConditionsBV = NewDefinition.first;
assert(NewConditionsBV.size() == SeenConditions.size());
bool Again = false;
bool Result = false;
llvm::SmallVector<BitVector, 2> &BVs = Targets[NewDefinition.second];
do {
Again = false;
for (auto TargetIt = BVs.begin(); TargetIt != BVs.end(); TargetIt++) {
switch (mergeConditionBits(*TargetIt, NewConditionsBV)) {
case Identical:
return Result;
case Complementary:
BVs.erase(TargetIt);
Again = true;
Result = true;
break;
case Different:
break;
}
}
} while (Again);
BVs.push_back(NewDefinition.first);
return true;
}
static bool isSupportedPointer(Value *V) {
if (auto *Global = dyn_cast<GlobalVariable>(V))
if (Global->getName() != "env")
return true;
if (isa<AllocaInst>(V))
return true;
return false;
}
template<class BBI, ReachingDefinitionsResult R>
bool ReachingDefinitionsImplPass<BBI, R>::runOnFunction(Function &F) {
DBG("passes", {
if (std::is_same<BBI, ConditionalBasicBlockInfo>::value)
dbg << "Starting ConditionalReachingDefinitionsPass\n";
else
dbg << "Starting ReachingDefinitionsPass\n";
});
for (auto &BB : F) {
if (!BB.empty()) {
if (auto *Call = dyn_cast<CallInst>(&*BB.begin())) {
Function *Callee = Call->getCalledFunction();
// TODO: comparing with "newpc" string is sad
if (Callee != nullptr && Callee->getName() == "newpc")
break;
}
}
BasicBlockBlackList.insert(&BB);
}
TypeSizeProvider TSP(F.getParent()->getDataLayout());
// Initialize queue
unsigned BasicBlockCount = 0;
unsigned BasicBlockVisits = 0;
ReversePostOrderTraversal<Function *> RPOT(&F);
UniquedStack<BasicBlock *> ToVisit;
for (BasicBlock *BB : RPOT) {
ToVisit.insert(BB);
BasicBlockCount++;
}
ToVisit.reverse();
while (!ToVisit.empty()) {
BasicBlockVisits++;
BasicBlock *BB = ToVisit.pop();
auto &Info = DefinitionsMap[BB];
Info.resetDefinitions(TSP);
// Find all the definitions
for (Instruction &I : *BB) {
auto *Store = dyn_cast<StoreInst>(&I);
auto *Load = dyn_cast<LoadInst>(&I);
if (Store != nullptr
&& isSupportedPointer(Store->getPointerOperand())) {
// Record new definition
Info.newDefinition(Store, TSP);
} else if (Load != nullptr
&& isSupportedPointer(Load->getPointerOperand())) {
// Check if it's a new definition and record it
auto LoadType = Info.newDefinition(Load, TSP);
switch (LoadType) {
case NoReachingDefinitions:
NRDLoads.insert(Load);
break;
case SelfReaching:
SelfReachingLoads.insert(Load);
break;
case HasReachingDefinitions:
NRDLoads.erase(Load);
break;
}
}
}
bool IsCall = false;
bool StorePCFound = false;
SmallVector<uint64_t, 3> ConstantStores;
auto It = BB->getTerminator()->getIterator();
while (It != BB->begin()) {
It--;
Instruction *I = &*It;
if (auto *Store = dyn_cast<StoreInst>(I)) {
Value *V = Store->getValueOperand();
if (Store->getPointerOperand()->getName() == "pc") {
StorePCFound = true;
} else if (auto *Constant = dyn_cast<ConstantInt>(V)) {
ConstantStores.push_back(Constant->getLimitedValue());
}
} else if (auto *Call = dyn_cast<CallInst>(I)) {
auto *Callee = Call->getCalledFunction();
if (Callee != nullptr && Callee->getName() == "newpc") {
uint64_t PC = getLimitedValue(Call->getArgOperand(0));
uint64_t Size = getLimitedValue(Call->getArgOperand(1));
auto RAIt = std::find(ConstantStores.begin(),
ConstantStores.end(),
PC + Size);
IsCall = StorePCFound && RAIt != ConstantStores.end();
break;
}
}
}
// TODO: this is an hack and should be replaced once we integrate calling
// convention and call graph in the basic block harvesting process
unsigned SuccessorsCount = succ_end(BB) - succ_begin(BB);
unsigned Size = Info.size();
if (!IsCall && Size * SuccessorsCount <= 5000) {
// Get the identifier of the conditional instruction
int32_t ConditionIndex = getConditionIndex(BB->getTerminator());
// Propagate definitions to successors, checking if actually we changed
// something, and if so re-enqueue them
for (BasicBlock *Successor : successors(BB)) {
if (BasicBlockBlackList.count(Successor) != 0)
continue;
auto &SuccessorInfo = DefinitionsMap[Successor];
if (ConditionIndex != 0) {
SuccessorInfo.addCondition(ConditionIndex);
// If ConditionIndex is positive we're in the true branch, prepare
// ConditionIndex for the false branch
if (ConditionIndex > 0)
ConditionIndex = -ConditionIndex;
}
// Enqueue the successor only if the propagation actually did something
unsigned Old = SuccessorInfo.size();
if (Info.propagateTo(SuccessorInfo, TSP))
ToVisit.insert(Successor);
DBG("rdp-propagation",
dbg << getName(Successor)
<< " got " << (SuccessorInfo.size() - Old) << " new reachers "
<< "from " << getName(BB) << " (had " << Old << ")\n");
}
// We no longer need to keep track of the definitions
Info.clearDefinitions();
}
}
// Collect final information
std::set<LoadInst *> &FreeLoads = NRDLoads;
FreeLoads.insert(SelfReachingLoads.begin(), SelfReachingLoads.end());
for (auto &P : DefinitionsMap) {
BasicBlock *BB = P.first;
BBI &Info = P.second;
// TODO: use a list?
vector<pair<Instruction *, MemoryAccess>> Definitions;
Definitions = Info.getReachingDefinitions(FreeLoads, TSP);
for (Instruction &I : *BB) {
auto *Store = dyn_cast<StoreInst>(&I);
auto *Load = dyn_cast<LoadInst>(&I);
using IMP = pair<Instruction *, MemoryAccess>;
if (Store != nullptr
&& isSupportedPointer(Store->getPointerOperand())) {
// Remove all the reaching definitions aliased by this store
MemoryAccess TargetMA(Store, TSP);
erase_if(Definitions, [&TargetMA] (IMP &P) {
return TargetMA.mayAlias(P.second);
});
Definitions.push_back({ Store, TargetMA });
} else if (Load != nullptr
&& isSupportedPointer(Load->getPointerOperand())) {
// Record all the relevant reaching defininitions
MemoryAccess TargetMA(Load, TSP);
if (FreeLoads.count(Load) != 0) {
// If it's a free load, remove all the matching loads
erase_if(Definitions, [&TargetMA, &TSP] (IMP &P) {
Instruction *I = P.first;
return isa<LoadInst>(I) && MemoryAccess(I, TSP) == TargetMA;
});
} else {
if (R == ReachingDefinitionsResult::ReachedLoads) {
for (auto &Definition : Definitions) {
if (TargetMA == Definition.second) {
ReachedLoads[Definition.first].push_back(Load);
ReachingDefinitionsCount[Load]++;
}
}
}
std::vector<Instruction *> LoadDefinitions;
for (auto &Definition : Definitions)
if (TargetMA == Definition.second)
LoadDefinitions.push_back(Definition.first);
// Save them in ReachingDefinitions
std::sort(LoadDefinitions.begin(), LoadDefinitions.end());
DBG("rdp",
{
dbg << getName(Load) << " is reached by:";
for (auto *Definition : LoadDefinitions)
dbg << " " << getName(Definition);
dbg << "\n";
});
ReachingDefinitions[Load] = std::move(LoadDefinitions);
}
}
}
}
DBG("rdp",
{
dbg << "Basic blocks: " << std::dec << BasicBlockCount << "\n"
<< "Visited: " << std::dec << BasicBlockVisits << "\n"
<< "Average visits per basic block: " << std::setprecision(2)
<< float(BasicBlockVisits) / BasicBlockCount << "\n";
});
if (R == ReachingDefinitionsResult::ReachedLoads) {
DBG("rdp",
for (auto P : ReachedLoads) {
dbg << getName(P.first) << " reaches";
for (auto *Load : P.second)
dbg << " " << getName(Load);
dbg << "\n";
});
}
// Clear all the temporary data that is not part of the analysis result
freeContainer(DefinitionsMap);
freeContainer(FreeLoads);
freeContainer(BasicBlockBlackList);
freeContainer(NRDLoads);
freeContainer(SelfReachingLoads);
DBG("passes", {
if (std::is_same<BBI, ConditionalBasicBlockInfo>::value)
dbg << "Ending ConditionalReachingDefinitionsPass\n";
else
dbg << "Ending ReachingDefinitionsPass\n";
});
return false;
}