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:
Daniel Berlin 2017-10-02 22:49:49 +00:00
parent 81df9eb0f2
commit 4d825bcf09
2 changed files with 46 additions and 35 deletions

View File

@ -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();
}

View File

@ -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))