forked from OSchip/llvm-project
Template the sparse propagation solver instead of using void pointers
Summary: This avoids using void * as the type of the lattice value and ugly casts needed to make that happen. (If folks want to use references, etc, they can use a reference_wrapper). Reviewers: davide, mssimpso Subscribers: sanjoy, llvm-commits Differential Revision: https://reviews.llvm.org/D38476 llvm-svn: 314734
This commit is contained in:
parent
81df9eb0f2
commit
4d825bcf09
|
@ -30,22 +30,19 @@ class Function;
|
|||
class Instruction;
|
||||
class PHINode;
|
||||
class raw_ostream;
|
||||
class SparseSolver;
|
||||
template <class LatticeVal> class SparseSolver;
|
||||
class TerminatorInst;
|
||||
class Value;
|
||||
template <typename T> class SmallVectorImpl;
|
||||
|
||||
/// AbstractLatticeFunction - This class is implemented by the dataflow instance
|
||||
/// to specify what the lattice values are and how they handle merges etc.
|
||||
/// This gives the client the power to compute lattice values from instructions,
|
||||
/// constants, etc. The requirement is that lattice values must all fit into
|
||||
/// a void*. If a void* is not sufficient, the implementation should use this
|
||||
/// pointer to be a pointer into a uniquing set or something.
|
||||
///
|
||||
class AbstractLatticeFunction {
|
||||
public:
|
||||
using LatticeVal = void *;
|
||||
/// to specify what the lattice values are and how they handle merges etc. This
|
||||
/// gives the client the power to compute lattice values from instructions,
|
||||
/// constants, etc. The current requirement is that lattice values must be
|
||||
/// copyable. At the moment, nothing tries to avoid copying.
|
||||
|
||||
|
||||
template <class LatticeVal> class AbstractLatticeFunction {
|
||||
private:
|
||||
LatticeVal UndefVal, OverdefinedVal, UntrackedVal;
|
||||
|
||||
|
@ -81,7 +78,8 @@ public:
|
|||
/// GetConstant - If the specified lattice value is representable as an LLVM
|
||||
/// constant value, return it. Otherwise return null. The returned value
|
||||
/// must be in the same LLVM type as Val.
|
||||
virtual Constant *GetConstant(LatticeVal LV, Value *Val, SparseSolver &SS) {
|
||||
virtual Constant *GetConstant(LatticeVal LV, Value *Val,
|
||||
SparseSolver<LatticeVal> &SS) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -100,7 +98,8 @@ public:
|
|||
|
||||
/// ComputeInstructionState - Given an instruction and a vector of its operand
|
||||
/// values, compute the result value of the instruction.
|
||||
virtual LatticeVal ComputeInstructionState(Instruction &I, SparseSolver &SS) {
|
||||
virtual LatticeVal ComputeInstructionState(Instruction &I,
|
||||
SparseSolver<LatticeVal> &SS) {
|
||||
return getOverdefinedVal(); // always safe, never useful.
|
||||
}
|
||||
|
||||
|
@ -110,12 +109,11 @@ public:
|
|||
|
||||
/// SparseSolver - This class is a general purpose solver for Sparse Conditional
|
||||
/// Propagation with a programmable lattice function.
|
||||
class SparseSolver {
|
||||
using LatticeVal = AbstractLatticeFunction::LatticeVal;
|
||||
template <class LatticeVal> class SparseSolver {
|
||||
|
||||
/// LatticeFunc - This is the object that knows the lattice and how to do
|
||||
/// compute transfer functions.
|
||||
AbstractLatticeFunction *LatticeFunc;
|
||||
AbstractLatticeFunction<LatticeVal> *LatticeFunc;
|
||||
|
||||
DenseMap<Value *, LatticeVal> ValueState; // The state each value is in.
|
||||
SmallPtrSet<BasicBlock *, 16> BBExecutable; // The bbs that are executable.
|
||||
|
@ -130,7 +128,7 @@ class SparseSolver {
|
|||
std::set<Edge> KnownFeasibleEdges;
|
||||
|
||||
public:
|
||||
explicit SparseSolver(AbstractLatticeFunction *Lattice)
|
||||
explicit SparseSolver(AbstractLatticeFunction<LatticeVal> *Lattice)
|
||||
: LatticeFunc(Lattice) {}
|
||||
SparseSolver(const SparseSolver &) = delete;
|
||||
SparseSolver &operator=(const SparseSolver &) = delete;
|
||||
|
@ -145,7 +143,7 @@ public:
|
|||
/// value. If an value is not in the map, it is returned as untracked,
|
||||
/// unlike the getOrInitValueState method.
|
||||
LatticeVal getLatticeState(Value *V) const {
|
||||
DenseMap<Value*, LatticeVal>::const_iterator I = ValueState.find(V);
|
||||
auto I = ValueState.find(V);
|
||||
return I != ValueState.end() ? I->second : LatticeFunc->getUntrackedVal();
|
||||
}
|
||||
|
||||
|
|
|
@ -36,10 +36,13 @@ using namespace llvm;
|
|||
// AbstractLatticeFunction Implementation
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
AbstractLatticeFunction::~AbstractLatticeFunction() = default;
|
||||
template <class LatticeVal>
|
||||
AbstractLatticeFunction<LatticeVal>::~AbstractLatticeFunction() = default;
|
||||
|
||||
/// PrintValue - Render the specified lattice value to the specified stream.
|
||||
void AbstractLatticeFunction::PrintValue(LatticeVal V, raw_ostream &OS) {
|
||||
template <class LatticeVal>
|
||||
void AbstractLatticeFunction<LatticeVal>::PrintValue(LatticeVal V,
|
||||
raw_ostream &OS) {
|
||||
if (V == UndefVal)
|
||||
OS << "undefined";
|
||||
else if (V == OverdefinedVal)
|
||||
|
@ -59,8 +62,9 @@ void AbstractLatticeFunction::PrintValue(LatticeVal V, raw_ostream &OS) {
|
|||
/// map yet. This function is necessary because not all values should start
|
||||
/// out in the underdefined state... Arguments should be overdefined, and
|
||||
/// constants should be marked as constants.
|
||||
SparseSolver::LatticeVal SparseSolver::getOrInitValueState(Value *V) {
|
||||
DenseMap<Value*, LatticeVal>::iterator I = ValueState.find(V);
|
||||
template <class LatticeVal>
|
||||
LatticeVal SparseSolver<LatticeVal>::getOrInitValueState(Value *V) {
|
||||
auto I = ValueState.find(V);
|
||||
if (I != ValueState.end()) return I->second; // Common case, in the map
|
||||
|
||||
LatticeVal LV;
|
||||
|
@ -85,8 +89,9 @@ SparseSolver::LatticeVal SparseSolver::getOrInitValueState(Value *V) {
|
|||
|
||||
/// UpdateState - When the state for some instruction is potentially updated,
|
||||
/// this function notices and adds I to the worklist if needed.
|
||||
void SparseSolver::UpdateState(Instruction &Inst, LatticeVal V) {
|
||||
DenseMap<Value*, LatticeVal>::iterator I = ValueState.find(&Inst);
|
||||
template <class LatticeVal>
|
||||
void SparseSolver<LatticeVal>::UpdateState(Instruction &Inst, LatticeVal V) {
|
||||
auto I = ValueState.find(&Inst);
|
||||
if (I != ValueState.end() && I->second == V)
|
||||
return; // No change.
|
||||
|
||||
|
@ -97,7 +102,8 @@ void SparseSolver::UpdateState(Instruction &Inst, LatticeVal V) {
|
|||
|
||||
/// MarkBlockExecutable - This method can be used by clients to mark all of
|
||||
/// the blocks that are known to be intrinsically live in the processed unit.
|
||||
void SparseSolver::MarkBlockExecutable(BasicBlock *BB) {
|
||||
template <class LatticeVal>
|
||||
void SparseSolver<LatticeVal>::MarkBlockExecutable(BasicBlock *BB) {
|
||||
DEBUG(dbgs() << "Marking Block Executable: " << BB->getName() << "\n");
|
||||
BBExecutable.insert(BB); // Basic block is executable!
|
||||
BBWorkList.push_back(BB); // Add the block to the work list!
|
||||
|
@ -105,7 +111,9 @@ void SparseSolver::MarkBlockExecutable(BasicBlock *BB) {
|
|||
|
||||
/// markEdgeExecutable - Mark a basic block as executable, adding it to the BB
|
||||
/// work list if it is not already executable...
|
||||
void SparseSolver::markEdgeExecutable(BasicBlock *Source, BasicBlock *Dest) {
|
||||
template <class LatticeVal>
|
||||
void SparseSolver<LatticeVal>::markEdgeExecutable(BasicBlock *Source,
|
||||
BasicBlock *Dest) {
|
||||
if (!KnownFeasibleEdges.insert(Edge(Source, Dest)).second)
|
||||
return; // This edge is already known to be executable!
|
||||
|
||||
|
@ -125,9 +133,9 @@ void SparseSolver::markEdgeExecutable(BasicBlock *Source, BasicBlock *Dest) {
|
|||
|
||||
/// getFeasibleSuccessors - Return a vector of booleans to indicate which
|
||||
/// successors are reachable from a given terminator instruction.
|
||||
void SparseSolver::getFeasibleSuccessors(TerminatorInst &TI,
|
||||
SmallVectorImpl<bool> &Succs,
|
||||
bool AggressiveUndef) {
|
||||
template <class LatticeVal>
|
||||
void SparseSolver<LatticeVal>::getFeasibleSuccessors(
|
||||
TerminatorInst &TI, SmallVectorImpl<bool> &Succs, bool AggressiveUndef) {
|
||||
Succs.resize(TI.getNumSuccessors());
|
||||
if (TI.getNumSuccessors() == 0) return;
|
||||
|
||||
|
@ -208,8 +216,9 @@ void SparseSolver::getFeasibleSuccessors(TerminatorInst &TI,
|
|||
|
||||
/// isEdgeFeasible - Return true if the control flow edge from the 'From'
|
||||
/// basic block to the 'To' basic block is currently feasible...
|
||||
bool SparseSolver::isEdgeFeasible(BasicBlock *From, BasicBlock *To,
|
||||
bool AggressiveUndef) {
|
||||
template <class LatticeVal>
|
||||
bool SparseSolver<LatticeVal>::isEdgeFeasible(BasicBlock *From, BasicBlock *To,
|
||||
bool AggressiveUndef) {
|
||||
SmallVector<bool, 16> SuccFeasible;
|
||||
TerminatorInst *TI = From->getTerminator();
|
||||
getFeasibleSuccessors(*TI, SuccFeasible, AggressiveUndef);
|
||||
|
@ -221,7 +230,8 @@ bool SparseSolver::isEdgeFeasible(BasicBlock *From, BasicBlock *To,
|
|||
return false;
|
||||
}
|
||||
|
||||
void SparseSolver::visitTerminatorInst(TerminatorInst &TI) {
|
||||
template <class LatticeVal>
|
||||
void SparseSolver<LatticeVal>::visitTerminatorInst(TerminatorInst &TI) {
|
||||
SmallVector<bool, 16> SuccFeasible;
|
||||
getFeasibleSuccessors(TI, SuccFeasible, true);
|
||||
|
||||
|
@ -233,7 +243,8 @@ void SparseSolver::visitTerminatorInst(TerminatorInst &TI) {
|
|||
markEdgeExecutable(BB, TI.getSuccessor(i));
|
||||
}
|
||||
|
||||
void SparseSolver::visitPHINode(PHINode &PN) {
|
||||
template <class LatticeVal>
|
||||
void SparseSolver<LatticeVal>::visitPHINode(PHINode &PN) {
|
||||
// The lattice function may store more information on a PHINode than could be
|
||||
// computed from its incoming values. For example, SSI form stores its sigma
|
||||
// functions as PHINodes with a single incoming value.
|
||||
|
@ -279,7 +290,8 @@ void SparseSolver::visitPHINode(PHINode &PN) {
|
|||
UpdateState(PN, PNIV);
|
||||
}
|
||||
|
||||
void SparseSolver::visitInst(Instruction &I) {
|
||||
template <class LatticeVal>
|
||||
void SparseSolver<LatticeVal>::visitInst(Instruction &I) {
|
||||
// PHIs are handled by the propagation logic, they are never passed into the
|
||||
// transfer functions.
|
||||
if (PHINode *PN = dyn_cast<PHINode>(&I))
|
||||
|
@ -295,7 +307,7 @@ void SparseSolver::visitInst(Instruction &I) {
|
|||
visitTerminatorInst(*TI);
|
||||
}
|
||||
|
||||
void SparseSolver::Solve(Function &F) {
|
||||
template <class LatticeVal> void SparseSolver<LatticeVal>::Solve(Function &F) {
|
||||
MarkBlockExecutable(&F.getEntryBlock());
|
||||
|
||||
// Process the work lists until they are empty!
|
||||
|
@ -331,7 +343,8 @@ void SparseSolver::Solve(Function &F) {
|
|||
}
|
||||
}
|
||||
|
||||
void SparseSolver::Print(Function &F, raw_ostream &OS) const {
|
||||
template <class LatticeVal>
|
||||
void SparseSolver<LatticeVal>::Print(Function &F, raw_ostream &OS) const {
|
||||
OS << "\nFUNCTION: " << F.getName() << "\n";
|
||||
for (auto &BB : F) {
|
||||
if (!BBExecutable.count(&BB))
|
||||
|
|
Loading…
Reference in New Issue