diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2017-12-18 20:10:56 +0000 | 
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2017-12-18 20:10:56 +0000 | 
| commit | 044eb2f6afba375a914ac9d8024f8f5142bb912e (patch) | |
| tree | 1475247dc9f9fe5be155ebd4c9069c75aadf8c20 /include/llvm/Analysis/SparsePropagation.h | |
| parent | eb70dddbd77e120e5d490bd8fbe7ff3f8fa81c6b (diff) | |
Notes
Diffstat (limited to 'include/llvm/Analysis/SparsePropagation.h')
| -rw-r--r-- | include/llvm/Analysis/SparsePropagation.h | 504 | 
1 files changed, 416 insertions, 88 deletions
| diff --git a/include/llvm/Analysis/SparsePropagation.h b/include/llvm/Analysis/SparsePropagation.h index d1a54171d8bd..1b8df03b3a1b 100644 --- a/include/llvm/Analysis/SparsePropagation.h +++ b/include/llvm/Analysis/SparsePropagation.h @@ -15,37 +15,35 @@  #ifndef LLVM_ANALYSIS_SPARSEPROPAGATION_H  #define LLVM_ANALYSIS_SPARSEPROPAGATION_H -#include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/SmallPtrSet.h" -#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/Instructions.h" +#include "llvm/Support/Debug.h"  #include <set> -#include <vector> + +#define DEBUG_TYPE "sparseprop"  namespace llvm { -class Value; -class Constant; -class Argument; -class Instruction; -class PHINode; -class TerminatorInst; -class BasicBlock; -class Function; -class SparseSolver; -class raw_ostream; -template <typename T> class SmallVectorImpl; +/// A template for translating between LLVM Values and LatticeKeys. Clients must +/// provide a specialization of LatticeKeyInfo for their LatticeKey type. +template <class LatticeKey> struct LatticeKeyInfo { +  // static inline Value *getValueFromLatticeKey(LatticeKey Key); +  // static inline LatticeKey getLatticeKeyFromValue(Value *V); +}; -/// 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: -  typedef void *LatticeVal; +template <class LatticeKey, class LatticeVal, +          class KeyInfo = LatticeKeyInfo<LatticeKey>> +class SparseSolver; +/// 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 current requirement is that lattice values must be +/// copyable.  At the moment, nothing tries to avoid copying.  Additionally, +/// lattice keys must be able to be used as keys of a mapping data structure. +/// Internally, the generic solver currently uses a DenseMap to map lattice keys +/// to lattice values.  If the lattice key is a non-standard type, a +/// specialization of DenseMapInfo must be provided. +template <class LatticeKey, class LatticeVal> class AbstractLatticeFunction {  private:    LatticeVal UndefVal, OverdefinedVal, UntrackedVal; @@ -56,40 +54,28 @@ public:      OverdefinedVal = overdefinedVal;      UntrackedVal = untrackedVal;    } -  virtual ~AbstractLatticeFunction(); + +  virtual ~AbstractLatticeFunction() = default;    LatticeVal getUndefVal()       const { return UndefVal; }    LatticeVal getOverdefinedVal() const { return OverdefinedVal; }    LatticeVal getUntrackedVal()   const { return UntrackedVal; } -  /// IsUntrackedValue - If the specified Value is something that is obviously -  /// uninteresting to the analysis (and would always return UntrackedVal), -  /// this function can return true to avoid pointless work. -  virtual bool IsUntrackedValue(Value *V) { return false; } +  /// IsUntrackedValue - If the specified LatticeKey is obviously uninteresting +  /// to the analysis (i.e., it would always return UntrackedVal), this +  /// function can return true to avoid pointless work. +  virtual bool IsUntrackedValue(LatticeKey Key) { return false; } -  /// ComputeConstant - Given a constant value, compute and return a lattice -  /// value corresponding to the specified constant. -  virtual LatticeVal ComputeConstant(Constant *C) { -    return getOverdefinedVal(); // always safe +  /// ComputeLatticeVal - Compute and return a LatticeVal corresponding to the +  /// given LatticeKey. +  virtual LatticeVal ComputeLatticeVal(LatticeKey Key) { +    return getOverdefinedVal();    }    /// IsSpecialCasedPHI - Given a PHI node, determine whether this PHI node is    /// one that the we want to handle through ComputeInstructionState.    virtual bool IsSpecialCasedPHI(PHINode *PN) { return false; } -  /// 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) { -    return nullptr; -  } - -  /// ComputeArgument - Given a formal argument value, compute and return a -  /// lattice value corresponding to the specified argument. -  virtual LatticeVal ComputeArgument(Argument *I) { -    return getOverdefinedVal(); // always safe -  } -    /// MergeValues - Compute and return the merge of the two specified lattice    /// values.  Merging should only move one direction down the lattice to    /// guarantee convergence (toward overdefined). @@ -97,67 +83,80 @@ public:      return getOverdefinedVal(); // always safe, never useful.    } -  /// 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) { -    return getOverdefinedVal(); // always safe, never useful. +  /// ComputeInstructionState - Compute the LatticeKeys that change as a result +  /// of executing instruction \p I. Their associated LatticeVals are store in +  /// \p ChangedValues. +  virtual void +  ComputeInstructionState(Instruction &I, +                          DenseMap<LatticeKey, LatticeVal> &ChangedValues, +                          SparseSolver<LatticeKey, LatticeVal> &SS) = 0; + +  /// PrintLatticeVal - Render the given LatticeVal to the specified stream. +  virtual void PrintLatticeVal(LatticeVal LV, raw_ostream &OS); + +  /// PrintLatticeKey - Render the given LatticeKey to the specified stream. +  virtual void PrintLatticeKey(LatticeKey Key, raw_ostream &OS); + +  /// GetValueFromLatticeVal - If the given LatticeVal is representable as an +  /// LLVM value, return it; otherwise, return nullptr. If a type is given, the +  /// returned value must have the same type. This function is used by the +  /// generic solver in attempting to resolve branch and switch conditions. +  virtual Value *GetValueFromLatticeVal(LatticeVal LV, Type *Ty = nullptr) { +    return nullptr;    } - -  /// PrintValue - Render the specified lattice value to the specified stream. -  virtual void PrintValue(LatticeVal V, raw_ostream &OS);  };  /// SparseSolver - This class is a general purpose solver for Sparse Conditional  /// Propagation with a programmable lattice function. -/// +template <class LatticeKey, class LatticeVal, class KeyInfo>  class SparseSolver { -  typedef AbstractLatticeFunction::LatticeVal LatticeVal; -  /// LatticeFunc - This is the object that knows the lattice and how to do +  /// LatticeFunc - This is the object that knows the lattice and how to    /// compute transfer functions. -  AbstractLatticeFunction *LatticeFunc; +  AbstractLatticeFunction<LatticeKey, LatticeVal> *LatticeFunc; + +  /// ValueState - Holds the LatticeVals associated with LatticeKeys. +  DenseMap<LatticeKey, LatticeVal> ValueState; -  DenseMap<Value *, LatticeVal> ValueState;   // The state each value is in. -  SmallPtrSet<BasicBlock *, 16> BBExecutable; // The bbs that are executable. +  /// BBExecutable - Holds the basic blocks that are executable. +  SmallPtrSet<BasicBlock *, 16> BBExecutable; -  std::vector<Instruction *> InstWorkList; // Worklist of insts to process. +  /// ValueWorkList - Holds values that should be processed. +  SmallVector<Value *, 64> ValueWorkList; -  std::vector<BasicBlock *> BBWorkList; // The BasicBlock work list +  /// BBWorkList - Holds basic blocks that should be processed. +  SmallVector<BasicBlock *, 64> BBWorkList; + +  using Edge = std::pair<BasicBlock *, BasicBlock *>;    /// KnownFeasibleEdges - Entries in this set are edges which have already had    /// PHI nodes retriggered. -  typedef std::pair<BasicBlock*,BasicBlock*> Edge;    std::set<Edge> KnownFeasibleEdges; -  SparseSolver(const SparseSolver&) = delete; -  void operator=(const SparseSolver&) = delete; -  public: -  explicit SparseSolver(AbstractLatticeFunction *Lattice) +  explicit SparseSolver( +      AbstractLatticeFunction<LatticeKey, LatticeVal> *Lattice)        : LatticeFunc(Lattice) {} -  ~SparseSolver() { delete LatticeFunc; } +  SparseSolver(const SparseSolver &) = delete; +  SparseSolver &operator=(const SparseSolver &) = delete;    /// Solve - Solve for constants and executable blocks. -  /// -  void Solve(Function &F); +  void Solve(); -  void Print(Function &F, raw_ostream &OS) const; +  void Print(raw_ostream &OS) const; -  /// getLatticeState - Return the LatticeVal object that corresponds to the -  /// 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); +  /// getExistingValueState - Return the LatticeVal object corresponding to the +  /// given value from the ValueState map. If the value is not in the map, +  /// UntrackedVal is returned, unlike the getValueState method. +  LatticeVal getExistingValueState(LatticeKey Key) const { +    auto I = ValueState.find(Key);      return I != ValueState.end() ? I->second : LatticeFunc->getUntrackedVal();    } -  /// getOrInitValueState - Return the LatticeVal object that corresponds to the -  /// value, initializing the value's state if it hasn't been entered into the -  /// 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. -  /// -  LatticeVal getOrInitValueState(Value *V); +  /// getValueState - Return the LatticeVal object corresponding to the given +  /// value from the ValueState map. If the value is not in the map, its state +  /// is initialized. +  LatticeVal getValueState(LatticeKey Key);    /// isEdgeFeasible - Return true if the control flow edge from the 'From'    /// basic block to the 'To' basic block is currently feasible.  If @@ -174,15 +173,16 @@ public:      return BBExecutable.count(BB);    } -private: -  /// UpdateState - When the state for some instruction is potentially updated, -  /// this function notices and adds I to the worklist if needed. -  void 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 MarkBlockExecutable(BasicBlock *BB); +private: +  /// UpdateState - When the state of some LatticeKey is potentially updated to +  /// the given LatticeVal, this function notices and adds the LLVM value +  /// corresponding the key to the work list, if needed. +  void UpdateState(LatticeKey Key, LatticeVal LV); +    /// markEdgeExecutable - Mark a basic block as executable, adding it to the BB    /// work list if it is not already executable.    void markEdgeExecutable(BasicBlock *Source, BasicBlock *Dest); @@ -197,6 +197,334 @@ private:    void visitTerminatorInst(TerminatorInst &TI);  }; +//===----------------------------------------------------------------------===// +//                  AbstractLatticeFunction Implementation +//===----------------------------------------------------------------------===// + +template <class LatticeKey, class LatticeVal> +void AbstractLatticeFunction<LatticeKey, LatticeVal>::PrintLatticeVal( +    LatticeVal V, raw_ostream &OS) { +  if (V == UndefVal) +    OS << "undefined"; +  else if (V == OverdefinedVal) +    OS << "overdefined"; +  else if (V == UntrackedVal) +    OS << "untracked"; +  else +    OS << "unknown lattice value"; +} + +template <class LatticeKey, class LatticeVal> +void AbstractLatticeFunction<LatticeKey, LatticeVal>::PrintLatticeKey( +    LatticeKey Key, raw_ostream &OS) { +  OS << "unknown lattice key"; +} + +//===----------------------------------------------------------------------===// +//                          SparseSolver Implementation +//===----------------------------------------------------------------------===// + +template <class LatticeKey, class LatticeVal, class KeyInfo> +LatticeVal +SparseSolver<LatticeKey, LatticeVal, KeyInfo>::getValueState(LatticeKey Key) { +  auto I = ValueState.find(Key); +  if (I != ValueState.end()) +    return I->second; // Common case, in the map + +  if (LatticeFunc->IsUntrackedValue(Key)) +    return LatticeFunc->getUntrackedVal(); +  LatticeVal LV = LatticeFunc->ComputeLatticeVal(Key); + +  // If this value is untracked, don't add it to the map. +  if (LV == LatticeFunc->getUntrackedVal()) +    return LV; +  return ValueState[Key] = LV; +} + +template <class LatticeKey, class LatticeVal, class KeyInfo> +void SparseSolver<LatticeKey, LatticeVal, KeyInfo>::UpdateState(LatticeKey Key, +                                                                LatticeVal LV) { +  auto I = ValueState.find(Key); +  if (I != ValueState.end() && I->second == LV) +    return; // No change. + +  // Update the state of the given LatticeKey and add its corresponding LLVM +  // value to the work list. +  ValueState[Key] = LV; +  if (Value *V = KeyInfo::getValueFromLatticeKey(Key)) +    ValueWorkList.push_back(V); +} + +template <class LatticeKey, class LatticeVal, class KeyInfo> +void SparseSolver<LatticeKey, LatticeVal, KeyInfo>::MarkBlockExecutable( +    BasicBlock *BB) { +  if (!BBExecutable.insert(BB).second) +    return; +  DEBUG(dbgs() << "Marking Block Executable: " << BB->getName() << "\n"); +  BBWorkList.push_back(BB); // Add the block to the work list! +} + +template <class LatticeKey, class LatticeVal, class KeyInfo> +void SparseSolver<LatticeKey, LatticeVal, KeyInfo>::markEdgeExecutable( +    BasicBlock *Source, BasicBlock *Dest) { +  if (!KnownFeasibleEdges.insert(Edge(Source, Dest)).second) +    return; // This edge is already known to be executable! + +  DEBUG(dbgs() << "Marking Edge Executable: " << Source->getName() << " -> " +               << Dest->getName() << "\n"); + +  if (BBExecutable.count(Dest)) { +    // The destination is already executable, but we just made an edge +    // feasible that wasn't before.  Revisit the PHI nodes in the block +    // because they have potentially new operands. +    for (BasicBlock::iterator I = Dest->begin(); isa<PHINode>(I); ++I) +      visitPHINode(*cast<PHINode>(I)); +  } else { +    MarkBlockExecutable(Dest); +  } +} + +template <class LatticeKey, class LatticeVal, class KeyInfo> +void SparseSolver<LatticeKey, LatticeVal, KeyInfo>::getFeasibleSuccessors( +    TerminatorInst &TI, SmallVectorImpl<bool> &Succs, bool AggressiveUndef) { +  Succs.resize(TI.getNumSuccessors()); +  if (TI.getNumSuccessors() == 0) +    return; + +  if (BranchInst *BI = dyn_cast<BranchInst>(&TI)) { +    if (BI->isUnconditional()) { +      Succs[0] = true; +      return; +    } + +    LatticeVal BCValue; +    if (AggressiveUndef) +      BCValue = +          getValueState(KeyInfo::getLatticeKeyFromValue(BI->getCondition())); +    else +      BCValue = getExistingValueState( +          KeyInfo::getLatticeKeyFromValue(BI->getCondition())); + +    if (BCValue == LatticeFunc->getOverdefinedVal() || +        BCValue == LatticeFunc->getUntrackedVal()) { +      // Overdefined condition variables can branch either way. +      Succs[0] = Succs[1] = true; +      return; +    } + +    // If undefined, neither is feasible yet. +    if (BCValue == LatticeFunc->getUndefVal()) +      return; + +    Constant *C = +        dyn_cast_or_null<Constant>(LatticeFunc->GetValueFromLatticeVal( +            BCValue, BI->getCondition()->getType())); +    if (!C || !isa<ConstantInt>(C)) { +      // Non-constant values can go either way. +      Succs[0] = Succs[1] = true; +      return; +    } + +    // Constant condition variables mean the branch can only go a single way +    Succs[C->isNullValue()] = true; +    return; +  } + +  if (TI.isExceptional()) { +    Succs.assign(Succs.size(), true); +    return; +  } + +  if (isa<IndirectBrInst>(TI)) { +    Succs.assign(Succs.size(), true); +    return; +  } + +  SwitchInst &SI = cast<SwitchInst>(TI); +  LatticeVal SCValue; +  if (AggressiveUndef) +    SCValue = getValueState(KeyInfo::getLatticeKeyFromValue(SI.getCondition())); +  else +    SCValue = getExistingValueState( +        KeyInfo::getLatticeKeyFromValue(SI.getCondition())); + +  if (SCValue == LatticeFunc->getOverdefinedVal() || +      SCValue == LatticeFunc->getUntrackedVal()) { +    // All destinations are executable! +    Succs.assign(TI.getNumSuccessors(), true); +    return; +  } + +  // If undefined, neither is feasible yet. +  if (SCValue == LatticeFunc->getUndefVal()) +    return; + +  Constant *C = dyn_cast_or_null<Constant>(LatticeFunc->GetValueFromLatticeVal( +      SCValue, SI.getCondition()->getType())); +  if (!C || !isa<ConstantInt>(C)) { +    // All destinations are executable! +    Succs.assign(TI.getNumSuccessors(), true); +    return; +  } +  SwitchInst::CaseHandle Case = *SI.findCaseValue(cast<ConstantInt>(C)); +  Succs[Case.getSuccessorIndex()] = true; +} + +template <class LatticeKey, class LatticeVal, class KeyInfo> +bool SparseSolver<LatticeKey, LatticeVal, KeyInfo>::isEdgeFeasible( +    BasicBlock *From, BasicBlock *To, bool AggressiveUndef) { +  SmallVector<bool, 16> SuccFeasible; +  TerminatorInst *TI = From->getTerminator(); +  getFeasibleSuccessors(*TI, SuccFeasible, AggressiveUndef); + +  for (unsigned i = 0, e = TI->getNumSuccessors(); i != e; ++i) +    if (TI->getSuccessor(i) == To && SuccFeasible[i]) +      return true; + +  return false; +} + +template <class LatticeKey, class LatticeVal, class KeyInfo> +void SparseSolver<LatticeKey, LatticeVal, KeyInfo>::visitTerminatorInst( +    TerminatorInst &TI) { +  SmallVector<bool, 16> SuccFeasible; +  getFeasibleSuccessors(TI, SuccFeasible, true); + +  BasicBlock *BB = TI.getParent(); + +  // Mark all feasible successors executable... +  for (unsigned i = 0, e = SuccFeasible.size(); i != e; ++i) +    if (SuccFeasible[i]) +      markEdgeExecutable(BB, TI.getSuccessor(i)); +} + +template <class LatticeKey, class LatticeVal, class KeyInfo> +void SparseSolver<LatticeKey, LatticeVal, KeyInfo>::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. +  if (LatticeFunc->IsSpecialCasedPHI(&PN)) { +    DenseMap<LatticeKey, LatticeVal> ChangedValues; +    LatticeFunc->ComputeInstructionState(PN, ChangedValues, *this); +    for (auto &ChangedValue : ChangedValues) +      if (ChangedValue.second != LatticeFunc->getUntrackedVal()) +        UpdateState(ChangedValue.first, ChangedValue.second); +    return; +  } + +  LatticeKey Key = KeyInfo::getLatticeKeyFromValue(&PN); +  LatticeVal PNIV = getValueState(Key); +  LatticeVal Overdefined = LatticeFunc->getOverdefinedVal(); + +  // If this value is already overdefined (common) just return. +  if (PNIV == Overdefined || PNIV == LatticeFunc->getUntrackedVal()) +    return; // Quick exit + +  // Super-extra-high-degree PHI nodes are unlikely to ever be interesting, +  // and slow us down a lot.  Just mark them overdefined. +  if (PN.getNumIncomingValues() > 64) { +    UpdateState(Key, Overdefined); +    return; +  } + +  // Look at all of the executable operands of the PHI node.  If any of them +  // are overdefined, the PHI becomes overdefined as well.  Otherwise, ask the +  // transfer function to give us the merge of the incoming values. +  for (unsigned i = 0, e = PN.getNumIncomingValues(); i != e; ++i) { +    // If the edge is not yet known to be feasible, it doesn't impact the PHI. +    if (!isEdgeFeasible(PN.getIncomingBlock(i), PN.getParent(), true)) +      continue; + +    // Merge in this value. +    LatticeVal OpVal = +        getValueState(KeyInfo::getLatticeKeyFromValue(PN.getIncomingValue(i))); +    if (OpVal != PNIV) +      PNIV = LatticeFunc->MergeValues(PNIV, OpVal); + +    if (PNIV == Overdefined) +      break; // Rest of input values don't matter. +  } + +  // Update the PHI with the compute value, which is the merge of the inputs. +  UpdateState(Key, PNIV); +} + +template <class LatticeKey, class LatticeVal, class KeyInfo> +void SparseSolver<LatticeKey, LatticeVal, KeyInfo>::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)) +    return visitPHINode(*PN); + +  // Otherwise, ask the transfer function what the result is.  If this is +  // something that we care about, remember it. +  DenseMap<LatticeKey, LatticeVal> ChangedValues; +  LatticeFunc->ComputeInstructionState(I, ChangedValues, *this); +  for (auto &ChangedValue : ChangedValues) +    if (ChangedValue.second != LatticeFunc->getUntrackedVal()) +      UpdateState(ChangedValue.first, ChangedValue.second); + +  if (TerminatorInst *TI = dyn_cast<TerminatorInst>(&I)) +    visitTerminatorInst(*TI); +} + +template <class LatticeKey, class LatticeVal, class KeyInfo> +void SparseSolver<LatticeKey, LatticeVal, KeyInfo>::Solve() { +  // Process the work lists until they are empty! +  while (!BBWorkList.empty() || !ValueWorkList.empty()) { +    // Process the value work list. +    while (!ValueWorkList.empty()) { +      Value *V = ValueWorkList.back(); +      ValueWorkList.pop_back(); + +      DEBUG(dbgs() << "\nPopped off V-WL: " << *V << "\n"); + +      // "V" got into the work list because it made a transition. See if any +      // users are both live and in need of updating. +      for (User *U : V->users()) +        if (Instruction *Inst = dyn_cast<Instruction>(U)) +          if (BBExecutable.count(Inst->getParent())) // Inst is executable? +            visitInst(*Inst); +    } + +    // Process the basic block work list. +    while (!BBWorkList.empty()) { +      BasicBlock *BB = BBWorkList.back(); +      BBWorkList.pop_back(); + +      DEBUG(dbgs() << "\nPopped off BBWL: " << *BB); + +      // Notify all instructions in this basic block that they are newly +      // executable. +      for (Instruction &I : *BB) +        visitInst(I); +    } +  } +} + +template <class LatticeKey, class LatticeVal, class KeyInfo> +void SparseSolver<LatticeKey, LatticeVal, KeyInfo>::Print( +    raw_ostream &OS) const { +  if (ValueState.empty()) +    return; + +  LatticeKey Key; +  LatticeVal LV; + +  OS << "ValueState:\n"; +  for (auto &Entry : ValueState) { +    std::tie(Key, LV) = Entry; +    if (LV == LatticeFunc->getUntrackedVal()) +      continue; +    OS << "\t"; +    LatticeFunc->PrintLatticeVal(LV, OS); +    OS << ": "; +    LatticeFunc->PrintLatticeKey(Key, OS); +    OS << "\n"; +  } +}  } // end namespace llvm +#undef DEBUG_TYPE +  #endif // LLVM_ANALYSIS_SPARSEPROPAGATION_H | 
