Files
revng-revng/reachingdefinitions.h
T
Alessandro Di Federico c8806c7dae Improve the ConditionalReachingDefinitionPass
This commit reworks quite heavily the `ConditionNumberingPass` and the
`ConditionalReachingDefinitionPass`.

* Increase debugging information for both passes.

* `ConditionNumberingPass`: rename the concept basic blocks "defining" a
  condition to the concept of basic blocks "resetting" a condition.
* `ConditionNumberingPass`: add to the list of basic blocks resetting a
  condition also the basic block post-dominating all the branches
  associated to that condition. In this way,
  `ConditionalReachingDefinitionPass` will not propagate the condition
  after them.
* `ConditionalBasicBlockInfo::mergeDefinition`: merge policy for
  condition bits is now simply or-merging them.
2016-12-04 00:28:58 +01:00

393 lines
13 KiB
C++

#ifndef _REACHINGDEFINITIONS_H
#define _REACHINGDEFINITIONS_H
//
// This file is distributed under the MIT License. See LICENSE.md for details.
//
// Standard includes
#include <unordered_set>
#include <vector>
// LLVM includes
#include "llvm/Pass.h"
#include "llvm/ADT/SmallBitVector.h"
#include "llvm/ADT/SmallSet.h"
// Local includes
#include "datastructures.h"
#include "debug.h"
#include "memoryaccess.h"
#define BitVector SmallBitVector
namespace llvm {
class Instruction;
class StoreInst;
class LoadInst;
class Value;
class BranchInst;
class TerminatorInst;
};
// TODO: [speedup] Use LoadStorePtr
// TODO: store in definitions/reaching the MemoryAccess
enum class ReachingDefinitionsResult {
ReachingDefinitions,
ReachedLoads
};
template<class BBI, ReachingDefinitionsResult R>
class ReachingDefinitionsImplPass;
enum LoadDefinitionType {
NoReachingDefinitions, ///< No one can reach it
SelfReaching, ///< Can see it self
HasReachingDefinitions
};
struct MemoryInstruction {
MemoryInstruction(llvm::Instruction *I,
TypeSizeProvider &TSP) : I(I), MA(I, TSP) { }
MemoryInstruction(llvm::StoreInst *I,
TypeSizeProvider &TSP) : I(I), MA(I, TSP) { }
MemoryInstruction(llvm::LoadInst *I,
TypeSizeProvider &TSP) : I(I), MA(I, TSP) { }
bool operator<(const MemoryInstruction Other) const {
return I < Other.I;
}
bool operator==(const MemoryInstruction Other) const {
return I == Other.I;
}
llvm::Instruction *I;
MemoryAccess MA;
};
template<class Container, class UnaryPredicate>
static inline void erase_if(Container &C, UnaryPredicate P) {
C.erase(std::remove_if(C.begin(), C.end(), P), C.end());
}
namespace std {
template <> struct hash<MemoryInstruction>
{
size_t operator()(const MemoryInstruction & MI) const {
return std::hash<llvm::Instruction *>()(MI.I);
}
};
}
class BasicBlockInfo {
public:
unsigned addCondition(int32_t ConditionIndex) { assert(false); }
void resetDefinitions(TypeSizeProvider &TSP) {
Definitions.clear();
// for (llvm::Instruction *I : Reaching)
// Definitions.push_back(MemoryInstruction(I, TSP));
std::copy(Reaching.begin(),
Reaching.end(),
std::back_inserter(Definitions));
}
unsigned size() const { return Reaching.size(); }
void clearDefinitions() {
Definitions.clear();
}
void newDefinition(llvm::StoreInst *Store, TypeSizeProvider &TSP);
LoadDefinitionType newDefinition(llvm::LoadInst *Load,
TypeSizeProvider &TSP);
bool propagateTo(BasicBlockInfo &Target,
TypeSizeProvider &TSP,
const llvm::SmallVector<int32_t, 2> &DefinedIndexes,
int32_t NewConditionIndex);
std::vector<std::pair<llvm::Instruction *, MemoryAccess>>
getReachingDefinitions(std::set<llvm::LoadInst *> &WhiteList,
TypeSizeProvider &TSP);
void dump(std::ostream &Output);
private:
template<class UnaryPredicate>
void removeDefinitions(UnaryPredicate P) {
erase_if(Definitions, P);
}
private:
// llvm::SmallSet<llvm::Instruction *, 3> Reaching;
std::unordered_set<MemoryInstruction> Reaching;
std::vector<MemoryInstruction> Definitions;
};
class ConditionalBasicBlockInfo {
public:
unsigned addCondition(int32_t ConditionIndex) {
unsigned Result = getConditionIndex(ConditionIndex);
Conditions.set(Result);
return Result;
}
bool hasCondition(int32_t ConditionIndex) {
unsigned Result = getConditionIndex(ConditionIndex);
return Conditions[Result];
}
void resetDefinitions(TypeSizeProvider &TSP) {
for (auto &P : Reaching)
Definitions.push_back({ P.second, P.first });
}
unsigned size() const { return Reaching.size(); }
void clearDefinitions() {
Definitions.clear();
}
void newDefinition(llvm::StoreInst *Store, TypeSizeProvider &TSP);
LoadDefinitionType newDefinition(llvm::LoadInst *Load,
TypeSizeProvider &TSP);
bool propagateTo(ConditionalBasicBlockInfo &Target,
TypeSizeProvider &TSP,
const llvm::SmallVector<int32_t, 2> &DefinedIndexes,
int32_t NewConditionIndex);
std::vector<std::pair<llvm::Instruction *, MemoryAccess>>
getReachingDefinitions(std::set<llvm::LoadInst *> &WhiteList,
TypeSizeProvider &TSP);
void dump(std::ostream& Output);
private:
using CondDefPair = std::pair<llvm::BitVector, MemoryInstruction>;
using ReachingType = std::unordered_map<MemoryInstruction, llvm::BitVector>;
enum ConditionsComparison {
Identical,
Different,
Complementary
};
private:
/// \brief Set the bit corresponding to \p Index in \p Target, if present in
/// SeenCondtions.
bool setIndexIfSeen(llvm::BitVector &Target, int32_t Index) const;
template<class UnaryPredicate>
void removeDefinitions(UnaryPredicate P) {
erase_if(Definitions, P);
}
unsigned getConditionIndex(uint32_t ConditionIndex) {
auto It = std::find(SeenConditions.begin(),
SeenConditions.end(),
ConditionIndex);
if (It != SeenConditions.end()) {
return It - SeenConditions.begin();
} else {
SeenConditions.push_back(ConditionIndex);
auto NewSize = SeenConditions.size();
Conditions.resize(NewSize);
for (auto &P : Reaching)
P.second.resize(NewSize);
for (CondDefPair &Definition : Definitions)
Definition.first.resize(NewSize);
return NewSize - 1;
}
}
bool mergeDefinition(CondDefPair NewDefinition,
std::vector<CondDefPair> &Targets,
TypeSizeProvider &TSP) const;
bool mergeDefinition(CondDefPair NewDefinition,
ReachingType &Targets,
TypeSizeProvider &TSP) const;
private:
// Seen conditions
std::vector<int32_t> SeenConditions;
// TODO: switch to list?
ReachingType Reaching;
std::vector<CondDefPair> Definitions;
llvm::BitVector Conditions;
};
using ReachingDefinitionsPass = ReachingDefinitionsImplPass<BasicBlockInfo,
ReachingDefinitionsResult::ReachingDefinitions>;
using ConditionalReachingDefinitionsPass =
ReachingDefinitionsImplPass<ConditionalBasicBlockInfo,
ReachingDefinitionsResult::ReachingDefinitions>;
using ReachedLoadsPass =
ReachingDefinitionsImplPass<BasicBlockInfo,
ReachingDefinitionsResult::ReachedLoads>;
using ConditionalReachedLoadsPass =
ReachingDefinitionsImplPass<ConditionalBasicBlockInfo,
ReachingDefinitionsResult::ReachedLoads>;
template<class BBI, ReachingDefinitionsResult R>
class ReachingDefinitionsImplPass : public llvm::FunctionPass {
public:
static char ID;
ReachingDefinitionsImplPass() : llvm::FunctionPass(ID) { };
bool runOnFunction(llvm::Function &F) override;
void getAnalysisUsage(llvm::AnalysisUsage &AU) const override;
const std::vector<llvm::LoadInst *> &
getReachedLoads(llvm::Instruction *I);
const std::vector<llvm::Instruction *> &
getReachingDefinitions(llvm::LoadInst *Load);
unsigned getReachingDefinitionsCount(llvm::LoadInst *Load);
virtual void releaseMemory() override {
DBG("release", {
dbg << "ReachingDefinitionsImplPass is releasing memory\n";
});
freeContainer(ReachedLoads);
freeContainer(ReachingDefinitions);
freeContainer(ReachingDefinitionsCount);
}
private:
int32_t getConditionIndex(llvm::TerminatorInst *T);
const llvm::SmallVector<int32_t, 2> &getDefinedConditions(llvm::BasicBlock *BB);
private:
using BasicBlock = llvm::BasicBlock;
using LoadInst = llvm::LoadInst;
using Instruction = llvm::Instruction;
std::map<BasicBlock *, BBI> DefinitionsMap;
std::set<BasicBlock *> BasicBlockBlackList;
std::set<LoadInst *> NRDLoads;
std::set<LoadInst *> SelfReachingLoads;
std::map<Instruction *, std::vector<LoadInst *>> ReachedLoads;
std::map<LoadInst *, std::vector<Instruction *>> ReachingDefinitions;
std::map<LoadInst *, unsigned> ReachingDefinitionsCount;
};
/// The ConditionNumberingPass loops over all the conditional branch
/// instructions in the program and tries to identify those that are based on
/// exactly the same condition, i.e., the pair for which can be sure that, if
/// the first branch is taken, then also the second branch will be taken. This
/// is particularly useful to handle consecutive predeicate instructions.
///
/// Two conditions are considered the same, if they actually are the same or if
/// they compute exactly the same operations on the same operands. To
/// efficiently identify which branch instructions use the same conditions we
/// populate an hashmap with a custom hash function. At the end, we will discard
/// all the entries of the hashmap with a single entry, since we're not
/// interested in considering a condition if it doesn't have at least a
/// companion branch instruction. Each condition with at least two branches
/// using it is assigned a unique identifier, the condition index.
///
/// The ConditionNumberingPass also provides, for each condition index, a list
/// of "reset" basic blocks, i.e., a list of basic blocks which define at least
/// one of the values involved in the computation of the condition. Such a list
/// can be used to understand when it doesn't make sense for an analysis to
/// consider that a certain condition is still holding.
///
/// "reset" basic blocks also include the last basic block that might be
/// affected by the associated condition index. This is useful to prevent an
/// analysis from keeping track of a condition index which we can be sure will
/// never be used again. The last basic block that might be affected by a
/// condition index is the immediate post-dominator of the set of basic blocks
/// containing the branches associated to that condition index.
///
/// The following figures examplifies the situation: BB1 and BB2 share the same
/// condition, BB3 is their immediate post-dominator. To easily identify it as
/// such we introduce a temporary basic block BB0 and make it a predecessor of
/// both BB1 and BB2. Then, we compute the post-dominator tree and ask for the
/// immediate post-domiantor of BB0, obtaining BB3.
///
/// +-----------+
/// | |
/// +- - - - - -+ BB0 +- - - - -+
/// | | | |
/// +-----------+
/// | |
///
/// +-----v-----+ +-----v-----+
/// | | | |
/// +---+ BB1 +---+ +---+ BB2 +---+
/// | | | | | | | |
/// | +-----------+ | | +-----------+ |
/// | | | |
/// | | | |
/// +-----v-----+ +-----v-----+ +-----v-----+ +-----v-----+
/// | | | | | | | |
/// | | | | | | | |
/// | | | | | | | |
/// +-----+-----+ +-----+-----+ +-----+-----+ +-----+-----+
/// | | | |
/// | | | |
/// | +-----v-----+ | +-----v-----+
/// | | | | | |
/// +-------------> <-------+ | |
/// | | | |
/// +-----+-----+ +-----+-----+
/// | |
/// | |
/// | +-----------+ |
/// | | | |
/// +----------> BB3 <----------+
/// | |
/// +-----+-----+
/// |
/// |
/// v
class ConditionNumberingPass : public llvm::FunctionPass {
public:
static char ID;
static const llvm::SmallVector<int32_t, 2> NoDefinedConditions;
ConditionNumberingPass() : llvm::FunctionPass(ID) { };
bool runOnFunction(llvm::Function &F) override;
void getAnalysisUsage(llvm::AnalysisUsage &AU) const override {
AU.addRequired<ReachingDefinitionsPass>();
AU.setPreservesAll();
}
int32_t getConditionIndex(llvm::TerminatorInst *T) {
return BranchConditionNumberMap[T];
}
const llvm::SmallVector<int32_t, 2> &getDefinedConditions(llvm::BasicBlock *BB) const {
auto It = DefinedConditions.find(BB);
if (It == DefinedConditions.end())
return NoDefinedConditions;
else
return It->second;
}
virtual void releaseMemory() override {
DBG("release", { dbg << "ConditionNumberingPass is releasing memory\n"; });
freeContainer(DefinedConditions);
freeContainer(BranchConditionNumberMap);
}
private:
std::map<llvm::BasicBlock *, llvm::SmallVector<int32_t, 2>> DefinedConditions;
std::map<llvm::TerminatorInst *, int32_t> BranchConditionNumberMap;
};
#endif // _REACHINGDEFINITIONS_H