[llvm] b5ba5d2 - [DebugInfo][NFC] Represent DbgValues with multiple ops in IRefLDV

Stephen Tozer via llvm-commits llvm-commits at lists.llvm.org
Mon Aug 22 10:05:17 PDT 2022


Author: Stephen Tozer
Date: 2022-08-22T18:04:38+01:00
New Revision: b5ba5d2aab11726218c75daaaf20d42a963a6c39

URL: https://github.com/llvm/llvm-project/commit/b5ba5d2aab11726218c75daaaf20d42a963a6c39
DIFF: https://github.com/llvm/llvm-project/commit/b5ba5d2aab11726218c75daaaf20d42a963a6c39.diff

LOG: [DebugInfo][NFC] Represent DbgValues with multiple ops in IRefLDV

In preparation for allowing InstrRefBasedLDV to handle DBG_VALUE_LIST,
this patch updates the internal representation that it uses to represent
debug values to store a list of values. This is one of the more
significant changes in terms of line count, but is fairly simple and
should not affect the output of this pass.

Differential Revision: https://reviews.llvm.org/D128177

Added: 
    

Modified: 
    llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp
    llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h
    llvm/unittests/CodeGen/InstrRefLDVTest.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp b/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp
index 63a01e33de6f2..66e86612e9bd3 100644
--- a/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp
+++ b/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp
@@ -155,6 +155,8 @@ static cl::opt<unsigned>
                          cl::desc("livedebugvalues-stack-ws-limit"),
                          cl::init(250));
 
+DbgOpID DbgOpID::UndefID = DbgOpID(0xffffffff);
+
 /// Tracker for converting machine value locations and variable values into
 /// variable locations (the output of LiveDebugValues), recorded as DBG_VALUEs
 /// specifying block live-in locations and transfers within blocks.
@@ -259,7 +261,7 @@ class TransferTracker {
   /// object fields to track variable locations as we step through the block.
   /// FIXME: could just examine mloctracker instead of passing in \p mlocs?
   void
-  loadInlocs(MachineBasicBlock &MBB, ValueTable &MLocs,
+  loadInlocs(MachineBasicBlock &MBB, ValueTable &MLocs, DbgOpIDMap &DbgOpStore,
              const SmallVectorImpl<std::pair<DebugVariable, DbgValue>> &VLocs,
              unsigned NumLocs) {
     ActiveMLocs.clear();
@@ -284,9 +286,13 @@ class TransferTracker {
 
     // Initialized the preferred-location map with illegal locations, to be
     // filled in later.
-    for (const auto &VLoc : VLocs)
-      if (VLoc.second.Kind == DbgValue::Def)
-        ValueToLoc.insert({VLoc.second.ID, LocIdx::MakeIllegalLoc()});
+    for (const auto &VLoc : VLocs) {
+      if (VLoc.second.Kind == DbgValue::Def &&
+          !VLoc.second.getDbgOpID(0).isConst()) {
+        ValueToLoc.insert({DbgOpStore.find(VLoc.second.getDbgOpID(0)).ID,
+                           LocIdx::MakeIllegalLoc()});
+      }
+    }
 
     ActiveMLocs.reserve(VLocs.size());
     ActiveVLocs.reserve(VLocs.size());
@@ -320,14 +326,16 @@ class TransferTracker {
 
     // Now map variables to their picked LocIdxes.
     for (const auto &Var : VLocs) {
-      if (Var.second.Kind == DbgValue::Const) {
+      DbgOpID OpID = Var.second.getDbgOpID(0);
+      DbgOp Op = DbgOpStore.find(OpID);
+      if (Var.second.Kind == DbgValue::Def && OpID.isConst()) {
         PendingDbgValues.push_back(
-            emitMOLoc(*Var.second.MO, Var.first, Var.second.Properties));
+            emitMOLoc(Op.MO, Var.first, Var.second.Properties));
         continue;
       }
 
       // If the value has no location, we can't make a variable location.
-      const ValueIDNum &Num = Var.second.ID;
+      const ValueIDNum &Num = Op.ID;
       auto ValuesPreferredLoc = ValueToLoc.find(Num);
       if (ValuesPreferredLoc->second.isIllegal()) {
         // If it's a def that occurs in this block, register it as a
@@ -674,17 +682,43 @@ ValueIDNum ValueIDNum::EmptyValue = {UINT_MAX, UINT_MAX, UINT_MAX};
 ValueIDNum ValueIDNum::TombstoneValue = {UINT_MAX, UINT_MAX, UINT_MAX - 1};
 
 #ifndef NDEBUG
-void DbgValue::dump(const MLocTracker *MTrack) const {
-  if (Kind == Const) {
-    MO->dump();
-  } else if (Kind == NoVal) {
-    dbgs() << "NoVal(" << BlockNo << ")";
-  } else if (Kind == VPHI) {
-    dbgs() << "VPHI(" << BlockNo << "," << MTrack->IDAsString(ID) << ")";
+void ResolvedDbgOp::dump(const MLocTracker *MTrack) const {
+  if (IsConst) {
+    dbgs() << MO;
   } else {
-    assert(Kind == Def);
+    dbgs() << MTrack->LocIdxToName(Loc);
+  }
+}
+void DbgOp::dump(const MLocTracker *MTrack) const {
+  if (IsConst) {
+    dbgs() << MO;
+  } else if (!isUndef()) {
     dbgs() << MTrack->IDAsString(ID);
   }
+}
+void DbgOpID::dump(const MLocTracker *MTrack, const DbgOpIDMap *OpStore) const {
+  if (!OpStore) {
+    dbgs() << "ID(" << asU32() << ")";
+  } else {
+    OpStore->find(*this).dump(MTrack);
+  }
+}
+void DbgValue::dump(const MLocTracker *MTrack,
+                    const DbgOpIDMap *OpStore) const {
+  if (Kind == NoVal) {
+    dbgs() << "NoVal(" << BlockNo << ")";
+  } else if (Kind == VPHI || Kind == Def) {
+    if (Kind == VPHI)
+      dbgs() << "VPHI(" << BlockNo << ",";
+    else
+      dbgs() << "Def(";
+    for (unsigned Idx = 0; Idx < getDbgOpIDs().size(); ++Idx) {
+      getDbgOpID(Idx).dump(MTrack, OpStore);
+      if (Idx != 0)
+        dbgs() << ",";
+    }
+    dbgs() << ")";
+  }
   if (Properties.Indirect)
     dbgs() << " indir";
   if (Properties.DIExpr)
@@ -1076,8 +1110,9 @@ bool InstrRefBasedLDV::transferDebugValue(const MachineInstr &MI) {
   // contribute to locations in this block, but don't propagate further.
   // Interpret it like a DBG_VALUE $noreg.
   if (MI.isDebugValueList()) {
+    SmallVector<DbgOpID> EmptyDebugOps;
     if (VTracker)
-      VTracker->defVar(MI, Properties, None);
+      VTracker->defVar(MI, Properties, EmptyDebugOps);
     if (TTracker)
       TTracker->redefVar(MI, Properties, None);
     return true;
@@ -1094,16 +1129,21 @@ bool InstrRefBasedLDV::transferDebugValue(const MachineInstr &MI) {
   // locations are already solved, and we report this DBG_VALUE and the value
   // it refers to to VLocTracker.
   if (VTracker) {
-    if (MO.isReg()) {
-      // Feed defVar the new variable location, or if this is a
-      // DBG_VALUE $noreg, feed defVar None.
-      if (MO.getReg())
-        VTracker->defVar(MI, Properties, MTracker->readReg(MO.getReg()));
-      else
-        VTracker->defVar(MI, Properties, None);
-    } else if (MO.isImm() || MO.isFPImm() || MO.isCImm()) {
-      VTracker->defVar(MI, MO);
+    SmallVector<DbgOpID> DebugOps;
+    // Feed defVar the new variable location, or if this is a DBG_VALUE $noreg,
+    // feed defVar None.
+    if (!MI.isUndefDebugValue()) {
+      // There should be no undef registers here, as we've screened for undef
+      // debug values.
+      if (MO.isReg()) {
+        DebugOps.push_back(DbgOpStore.insert(MTracker->readReg(MO.getReg())));
+      } else if (MO.isImm() || MO.isFPImm() || MO.isCImm()) {
+        DebugOps.push_back(DbgOpStore.insert(MO));
+      } else {
+        llvm_unreachable("Unexpected debug operand type.");
+      }
     }
+    VTracker->defVar(MI, Properties, DebugOps);
   }
 
   // If performing final tracking of transfers, report this variable definition
@@ -1287,8 +1327,11 @@ bool InstrRefBasedLDV::transferDebugInstrRef(MachineInstr &MI,
   // for DBG_INSTR_REFs as DBG_VALUEs (just, the former can refer to values that
   // aren't immediately available).
   DbgValueProperties Properties(Expr, false, false);
+  SmallVector<DbgOpID> DbgOpIDs;
+  if (NewID)
+    DbgOpIDs.push_back(DbgOpStore.insert(*NewID));
   if (VTracker)
-    VTracker->defVar(MI, Properties, NewID);
+    VTracker->defVar(MI, Properties, DbgOpIDs);
 
   // If we're on the final pass through the function, decompose this INSTR_REF
   // into a plain DBG_VALUE.
@@ -1334,6 +1377,7 @@ bool InstrRefBasedLDV::transferDebugInstrRef(MachineInstr &MI,
   // FoundLoc is None.
   // (XXX -- could morph the DBG_INSTR_REF in the future).
   MachineInstr *DbgMI = MTracker->emitLoc(FoundLoc, V, Properties);
+
   TTracker->PendingDbgValues.push_back(DbgMI);
   TTracker->flushDbgValues(MI.getIterator(), nullptr);
   return true;
@@ -2397,7 +2441,7 @@ Optional<ValueIDNum> InstrRefBasedLDV::pickVPHILoc(
       return None;
     const DbgValue &OutVal = *OutValIt->second;
 
-    if (OutVal.Kind == DbgValue::Const || OutVal.Kind == DbgValue::NoVal)
+    if (OutVal.getDbgOpID(0).isConst() || OutVal.Kind == DbgValue::NoVal)
       // Consts and no-values cannot have locations we can join on.
       return None;
 
@@ -2410,8 +2454,8 @@ Optional<ValueIDNum> InstrRefBasedLDV::pickVPHILoc(
     // present. Do the same for VPHIs where we know the VPHI value.
     if (OutVal.Kind == DbgValue::Def ||
         (OutVal.Kind == DbgValue::VPHI && OutVal.BlockNo != MBB.getNumber() &&
-         OutVal.ID != ValueIDNum::EmptyValue)) {
-      ValueIDNum ValToLookFor = OutVal.ID;
+         !OutVal.getDbgOpID(0).isUndef())) {
+      ValueIDNum ValToLookFor = DbgOpStore.find(OutVal.getDbgOpID(0)).ID;
       // Search the live-outs of the predecessor for the specified value.
       for (unsigned int I = 0; I < NumLocs; ++I) {
         if (MOutLocs[ThisBBNum][I] == ValToLookFor)
@@ -2543,7 +2587,7 @@ bool InstrRefBasedLDV::vlocJoin(
       return false;
     if (V.second->Kind == DbgValue::NoVal)
       return false;
-    if (V.second->Kind == DbgValue::Const && FirstVal.Kind != DbgValue::Const)
+    if (!V.second->hasJoinableLocOps(FirstVal))
       return false;
   }
 
@@ -2556,7 +2600,7 @@ bool InstrRefBasedLDV::vlocJoin(
     // If both values are not equal but have equal non-empty IDs then they refer
     // to the same value from 
diff erent sources (e.g. one is VPHI and the other
     // is Def), which does not cause disagreement.
-    if (V.second->ID != ValueIDNum::EmptyValue && V.second->ID == FirstVal.ID)
+    if (V.second->hasIdenticalValidLocOps(FirstVal))
       continue;
 
     // Eliminate if a backedge feeds a VPHI back into itself.
@@ -2807,8 +2851,9 @@ void InstrRefBasedLDV::buildVLocValueMap(
               pickVPHILoc(*MBB, Var, LiveOutIdx, MOutLocs, Preds);
 
           if (ValueNum) {
-            InLocsChanged |= LiveIn->ID != *ValueNum;
-            LiveIn->ID = *ValueNum;
+            DbgOpID ValueID = DbgOpStore.insert(*ValueNum);
+            InLocsChanged |= LiveIn->getDbgOpID(0) != ValueID;
+            LiveIn->setDbgOpIDs(ValueID);
           }
         }
 
@@ -2878,8 +2923,7 @@ void InstrRefBasedLDV::buildVLocValueMap(
       DbgValue *BlockLiveIn = LiveInIdx[MBB];
       if (BlockLiveIn->Kind == DbgValue::NoVal)
         continue;
-      if (BlockLiveIn->Kind == DbgValue::VPHI &&
-          BlockLiveIn->ID == ValueIDNum::EmptyValue)
+      if (BlockLiveIn->isUnjoinedPHI())
         continue;
       if (BlockLiveIn->Kind == DbgValue::VPHI)
         BlockLiveIn->Kind = DbgValue::Def;
@@ -3070,7 +3114,8 @@ bool InstrRefBasedLDV::depthFirstVLocAndEmit(
     // instructions, installing transfers.
     MTracker->reset();
     MTracker->loadFromArray(MInLocs[BBNum], BBNum);
-    TTracker->loadInlocs(MBB, MInLocs[BBNum], Output[BBNum], NumLocs);
+    TTracker->loadInlocs(MBB, MInLocs[BBNum], DbgOpStore, Output[BBNum],
+                         NumLocs);
 
     CurBB = BBNum;
     CurInst = 1;
@@ -3368,6 +3413,7 @@ bool InstrRefBasedLDV::ExtendRanges(MachineFunction &MF,
   OverlapFragments.clear();
   SeenFragments.clear();
   SeenDbgPHIs.clear();
+  DbgOpStore.clear();
 
   return Changed;
 }

diff  --git a/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h b/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h
index bbadd9128353e..f84bcaecac5d3 100644
--- a/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h
+++ b/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h
@@ -30,6 +30,7 @@ class InstrRefLDVTest;
 namespace LiveDebugValues {
 
 class MLocTracker;
+class DbgOpIDMap;
 
 using namespace llvm;
 
@@ -168,6 +169,40 @@ class ValueIDNum {
   static ValueIDNum TombstoneValue;
 };
 
+} // End namespace LiveDebugValues
+
+namespace llvm {
+using namespace LiveDebugValues;
+
+template <> struct DenseMapInfo<LocIdx> {
+  static inline LocIdx getEmptyKey() { return LocIdx::MakeIllegalLoc(); }
+  static inline LocIdx getTombstoneKey() { return LocIdx::MakeTombstoneLoc(); }
+
+  static unsigned getHashValue(const LocIdx &Loc) { return Loc.asU64(); }
+
+  static bool isEqual(const LocIdx &A, const LocIdx &B) { return A == B; }
+};
+
+template <> struct DenseMapInfo<ValueIDNum> {
+  static inline ValueIDNum getEmptyKey() { return ValueIDNum::EmptyValue; }
+  static inline ValueIDNum getTombstoneKey() {
+    return ValueIDNum::TombstoneValue;
+  }
+
+  static unsigned getHashValue(const ValueIDNum &Val) {
+    return hash_value(Val.asU64());
+  }
+
+  static bool isEqual(const ValueIDNum &A, const ValueIDNum &B) {
+    return A == B;
+  }
+};
+
+} // end namespace llvm
+
+namespace LiveDebugValues {
+using namespace llvm;
+
 /// Type for a table of values in a block.
 using ValueTable = std::unique_ptr<ValueIDNum[]>;
 
@@ -230,19 +265,181 @@ class DbgValueProperties {
   bool IsVariadic;
 };
 
-/// Class recording the (high level) _value_ of a variable. Identifies either
-/// the value of the variable as a ValueIDNum, or a constant MachineOperand.
+/// TODO: Might pack better if we changed this to a Struct of Arrays, since
+/// MachineOperand is width 32, making this struct width 33. We could also
+/// potentially avoid storing the whole MachineOperand (sizeof=32), instead
+/// choosing to store just the contents portion (sizeof=8) and a Kind enum,
+/// since we already know it is some type of immediate value.
+/// Stores a single debug operand, which can either be a MachineOperand for
+/// directly storing immediate values, or a ValueIDNum representing some value
+/// computed at some point in the program. IsConst is used as a discriminator.
+struct DbgOp {
+  union {
+    ValueIDNum ID;
+    MachineOperand MO;
+  };
+  bool IsConst;
+
+  DbgOp() : ID(ValueIDNum::EmptyValue), IsConst(false) {}
+  DbgOp(ValueIDNum ID) : ID(ID), IsConst(false) {}
+  DbgOp(MachineOperand MO) : MO(MO), IsConst(true) {}
+
+  bool isUndef() const { return !IsConst && ID == ValueIDNum::EmptyValue; }
+
+#ifndef NDEBUG
+  void dump(const MLocTracker *MTrack) const;
+#endif
+};
+
+/// A DbgOp whose ID (if any) has resolved to an actual location, LocIdx. Used
+/// when working with concrete debug values, i.e. when joining MLocs and VLocs
+/// in the TransferTracker or emitting DBG_VALUE/DBG_VALUE_LIST instructions in
+/// the MLocTracker.
+struct ResolvedDbgOp {
+  union {
+    LocIdx Loc;
+    MachineOperand MO;
+  };
+  bool IsConst;
+
+  ResolvedDbgOp(LocIdx Loc) : Loc(Loc), IsConst(false) {}
+  ResolvedDbgOp(MachineOperand MO) : MO(MO), IsConst(true) {}
+
+  bool operator==(const ResolvedDbgOp &Other) const {
+    if (IsConst != Other.IsConst)
+      return false;
+    if (IsConst)
+      return MO.isIdenticalTo(Other.MO);
+    return Loc == Other.Loc;
+  }
+
+#ifndef NDEBUG
+  void dump(const MLocTracker *MTrack) const;
+#endif
+};
+
+/// An ID used in the DbgOpIDMap (below) to lookup a stored DbgOp. This is used
+/// in place of actual DbgOps inside of a DbgValue to reduce its size, as
+/// DbgValue is very frequently used and passed around, and the actual DbgOp is
+/// over 8x larger than this class, due to storing a MachineOperand. This ID
+/// should be equal for all equal DbgOps, and also encodes whether the mapped
+/// DbgOp is a constant, meaning that for simple equality or const-ness checks
+/// it is not necessary to lookup this ID.
+struct DbgOpID {
+  union {
+    struct {
+      uint32_t IsConst : 1;
+      uint32_t Index : 31;
+    } ID;
+    uint32_t RawID;
+  };
+
+  DbgOpID() : RawID(UndefID.RawID) {
+    static_assert(sizeof(DbgOpID) == 4, "DbgOpID should fit within 4 bytes.");
+  }
+  DbgOpID(uint32_t RawID) : RawID(RawID) {}
+  DbgOpID(bool IsConst, uint32_t Index) : ID({IsConst, Index}) {}
+
+  static DbgOpID UndefID;
+
+  bool operator==(const DbgOpID &Other) const { return RawID == Other.RawID; }
+  bool operator!=(const DbgOpID &Other) const { return !(*this == Other); }
+
+  uint32_t asU32() const { return RawID; }
+
+  bool isUndef() const { return *this == UndefID; }
+  bool isConst() const { return ID.IsConst && !isUndef(); }
+  uint32_t getIndex() const { return ID.Index; }
+
+#ifndef NDEBUG
+  void dump(const MLocTracker *MTrack, const DbgOpIDMap *OpStore) const;
+#endif
+};
+
+/// Class storing the complete set of values that are observed by DbgValues
+/// within the current function. Allows 2-way lookup, with `find` returning the
+/// Op for a given ID and `insert` returning the ID for a given Op (creating one
+/// if none exists).
+class DbgOpIDMap {
+
+  SmallVector<ValueIDNum, 0> ValueOps;
+  SmallVector<MachineOperand, 0> ConstOps;
+
+  DenseMap<ValueIDNum, DbgOpID> ValueOpToID;
+  DenseMap<MachineOperand, DbgOpID> ConstOpToID;
+
+public:
+  /// If \p Op does not already exist in this map, it is inserted and the
+  /// corresponding DbgOpID is returned. If Op already exists in this map, then
+  /// no change is made and the existing ID for Op is returned.
+  /// Calling this with the undef DbgOp will always return DbgOpID::UndefID.
+  DbgOpID insert(DbgOp Op) {
+    if (Op.isUndef())
+      return DbgOpID::UndefID;
+    if (Op.IsConst)
+      return insertConstOp(Op.MO);
+    return insertValueOp(Op.ID);
+  }
+  /// Returns the DbgOp associated with \p ID. Should only be used for IDs
+  /// returned from calling `insert` from this map or DbgOpID::UndefID.
+  DbgOp find(DbgOpID ID) const {
+    if (ID == DbgOpID::UndefID)
+      return DbgOp();
+    if (ID.isConst())
+      return DbgOp(ConstOps[ID.getIndex()]);
+    return DbgOp(ValueOps[ID.getIndex()]);
+  }
+
+  void clear() {
+    ValueOps.clear();
+    ConstOps.clear();
+    ValueOpToID.clear();
+    ConstOpToID.clear();
+  }
+
+private:
+  DbgOpID insertConstOp(MachineOperand &MO) {
+    auto ExistingIt = ConstOpToID.find(MO);
+    if (ExistingIt != ConstOpToID.end())
+      return ExistingIt->second;
+    DbgOpID ID(true, ConstOps.size());
+    ConstOpToID.insert(std::make_pair(MO, ID));
+    ConstOps.push_back(MO);
+    return ID;
+  }
+  DbgOpID insertValueOp(ValueIDNum VID) {
+    auto ExistingIt = ValueOpToID.find(VID);
+    if (ExistingIt != ValueOpToID.end())
+      return ExistingIt->second;
+    DbgOpID ID(false, ValueOps.size());
+    ValueOpToID.insert(std::make_pair(VID, ID));
+    ValueOps.push_back(VID);
+    return ID;
+  }
+};
+
+// We set the maximum number of operands that we will handle to keep DbgValue
+// within a reasonable size (64 bytes), as we store and pass a lot of them
+// around.
+#define MAX_DBG_OPS 8
+
+/// Class recording the (high level) _value_ of a variable. Identifies the value
+/// of the variable as a list of ValueIDNums and constant MachineOperands, or as
+/// an empty list for undef debug values or VPHI values which we have not found
+/// valid locations for.
 /// This class also stores meta-information about how the value is qualified.
 /// Used to reason about variable values when performing the second
 /// (DebugVariable specific) dataflow analysis.
 class DbgValue {
+private:
+  /// If Kind is Def or VPHI, the set of IDs corresponding to the DbgOps that
+  /// are used. VPHIs set every ID to EmptyID when we have not found a valid
+  /// machine-value for every operand, and sets them to the corresponding
+  /// machine-values when we have found all of them.
+  DbgOpID DbgOps[MAX_DBG_OPS];
+  unsigned OpCount;
+
 public:
-  /// If Kind is Def, the value number that this value is based on. VPHIs set
-  /// this field to EmptyValue if there is no machine-value for this VPHI, or
-  /// the corresponding machine-value if there is one.
-  ValueIDNum ID;
-  /// If Kind is Const, the MachineOperand defining this value.
-  Optional<MachineOperand> MO;
   /// For a NoVal or VPHI DbgValue, which block it was generated in.
   int BlockNo;
 
@@ -251,8 +448,8 @@ class DbgValue {
 
   typedef enum {
     Undef, // Represents a DBG_VALUE $noreg in the transfer function only.
-    Def,   // This value is defined by an inst, or is a PHI value.
-    Const, // A constant value contained in the MachineOperand field.
+    Def,   // This value is defined by some combination of constants,
+           // instructions, or PHI values.
     VPHI,  // Incoming values to BlockNo 
diff er, those values must be joined by
            // a PHI in this block.
     NoVal, // Empty DbgValue indicating an unknown value. Used as initializer,
@@ -261,52 +458,113 @@ class DbgValue {
   /// Discriminator for whether this is a constant or an in-program value.
   KindT Kind;
 
-  DbgValue(const ValueIDNum &Val, const DbgValueProperties &Prop, KindT Kind)
-      : ID(Val), MO(None), BlockNo(0), Properties(Prop), Kind(Kind) {
-    assert(Kind == Def);
+  DbgValue(ArrayRef<DbgOpID> DbgOps, const DbgValueProperties &Prop)
+      : OpCount(DbgOps.size()), BlockNo(0), Properties(Prop), Kind(Def) {
+    static_assert(sizeof(DbgValue) <= 64,
+                  "DbgValue should fit within 64 bytes.");
+    assert(DbgOps.size() == Prop.getLocationOpCount());
+    if (DbgOps.size() > MAX_DBG_OPS ||
+        any_of(DbgOps, [](DbgOpID ID) { return ID.isUndef(); })) {
+      Kind = Undef;
+      OpCount = 0;
+#define DEBUG_TYPE "LiveDebugValues"
+      if (DbgOps.size() > MAX_DBG_OPS) {
+        LLVM_DEBUG(dbgs() << "Found DbgValue with more than maximum allowed "
+                             "operands.\n");
+      }
+#undef DEBUG_TYPE
+    } else {
+      for (unsigned Idx = 0; Idx < DbgOps.size(); ++Idx)
+        this->DbgOps[Idx] = DbgOps[Idx];
+    }
   }
 
   DbgValue(unsigned BlockNo, const DbgValueProperties &Prop, KindT Kind)
-      : ID(ValueIDNum::EmptyValue), MO(None), BlockNo(BlockNo),
-        Properties(Prop), Kind(Kind) {
+      : OpCount(0), BlockNo(BlockNo), Properties(Prop), Kind(Kind) {
     assert(Kind == NoVal || Kind == VPHI);
   }
 
-  DbgValue(const MachineOperand &MO, const DbgValueProperties &Prop, KindT Kind)
-      : ID(ValueIDNum::EmptyValue), MO(MO), BlockNo(0), Properties(Prop),
-        Kind(Kind) {
-    assert(Kind == Const);
-  }
-
   DbgValue(const DbgValueProperties &Prop, KindT Kind)
-    : ID(ValueIDNum::EmptyValue), MO(None), BlockNo(0), Properties(Prop),
-      Kind(Kind) {
+      : OpCount(0), BlockNo(0), Properties(Prop), Kind(Kind) {
     assert(Kind == Undef &&
            "Empty DbgValue constructor must pass in Undef kind");
   }
 
 #ifndef NDEBUG
-  void dump(const MLocTracker *MTrack) const;
+  void dump(const MLocTracker *MTrack = nullptr,
+            const DbgOpIDMap *OpStore = nullptr) const;
 #endif
 
   bool operator==(const DbgValue &Other) const {
     if (std::tie(Kind, Properties) != std::tie(Other.Kind, Other.Properties))
       return false;
-    else if (Kind == Def && ID != Other.ID)
+    else if (Kind == Def && !equal(getDbgOpIDs(), Other.getDbgOpIDs()))
       return false;
     else if (Kind == NoVal && BlockNo != Other.BlockNo)
       return false;
-    else if (Kind == Const)
-      return MO->isIdenticalTo(*Other.MO);
     else if (Kind == VPHI && BlockNo != Other.BlockNo)
       return false;
-    else if (Kind == VPHI && ID != Other.ID)
+    else if (Kind == VPHI && !equal(getDbgOpIDs(), Other.getDbgOpIDs()))
       return false;
 
     return true;
   }
 
   bool operator!=(const DbgValue &Other) const { return !(*this == Other); }
+
+  // Returns an array of all the machine values used to calculate this variable
+  // value, or an empty list for an Undef or unjoined VPHI.
+  ArrayRef<DbgOpID> getDbgOpIDs() const { return {DbgOps, OpCount}; }
+
+  // Returns either DbgOps[Index] if this DbgValue has Debug Operands, or
+  // the ID for ValueIDNum::EmptyValue otherwise (i.e. if this is an Undef,
+  // NoVal, or an unjoined VPHI).
+  DbgOpID getDbgOpID(unsigned Index) const {
+    if (!OpCount)
+      return DbgOpID::UndefID;
+    assert(Index < OpCount);
+    return DbgOps[Index];
+  }
+  // Replaces this DbgValue's existing DbgOpIDs (if any) with the contents of
+  // \p NewIDs. The number of DbgOpIDs passed must be equal to the number of
+  // arguments expected by this DbgValue's properties (the return value of
+  // `getLocationOpCount()`).
+  void setDbgOpIDs(ArrayRef<DbgOpID> NewIDs) {
+    // We can go from no ops to some ops, but not from some ops to no ops.
+    assert(NewIDs.size() == getLocationOpCount() &&
+           "Incorrect number of Debug Operands for this DbgValue.");
+    OpCount = NewIDs.size();
+    for (unsigned Idx = 0; Idx < NewIDs.size(); ++Idx)
+      DbgOps[Idx] = NewIDs[Idx];
+  }
+
+  // The number of debug operands expected by this DbgValue's expression.
+  // getDbgOpIDs() should return an array of this length, unless this is an
+  // Undef or an unjoined VPHI.
+  unsigned getLocationOpCount() const {
+    return Properties.getLocationOpCount();
+  }
+
+  // Returns true if this or Other are unjoined PHIs, which do not have defined
+  // Loc Ops, or if the `n`th Loc Op for this has a 
diff erent constness to the
+  // `n`th Loc Op for Other.
+  bool hasJoinableLocOps(const DbgValue &Other) const {
+    if (isUnjoinedPHI() || Other.isUnjoinedPHI())
+      return true;
+    for (unsigned Idx = 0; Idx < getLocationOpCount(); ++Idx) {
+      if (getDbgOpID(Idx).isConst() != Other.getDbgOpID(Idx).isConst())
+        return false;
+    }
+    return true;
+  }
+
+  bool isUnjoinedPHI() const { return Kind == VPHI && OpCount == 0; }
+
+  bool hasIdenticalValidLocOps(const DbgValue &Other) const {
+    if (!OpCount)
+      return false;
+    return equal(getDbgOpIDs(), Other.getDbgOpIDs());
+  }
 };
 
 class LocIdxToIndexFunctor {
@@ -716,29 +974,14 @@ class VLocTracker {
       : OverlappingFragments(O), EmptyProperties(EmptyExpr, false, false) {}
 
   void defVar(const MachineInstr &MI, const DbgValueProperties &Properties,
-              Optional<ValueIDNum> ID) {
+              const SmallVectorImpl<DbgOpID> &DebugOps) {
     assert(MI.isDebugValue() || MI.isDebugRef());
+    assert(DebugOps.size() <= 1);
     DebugVariable Var(MI.getDebugVariable(), MI.getDebugExpression(),
                       MI.getDebugLoc()->getInlinedAt());
-    DbgValue Rec = (ID) ? DbgValue(*ID, Properties, DbgValue::Def)
-                        : DbgValue(Properties, DbgValue::Undef);
-
-    // Attempt insertion; overwrite if it's already mapped.
-    auto Result = Vars.insert(std::make_pair(Var, Rec));
-    if (!Result.second)
-      Result.first->second = Rec;
-    Scopes[Var] = MI.getDebugLoc().get();
-
-    considerOverlaps(Var, MI.getDebugLoc().get());
-  }
-
-  void defVar(const MachineInstr &MI, const MachineOperand &MO) {
-    // Only DBG_VALUEs can define constant-valued variables.
-    assert(MI.isDebugValue());
-    DebugVariable Var(MI.getDebugVariable(), MI.getDebugExpression(),
-                      MI.getDebugLoc()->getInlinedAt());
-    DbgValueProperties Properties(MI);
-    DbgValue Rec = DbgValue(MO, Properties, DbgValue::Const);
+    DbgValue Rec = (DebugOps.size() > 0)
+                       ? DbgValue(DebugOps, Properties)
+                       : DbgValue(Properties, DbgValue::Undef);
 
     // Attempt insertion; overwrite if it's already mapped.
     auto Result = Vars.insert(std::make_pair(Var, Rec));
@@ -907,6 +1150,8 @@ class InstrRefBasedLDV : public LDVImpl {
   /// the result.
   DenseMap<MachineInstr *, Optional<ValueIDNum>> SeenDbgPHIs;
 
+  DbgOpIDMap DbgOpStore;
+
   /// True if we need to examine call instructions for stack clobbers. We
   /// normally assume that they don't clobber SP, but stack probes on Windows
   /// do.
@@ -1166,33 +1411,4 @@ class InstrRefBasedLDV : public LDVImpl {
 
 } // namespace LiveDebugValues
 
-namespace llvm {
-using namespace LiveDebugValues;
-
-template <> struct DenseMapInfo<LocIdx> {
-  static inline LocIdx getEmptyKey() { return LocIdx::MakeIllegalLoc(); }
-  static inline LocIdx getTombstoneKey() { return LocIdx::MakeTombstoneLoc(); }
-
-  static unsigned getHashValue(const LocIdx &Loc) { return Loc.asU64(); }
-
-  static bool isEqual(const LocIdx &A, const LocIdx &B) { return A == B; }
-};
-
-template <> struct DenseMapInfo<ValueIDNum> {
-  static inline ValueIDNum getEmptyKey() { return ValueIDNum::EmptyValue; }
-  static inline ValueIDNum getTombstoneKey() {
-    return ValueIDNum::TombstoneValue;
-  }
-
-  static unsigned getHashValue(const ValueIDNum &Val) {
-    return hash_value(Val.asU64());
-  }
-
-  static bool isEqual(const ValueIDNum &A, const ValueIDNum &B) {
-    return A == B;
-  }
-};
-
-} // end namespace llvm
-
 #endif /* LLVM_LIB_CODEGEN_LIVEDEBUGVALUES_INSTRREFBASEDLDV_H */

diff  --git a/llvm/unittests/CodeGen/InstrRefLDVTest.cpp b/llvm/unittests/CodeGen/InstrRefLDVTest.cpp
index 974de5c430dad..515797dfb307d 100644
--- a/llvm/unittests/CodeGen/InstrRefLDVTest.cpp
+++ b/llvm/unittests/CodeGen/InstrRefLDVTest.cpp
@@ -178,6 +178,13 @@ class InstrRefLDVTest : public testing::Test {
     LDV->VTracker = &*VTracker;
   }
 
+  DbgOpID addValueDbgOp(ValueIDNum V) {
+    return LDV->DbgOpStore.insert(DbgOp(V));
+  }
+  DbgOpID addConstDbgOp(MachineOperand MO) {
+    return LDV->DbgOpStore.insert(DbgOp(MO));
+  }
+
   // Some routines for bouncing into LDV,
   void buildMLocValueMap(FuncValueTable &MInLocs, FuncValueTable &MOutLocs,
                          SmallVectorImpl<MLocTransferMap> &MLocTransfer) {
@@ -1798,6 +1805,10 @@ TEST_F(InstrRefLDVTest, pickVPHILocDiamond) {
   ValueIDNum LiveInRax(EntryBlk, 0, RaxLoc);
   ValueIDNum RspPHIInBlk2(Br2Blk, 0, RspLoc);
   ValueIDNum RspPHIInBlk3(RetBlk, 0, RspLoc);
+  DbgOpID LiveInRspID = addValueDbgOp(LiveInRsp);
+  DbgOpID LiveInRaxID = addValueDbgOp(LiveInRax);
+  DbgOpID RspPHIInBlk2ID = addValueDbgOp(RspPHIInBlk2);
+  DbgOpID RspPHIInBlk3ID = addValueDbgOp(RspPHIInBlk3);
 
   DebugVariable Var(FuncVariable, None, nullptr);
   DbgValueProperties EmptyProps(EmptyExpr, false, false);
@@ -1820,8 +1831,8 @@ TEST_F(InstrRefLDVTest, pickVPHILocDiamond) {
   Optional<ValueIDNum> Result;
 
   // Simple case: join two distinct values on entry to the block.
-  VLiveOuts[1] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);
-  VLiveOuts[2] = DbgValue(LiveInRax, EmptyProps, DbgValue::Def);
+  VLiveOuts[1] = DbgValue(LiveInRspID, EmptyProps);
+  VLiveOuts[2] = DbgValue(LiveInRaxID, EmptyProps);
   Result = pickVPHILoc(*MBB3, Var, VLiveOutIdx, MOutLocs, Preds);
   // Should have picked a PHI in $rsp in block 3.
   EXPECT_TRUE(Result);
@@ -1839,8 +1850,7 @@ TEST_F(InstrRefLDVTest, pickVPHILocDiamond) {
   // Swap back,
   std::swap(VLiveOuts[1], VLiveOuts[2]);
   // Setting one of these to being a constant should prohibit merging.
-  VLiveOuts[1].Kind = DbgValue::Const;
-  VLiveOuts[1].MO = MachineOperand::CreateImm(0);
+  VLiveOuts[1].setDbgOpIDs(addConstDbgOp(MachineOperand::CreateImm(0)));
   Result = pickVPHILoc(*MBB3, Var, VLiveOutIdx, MOutLocs, Preds);
   EXPECT_FALSE(Result);
 
@@ -1851,7 +1861,7 @@ TEST_F(InstrRefLDVTest, pickVPHILocDiamond) {
   EXPECT_FALSE(Result);
 
   // NoVals shouldn't join with anything else.
-  VLiveOuts[1] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);
+  VLiveOuts[1] = DbgValue(LiveInRspID, EmptyProps);
   VLiveOuts[2] = DbgValue(2, EmptyProps, DbgValue::NoVal);
   Result = pickVPHILoc(*MBB3, Var, VLiveOutIdx, MOutLocs, Preds);
   EXPECT_FALSE(Result);
@@ -1860,9 +1870,8 @@ TEST_F(InstrRefLDVTest, pickVPHILocDiamond) {
   // such a scenario: first, where one incoming edge has a VPHI with no known
   // value. This represents an edge where there was a PHI value that can't be
   // found in the register file -- we can't subsequently find a PHI here.
-  VLiveOuts[1] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);
+  VLiveOuts[1] = DbgValue(LiveInRspID, EmptyProps);
   VLiveOuts[2] = DbgValue(2, EmptyProps, DbgValue::VPHI);
-  EXPECT_EQ(VLiveOuts[2].ID, ValueIDNum::EmptyValue);
   Result = pickVPHILoc(*MBB3, Var, VLiveOutIdx, MOutLocs, Preds);
   EXPECT_FALSE(Result);
 
@@ -1870,9 +1879,9 @@ TEST_F(InstrRefLDVTest, pickVPHILocDiamond) {
   // location. Use a PHI machine-value for doing this, as VPHIs should always
   // have PHI values, or they should have been eliminated.
   MOutLocs[2][0] = RspPHIInBlk2;
-  VLiveOuts[1] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);
+  VLiveOuts[1] = DbgValue(LiveInRspID, EmptyProps);
   VLiveOuts[2] = DbgValue(2, EmptyProps, DbgValue::VPHI);
-  VLiveOuts[2].ID = RspPHIInBlk2; // Set location where PHI happens.
+  VLiveOuts[2].setDbgOpIDs(RspPHIInBlk2ID); // Set location where PHI happens.
   Result = pickVPHILoc(*MBB3, Var, VLiveOutIdx, MOutLocs, Preds);
   EXPECT_TRUE(Result);
   if (Result) {
@@ -1889,14 +1898,14 @@ TEST_F(InstrRefLDVTest, pickVPHILocDiamond) {
   DIExpression *NewExpr =
       DIExpression::prepend(EmptyExpr, DIExpression::ApplyOffset, 4);
   DbgValueProperties PropsWithExpr(NewExpr, false, false);
-  VLiveOuts[1] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);
-  VLiveOuts[2] = DbgValue(LiveInRsp, PropsWithExpr, DbgValue::Def);
+  VLiveOuts[1] = DbgValue(LiveInRspID, EmptyProps);
+  VLiveOuts[2] = DbgValue(LiveInRspID, PropsWithExpr);
   Result = pickVPHILoc(*MBB3, Var, VLiveOutIdx, MOutLocs, Preds);
   EXPECT_FALSE(Result);
 
   DbgValueProperties PropsWithIndirect(EmptyExpr, true, false);
-  VLiveOuts[1] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);
-  VLiveOuts[2] = DbgValue(LiveInRsp, PropsWithIndirect, DbgValue::Def);
+  VLiveOuts[1] = DbgValue(LiveInRspID, EmptyProps);
+  VLiveOuts[2] = DbgValue(LiveInRspID, PropsWithIndirect);
   Result = pickVPHILoc(*MBB3, Var, VLiveOutIdx, MOutLocs, Preds);
   EXPECT_FALSE(Result);
 }
@@ -1927,6 +1936,10 @@ TEST_F(InstrRefLDVTest, pickVPHILocLoops) {
   ValueIDNum LiveInRax(EntryBlk, 0, RaxLoc);
   ValueIDNum RspPHIInBlk1(LoopBlk, 0, RspLoc);
   ValueIDNum RaxPHIInBlk1(LoopBlk, 0, RaxLoc);
+  DbgOpID LiveInRspID = addValueDbgOp(LiveInRsp);
+  DbgOpID LiveInRaxID = addValueDbgOp(LiveInRax);
+  DbgOpID RspPHIInBlk1ID = addValueDbgOp(RspPHIInBlk1);
+  DbgOpID RaxPHIInBlk1ID = addValueDbgOp(RaxPHIInBlk1);
 
   DebugVariable Var(FuncVariable, None, nullptr);
   DbgValueProperties EmptyProps(EmptyExpr, false, false);
@@ -1948,8 +1961,8 @@ TEST_F(InstrRefLDVTest, pickVPHILocLoops) {
   Optional<ValueIDNum> Result;
 
   // See that we can merge as normal on a backedge.
-  VLiveOuts[0] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);
-  VLiveOuts[1] = DbgValue(LiveInRax, EmptyProps, DbgValue::Def);
+  VLiveOuts[0] = DbgValue(LiveInRspID, EmptyProps);
+  VLiveOuts[1] = DbgValue(LiveInRaxID, EmptyProps);
   Result = pickVPHILoc(*MBB1, Var, VLiveOutIdx, MOutLocs, Preds);
   // Should have picked a PHI in $rsp in block 1.
   EXPECT_TRUE(Result);
@@ -1970,7 +1983,7 @@ TEST_F(InstrRefLDVTest, pickVPHILocLoops) {
   MOutLocs[0][1] = LiveInRsp;
   MOutLocs[1][0] = RaxPHIInBlk1;
   MOutLocs[1][1] = RaxPHIInBlk1;
-  VLiveOuts[0] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);
+  VLiveOuts[0] = DbgValue(LiveInRspID, EmptyProps);
   // Crucially, a VPHI originating in this block:
   VLiveOuts[1] = DbgValue(1, EmptyProps, DbgValue::VPHI);
   Result = pickVPHILoc(*MBB1, Var, VLiveOutIdx, MOutLocs, Preds);
@@ -1988,7 +2001,7 @@ TEST_F(InstrRefLDVTest, pickVPHILocLoops) {
   // Additionally, if the VPHI coming back on the loop backedge isn't from
   // this block (block 1), we can't merge it.
   MOutLocs[1][1] = RaxPHIInBlk1;
-  VLiveOuts[0] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);
+  VLiveOuts[0] = DbgValue(LiveInRspID, EmptyProps);
   VLiveOuts[1] = DbgValue(0, EmptyProps, DbgValue::VPHI);
   Result = pickVPHILoc(*MBB1, Var, VLiveOutIdx, MOutLocs, Preds);
   EXPECT_FALSE(Result);
@@ -2029,6 +2042,12 @@ TEST_F(InstrRefLDVTest, pickVPHILocBadlyNestedLoops) {
   ValueIDNum RspPHIInBlk1(Loop1Blk, 0, RspLoc);
   ValueIDNum RaxPHIInBlk1(Loop1Blk, 0, RaxLoc);
   ValueIDNum RbxPHIInBlk1(Loop1Blk, 0, RbxLoc);
+  DbgOpID LiveInRspID = addValueDbgOp(LiveInRsp);
+  DbgOpID LiveInRaxID = addValueDbgOp(LiveInRax);
+  DbgOpID LiveInRbxID = addValueDbgOp(LiveInRbx);
+  DbgOpID RspPHIInBlk1ID = addValueDbgOp(RspPHIInBlk1);
+  DbgOpID RaxPHIInBlk1ID = addValueDbgOp(RaxPHIInBlk1);
+  DbgOpID RbxPHIInBlk1ID = addValueDbgOp(RbxPHIInBlk1);
 
   DebugVariable Var(FuncVariable, None, nullptr);
   DbgValueProperties EmptyProps(EmptyExpr, false, false);
@@ -2055,9 +2074,9 @@ TEST_F(InstrRefLDVTest, pickVPHILocBadlyNestedLoops) {
   Optional<ValueIDNum> Result;
 
   // See that we can merge as normal on a backedge.
-  VLiveOuts[0] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);
-  VLiveOuts[1] = DbgValue(LiveInRax, EmptyProps, DbgValue::Def);
-  VLiveOuts[2] = DbgValue(LiveInRbx, EmptyProps, DbgValue::Def);
+  VLiveOuts[0] = DbgValue(LiveInRspID, EmptyProps);
+  VLiveOuts[1] = DbgValue(LiveInRaxID, EmptyProps);
+  VLiveOuts[2] = DbgValue(LiveInRbxID, EmptyProps);
   Result = pickVPHILoc(*MBB1, Var, VLiveOutIdx, MOutLocs, Preds);
   // Should have picked a PHI in $rsp in block 1.
   EXPECT_TRUE(Result);
@@ -2084,9 +2103,9 @@ TEST_F(InstrRefLDVTest, pickVPHILocBadlyNestedLoops) {
 
   // If the variables value on that edge is a VPHI feeding into itself, that's
   // fine.
-  VLiveOuts[0] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);
+  VLiveOuts[0] = DbgValue(LiveInRspID, EmptyProps);
   VLiveOuts[1] = DbgValue(1, EmptyProps, DbgValue::VPHI);
-  VLiveOuts[2] = DbgValue(LiveInRbx, EmptyProps, DbgValue::Def);
+  VLiveOuts[2] = DbgValue(LiveInRbxID, EmptyProps);
   Result = pickVPHILoc(*MBB1, Var, VLiveOutIdx, MOutLocs, Preds);
   EXPECT_TRUE(Result);
   if (Result) {
@@ -2095,7 +2114,7 @@ TEST_F(InstrRefLDVTest, pickVPHILocBadlyNestedLoops) {
 
   // Likewise: the other backedge being a VPHI from block 1 should be accepted.
   MOutLocs[2][0] = RspPHIInBlk1;
-  VLiveOuts[0] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);
+  VLiveOuts[0] = DbgValue(LiveInRspID, EmptyProps);
   VLiveOuts[1] = DbgValue(1, EmptyProps, DbgValue::VPHI);
   VLiveOuts[2] = DbgValue(1, EmptyProps, DbgValue::VPHI);
   Result = pickVPHILoc(*MBB1, Var, VLiveOutIdx, MOutLocs, Preds);
@@ -2154,10 +2173,10 @@ TEST_F(InstrRefLDVTest, vlocJoinDiamond) {
 
   unsigned EntryBlk = 0, Br2Blk = 2, RetBlk = 3;
 
-  ValueIDNum LiveInRsp(EntryBlk, 0, RspLoc);
-  ValueIDNum LiveInRax(EntryBlk, 0, RaxLoc);
-  ValueIDNum RspPHIInBlkBr2Blk(Br2Blk, 0, RspLoc);
-  ValueIDNum RspPHIInBlkRetBlk(RetBlk, 0, RspLoc);
+  DbgOpID LiveInRspID = DbgOpID(false, 0);
+  DbgOpID LiveInRaxID = DbgOpID(false, 1);
+  DbgOpID RspPHIInBlkBr2BlkID = DbgOpID(false, 2);
+  DbgOpID RspPHIInBlkRetBlkID = DbgOpID(false, 3);
 
   DebugVariable Var(FuncVariable, None, nullptr);
   DbgValueProperties EmptyProps(EmptyExpr, false, false);
@@ -2185,12 +2204,12 @@ TEST_F(InstrRefLDVTest, vlocJoinDiamond) {
   // vlocJoin is here to propagate incoming values, and eliminate PHIs. Start
   // off by propagating a value into the merging block, number 3.
   DbgValue JoinedLoc = DbgValue(3, EmptyProps, DbgValue::NoVal);
-  VLiveOuts[1] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);
-  VLiveOuts[2] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);
+  VLiveOuts[1] = DbgValue(LiveInRspID, EmptyProps);
+  VLiveOuts[2] = DbgValue(LiveInRspID, EmptyProps);
   bool Result = vlocJoin(*MBB3, VLiveOutIdx, AllBlocks, JoinedLoc);
   EXPECT_TRUE(Result); // Output locs should have changed.
   EXPECT_EQ(JoinedLoc.Kind, DbgValue::Def);
-  EXPECT_EQ(JoinedLoc.ID, LiveInRsp);
+  EXPECT_EQ(JoinedLoc.getDbgOpID(0), LiveInRspID);
 
   // And if we did it a second time, leaving the live-ins as it was, then
   // we should report no change.
@@ -2200,15 +2219,15 @@ TEST_F(InstrRefLDVTest, vlocJoinDiamond) {
   // If the live-in variable values are 
diff erent, but there's no PHI placed
   // in this block, then just pick a location. It should be the first (in RPO)
   // predecessor to avoid being a backedge.
-  VLiveOuts[1] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);
-  VLiveOuts[2] = DbgValue(LiveInRax, EmptyProps, DbgValue::Def);
+  VLiveOuts[1] = DbgValue(LiveInRspID, EmptyProps);
+  VLiveOuts[2] = DbgValue(LiveInRaxID, EmptyProps);
   JoinedLoc = DbgValue(3, EmptyProps, DbgValue::NoVal);
   Result = vlocJoin(*MBB3, VLiveOutIdx, AllBlocks, JoinedLoc);
   EXPECT_TRUE(Result);
   EXPECT_EQ(JoinedLoc.Kind, DbgValue::Def);
   // RPO is blocks 0 2 1 3, so LiveInRax is picked as the first predecessor
   // of this join.
-  EXPECT_EQ(JoinedLoc.ID, LiveInRax);
+  EXPECT_EQ(JoinedLoc.getDbgOpID(0), LiveInRaxID);
 
   // No tests for whether vlocJoin will pass-through a variable with 
diff ering
   // expressions / properties. Those can only come about due to assignments; and
@@ -2224,46 +2243,46 @@ TEST_F(InstrRefLDVTest, vlocJoinDiamond) {
   EXPECT_FALSE(Result);
   EXPECT_EQ(JoinedLoc.Kind, DbgValue::VPHI);
   // This should not have been assigned a fixed value.
-  EXPECT_EQ(JoinedLoc.ID, ValueIDNum::EmptyValue);
+  EXPECT_EQ(JoinedLoc.getDbgOpID(0), DbgOpID::UndefID);
   EXPECT_EQ(JoinedLoc.BlockNo, 3);
 
   // Try a simple PHI elimination. Put a PHI in block 3, but LiveInRsp on both
   // incoming edges. Re-load in and out-locs with unrelated values; they're
   // irrelevant.
-  VLiveOuts[1] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);
-  VLiveOuts[2] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);
+  VLiveOuts[1] = DbgValue(LiveInRspID, EmptyProps);
+  VLiveOuts[2] = DbgValue(LiveInRspID, EmptyProps);
   JoinedLoc = DbgValue(3, EmptyProps, DbgValue::VPHI);
   Result = vlocJoin(*MBB3, VLiveOutIdx, AllBlocks, JoinedLoc);
   EXPECT_TRUE(Result);
   EXPECT_EQ(JoinedLoc.Kind, DbgValue::Def);
-  EXPECT_EQ(JoinedLoc.ID, LiveInRsp);
+  EXPECT_EQ(JoinedLoc.getDbgOpID(0), LiveInRspID);
 
   // Try the same PHI elimination but with one incoming value being a VPHI
   // referring to the same value.
-  VLiveOuts[1] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);
+  VLiveOuts[1] = DbgValue(LiveInRspID, EmptyProps);
   VLiveOuts[2] = DbgValue(2, EmptyProps, DbgValue::VPHI);
-  VLiveOuts[2].ID = LiveInRsp;
+  VLiveOuts[2].setDbgOpIDs(LiveInRspID);
   JoinedLoc = DbgValue(3, EmptyProps, DbgValue::VPHI);
   Result = vlocJoin(*MBB3, VLiveOutIdx, AllBlocks, JoinedLoc);
   EXPECT_TRUE(Result);
   EXPECT_EQ(JoinedLoc.Kind, DbgValue::VPHI);
-  EXPECT_EQ(JoinedLoc.ID, LiveInRsp);
+  EXPECT_EQ(JoinedLoc.getDbgOpID(0), LiveInRspID);
 
   // If the "current" live-in is a VPHI, but not a VPHI generated in the current
   // block, then it's the remains of an earlier value propagation. We should
   // value propagate through this merge. Even if the current incoming values
   // disagree, because we've previously determined any VPHI here is redundant.
-  VLiveOuts[1] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);
-  VLiveOuts[2] = DbgValue(LiveInRax, EmptyProps, DbgValue::Def);
+  VLiveOuts[1] = DbgValue(LiveInRspID, EmptyProps);
+  VLiveOuts[2] = DbgValue(LiveInRaxID, EmptyProps);
   JoinedLoc = DbgValue(2, EmptyProps, DbgValue::VPHI);
   Result = vlocJoin(*MBB3, VLiveOutIdx, AllBlocks, JoinedLoc);
   EXPECT_TRUE(Result);
   EXPECT_EQ(JoinedLoc.Kind, DbgValue::Def);
-  EXPECT_EQ(JoinedLoc.ID, LiveInRax); // from block 2
+  EXPECT_EQ(JoinedLoc.getDbgOpID(0), LiveInRaxID); // from block 2
 
   // The above test, but test that we will install one value-propagated VPHI
   // over another.
-  VLiveOuts[1] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);
+  VLiveOuts[1] = DbgValue(LiveInRspID, EmptyProps);
   VLiveOuts[2] = DbgValue(0, EmptyProps, DbgValue::VPHI);
   JoinedLoc = DbgValue(2, EmptyProps, DbgValue::VPHI);
   Result = vlocJoin(*MBB3, VLiveOutIdx, AllBlocks, JoinedLoc);
@@ -2273,8 +2292,8 @@ TEST_F(InstrRefLDVTest, vlocJoinDiamond) {
 
   // We shouldn't eliminate PHIs when properties disagree.
   DbgValueProperties PropsWithIndirect(EmptyExpr, true, false);
-  VLiveOuts[1] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);
-  VLiveOuts[2] = DbgValue(LiveInRsp, PropsWithIndirect, DbgValue::Def);
+  VLiveOuts[1] = DbgValue(LiveInRspID, EmptyProps);
+  VLiveOuts[2] = DbgValue(LiveInRspID, PropsWithIndirect);
   JoinedLoc = DbgValue(3, EmptyProps, DbgValue::VPHI);
   Result = vlocJoin(*MBB3, VLiveOutIdx, AllBlocks, JoinedLoc);
   EXPECT_FALSE(Result);
@@ -2284,13 +2303,13 @@ TEST_F(InstrRefLDVTest, vlocJoinDiamond) {
   // Even if properties disagree, we should still value-propagate if there's no
   // PHI to be eliminated. The disagreeing values should work themselves out,
   // seeing how we've determined no PHI is necessary.
-  VLiveOuts[1] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);
-  VLiveOuts[2] = DbgValue(LiveInRsp, PropsWithIndirect, DbgValue::Def);
+  VLiveOuts[1] = DbgValue(LiveInRspID, EmptyProps);
+  VLiveOuts[2] = DbgValue(LiveInRspID, PropsWithIndirect);
   JoinedLoc = DbgValue(2, EmptyProps, DbgValue::VPHI);
   Result = vlocJoin(*MBB3, VLiveOutIdx, AllBlocks, JoinedLoc);
   EXPECT_TRUE(Result);
   EXPECT_EQ(JoinedLoc.Kind, DbgValue::Def);
-  EXPECT_EQ(JoinedLoc.ID, LiveInRsp);
+  EXPECT_EQ(JoinedLoc.getDbgOpID(0), LiveInRspID);
   // Also check properties come from block 2, the first RPO predecessor to block
   // three.
   EXPECT_EQ(JoinedLoc.Properties, PropsWithIndirect);
@@ -2300,11 +2319,30 @@ TEST_F(InstrRefLDVTest, vlocJoinDiamond) {
   DIExpression *NewExpr =
       DIExpression::prepend(EmptyExpr, DIExpression::ApplyOffset, 4);
   DbgValueProperties PropsWithExpr(NewExpr, false, false);
-  VLiveOuts[1] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);
-  VLiveOuts[2] = DbgValue(LiveInRsp, PropsWithExpr, DbgValue::Def);
+  VLiveOuts[1] = DbgValue(LiveInRspID, EmptyProps);
+  VLiveOuts[2] = DbgValue(LiveInRspID, PropsWithExpr);
   JoinedLoc = DbgValue(3, EmptyProps, DbgValue::VPHI);
   Result = vlocJoin(*MBB3, VLiveOutIdx, AllBlocks, JoinedLoc);
   EXPECT_FALSE(Result);
+
+  // Try placing a PHI with variadic debug values. With 
diff ering input values
+  // (LiveInRsp, LiveInRax), this PHI should not be eliminated.
+  DIExpression *TwoOpExpr =
+      DIExpression::get(Ctx, {dwarf::DW_OP_LLVM_arg, 0, dwarf::DW_OP_LLVM_arg,
+                              1, dwarf::DW_OP_plus});
+  DbgValueProperties TwoOpProps(TwoOpExpr, false, true);
+  DbgOpID Locs0[] = {LiveInRspID, LiveInRaxID};
+  DbgOpID Locs1[] = {LiveInRaxID, LiveInRspID};
+  JoinedLoc = DbgValue(3, TwoOpProps, DbgValue::VPHI);
+  VLiveOuts[1] = DbgValue(Locs0, TwoOpProps);
+  VLiveOuts[2] = DbgValue(Locs1, TwoOpProps);
+  Result = vlocJoin(*MBB3, VLiveOutIdx, AllBlocks, JoinedLoc);
+  // Expect no change.
+  EXPECT_FALSE(Result);
+  EXPECT_EQ(JoinedLoc.Kind, DbgValue::VPHI);
+  // This should not have been assigned a fixed value.
+  EXPECT_EQ(JoinedLoc.getDbgOpID(0), DbgOpID::UndefID);
+  EXPECT_EQ(JoinedLoc.BlockNo, 3);
 }
 
 TEST_F(InstrRefLDVTest, vlocJoinLoops) {
@@ -2323,9 +2361,9 @@ TEST_F(InstrRefLDVTest, vlocJoinLoops) {
 
   unsigned EntryBlk = 0, LoopBlk = 1;
 
-  ValueIDNum LiveInRsp(EntryBlk, 0, RspLoc);
-  ValueIDNum LiveInRax(EntryBlk, 0, RaxLoc);
-  ValueIDNum RspPHIInBlk1(LoopBlk, 0, RspLoc);
+  DbgOpID LiveInRspID = DbgOpID(false, 0);
+  DbgOpID LiveInRaxID = DbgOpID(false, 1);
+  DbgOpID RspPHIInBlk1ID = DbgOpID(false, 2);
 
   DebugVariable Var(FuncVariable, None, nullptr);
   DbgValueProperties EmptyProps(EmptyExpr, false, false);
@@ -2354,17 +2392,17 @@ TEST_F(InstrRefLDVTest, vlocJoinLoops) {
 
   // First: when there's no VPHI placed already, propagate the live-in value of
   // the first RPO predecessor.
-  VLiveOuts[0] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);
-  VLiveOuts[1] = DbgValue(LiveInRax, EmptyProps, DbgValue::Def);
-  DbgValue JoinedLoc = DbgValue(LiveInRax, EmptyProps, DbgValue::Def);
+  VLiveOuts[0] = DbgValue(LiveInRspID, EmptyProps);
+  VLiveOuts[1] = DbgValue(LiveInRaxID, EmptyProps);
+  DbgValue JoinedLoc = DbgValue(LiveInRaxID, EmptyProps);
   bool Result = vlocJoin(*MBB1, VLiveOutIdx, AllBlocks, JoinedLoc);
   EXPECT_TRUE(Result);
   EXPECT_EQ(JoinedLoc.Kind, DbgValue::Def);
-  EXPECT_EQ(JoinedLoc.ID, LiveInRsp);
+  EXPECT_EQ(JoinedLoc.getDbgOpID(0), LiveInRspID);
 
   // If there is a VPHI: don't elimiante it if there are disagreeing values.
-  VLiveOuts[0] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);
-  VLiveOuts[1] = DbgValue(LiveInRax, EmptyProps, DbgValue::Def);
+  VLiveOuts[0] = DbgValue(LiveInRspID, EmptyProps);
+  VLiveOuts[1] = DbgValue(LiveInRaxID, EmptyProps);
   JoinedLoc = DbgValue(1, EmptyProps, DbgValue::VPHI);
   Result = vlocJoin(*MBB1, VLiveOutIdx, AllBlocks, JoinedLoc);
   EXPECT_FALSE(Result);
@@ -2372,20 +2410,20 @@ TEST_F(InstrRefLDVTest, vlocJoinLoops) {
   EXPECT_EQ(JoinedLoc.BlockNo, 1);
 
   // If we feed this VPHI back into itself though, we can eliminate it.
-  VLiveOuts[0] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);
+  VLiveOuts[0] = DbgValue(LiveInRspID, EmptyProps);
   VLiveOuts[1] = DbgValue(1, EmptyProps, DbgValue::VPHI);
   JoinedLoc = DbgValue(1, EmptyProps, DbgValue::VPHI);
   Result = vlocJoin(*MBB1, VLiveOutIdx, AllBlocks, JoinedLoc);
   EXPECT_TRUE(Result);
   EXPECT_EQ(JoinedLoc.Kind, DbgValue::Def);
-  EXPECT_EQ(JoinedLoc.ID, LiveInRsp);
+  EXPECT_EQ(JoinedLoc.getDbgOpID(0), LiveInRspID);
 
   // Don't eliminate backedge VPHIs if the predecessors have 
diff erent
   // properties.
   DIExpression *NewExpr =
       DIExpression::prepend(EmptyExpr, DIExpression::ApplyOffset, 4);
   DbgValueProperties PropsWithExpr(NewExpr, false, false);
-  VLiveOuts[0] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);
+  VLiveOuts[0] = DbgValue(LiveInRspID, EmptyProps);
   VLiveOuts[1] = DbgValue(1, PropsWithExpr, DbgValue::VPHI);
   JoinedLoc = DbgValue(1, EmptyProps, DbgValue::VPHI);
   Result = vlocJoin(*MBB1, VLiveOutIdx, AllBlocks, JoinedLoc);
@@ -2394,7 +2432,7 @@ TEST_F(InstrRefLDVTest, vlocJoinLoops) {
   EXPECT_EQ(JoinedLoc.BlockNo, 1);
 
   // Backedges with VPHIs, but from the wrong block, shouldn't be eliminated.
-  VLiveOuts[0] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);
+  VLiveOuts[0] = DbgValue(LiveInRspID, EmptyProps);
   VLiveOuts[1] = DbgValue(0, EmptyProps, DbgValue::VPHI);
   JoinedLoc = DbgValue(1, EmptyProps, DbgValue::VPHI);
   Result = vlocJoin(*MBB1, VLiveOutIdx, AllBlocks, JoinedLoc);
@@ -2426,9 +2464,9 @@ TEST_F(InstrRefLDVTest, vlocJoinBadlyNestedLoops) {
 
   unsigned EntryBlk = 0;
 
-  ValueIDNum LiveInRsp(EntryBlk, 0, RspLoc);
-  ValueIDNum LiveInRax(EntryBlk, 0, RaxLoc);
-  ValueIDNum LiveInRbx(EntryBlk, 0, RbxLoc);
+  DbgOpID LiveInRspID = DbgOpID(false, 0);
+  DbgOpID LiveInRaxID = DbgOpID(false, 1);
+  DbgOpID LiveInRbxID = DbgOpID(false, 2);
 
   DebugVariable Var(FuncVariable, None, nullptr);
   DbgValueProperties EmptyProps(EmptyExpr, false, false);
@@ -2457,9 +2495,9 @@ TEST_F(InstrRefLDVTest, vlocJoinBadlyNestedLoops) {
   AllVars.insert(Var);
 
   // Test a normal VPHI isn't eliminated.
-  VLiveOuts[0] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);
-  VLiveOuts[1] = DbgValue(LiveInRax, EmptyProps, DbgValue::Def);
-  VLiveOuts[2] = DbgValue(LiveInRbx, EmptyProps, DbgValue::Def);
+  VLiveOuts[0] = DbgValue(LiveInRspID, EmptyProps);
+  VLiveOuts[1] = DbgValue(LiveInRaxID, EmptyProps);
+  VLiveOuts[2] = DbgValue(LiveInRbxID, EmptyProps);
   DbgValue JoinedLoc = DbgValue(1, EmptyProps, DbgValue::VPHI);
   bool Result = vlocJoin(*MBB1, VLiveOutIdx, AllBlocks, JoinedLoc);
   EXPECT_FALSE(Result);
@@ -2467,18 +2505,18 @@ TEST_F(InstrRefLDVTest, vlocJoinBadlyNestedLoops) {
   EXPECT_EQ(JoinedLoc.BlockNo, 1);
 
   // Common VPHIs on backedges should merge.
-  VLiveOuts[0] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);
+  VLiveOuts[0] = DbgValue(LiveInRspID, EmptyProps);
   VLiveOuts[1] = DbgValue(1, EmptyProps, DbgValue::VPHI);
   VLiveOuts[2] = DbgValue(1, EmptyProps, DbgValue::VPHI);
   JoinedLoc = DbgValue(1, EmptyProps, DbgValue::VPHI);
   Result = vlocJoin(*MBB1, VLiveOutIdx, AllBlocks, JoinedLoc);
   EXPECT_TRUE(Result);
   EXPECT_EQ(JoinedLoc.Kind, DbgValue::Def);
-  EXPECT_EQ(JoinedLoc.ID, LiveInRsp);
+  EXPECT_EQ(JoinedLoc.getDbgOpID(0), LiveInRspID);
 
   // They shouldn't merge if one of their properties is 
diff erent.
   DbgValueProperties PropsWithIndirect(EmptyExpr, true, false);
-  VLiveOuts[0] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);
+  VLiveOuts[0] = DbgValue(LiveInRspID, EmptyProps);
   VLiveOuts[1] = DbgValue(1, EmptyProps, DbgValue::VPHI);
   VLiveOuts[2] = DbgValue(1, PropsWithIndirect, DbgValue::VPHI);
   JoinedLoc = DbgValue(1, EmptyProps, DbgValue::VPHI);
@@ -2488,7 +2526,7 @@ TEST_F(InstrRefLDVTest, vlocJoinBadlyNestedLoops) {
   EXPECT_EQ(JoinedLoc.BlockNo, 1);
 
   // VPHIs from 
diff erent blocks should not merge.
-  VLiveOuts[0] = DbgValue(LiveInRsp, EmptyProps, DbgValue::Def);
+  VLiveOuts[0] = DbgValue(LiveInRspID, EmptyProps);
   VLiveOuts[1] = DbgValue(1, EmptyProps, DbgValue::VPHI);
   VLiveOuts[2] = DbgValue(2, EmptyProps, DbgValue::VPHI);
   JoinedLoc = DbgValue(1, EmptyProps, DbgValue::VPHI);
@@ -2513,6 +2551,7 @@ TEST_F(InstrRefLDVTest, VLocSingleBlock) {
   std::tie(MInLocs, MOutLocs) = allocValueTables(1, 2);
 
   ValueIDNum LiveInRsp = ValueIDNum(0, 0, RspLoc);
+  DbgOpID LiveInRspID = addValueDbgOp(LiveInRsp);
   MInLocs[0][0] = MOutLocs[0][0] = LiveInRsp;
 
   DebugVariable Var(FuncVariable, None, nullptr);
@@ -2541,7 +2580,7 @@ TEST_F(InstrRefLDVTest, VLocSingleBlock) {
 
   // If we put an assignment in the transfer function, that should... well,
   // do nothing, because we don't store the live-outs.
-  VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  VLocs[0].Vars.insert({Var, DbgValue(LiveInRspID, EmptyProps)});
   buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
                     MOutLocs, MInLocs, VLocs);
   EXPECT_EQ(Output.size(), 0ul);
@@ -2568,6 +2607,9 @@ TEST_F(InstrRefLDVTest, VLocDiamondBlocks) {
   ValueIDNum LiveInRsp = ValueIDNum(EntryBlk, 0, RspLoc);
   ValueIDNum LiveInRax = ValueIDNum(EntryBlk, 0, RaxLoc);
   ValueIDNum RspPHIInBlk3 = ValueIDNum(RetBlk, 0, RspLoc);
+  DbgOpID LiveInRspID = addValueDbgOp(LiveInRsp);
+  DbgOpID LiveInRaxID = addValueDbgOp(LiveInRax);
+  DbgOpID RspPHIInBlk3ID = addValueDbgOp(RspPHIInBlk3);
 
   FuncValueTable MInLocs, MOutLocs;
   std::tie(MInLocs, MOutLocs) = allocValueTables(4, 2);
@@ -2618,7 +2660,7 @@ TEST_F(InstrRefLDVTest, VLocDiamondBlocks) {
 
   // An assignment in the end block should also not affect other blocks; or
   // produce any live-ins.
-  VLocs[3].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  VLocs[3].Vars.insert({Var, DbgValue(LiveInRspID, EmptyProps)});
   buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
                     MOutLocs, MInLocs, VLocs);
   EXPECT_EQ(Output[0].size(), 0ul);
@@ -2630,7 +2672,7 @@ TEST_F(InstrRefLDVTest, VLocDiamondBlocks) {
   // Assignments in either of the side-of-diamond blocks should also not be
   // propagated anywhere.
   VLocs[3].Vars.clear();
-  VLocs[2].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  VLocs[2].Vars.insert({Var, DbgValue(LiveInRspID, EmptyProps)});
   buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
                     MOutLocs, MInLocs, VLocs);
   EXPECT_EQ(Output[0].size(), 0ul);
@@ -2640,7 +2682,7 @@ TEST_F(InstrRefLDVTest, VLocDiamondBlocks) {
   VLocs[2].Vars.clear();
   ClearOutputs();
 
-  VLocs[1].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  VLocs[1].Vars.insert({Var, DbgValue(LiveInRspID, EmptyProps)});
   buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
                     MOutLocs, MInLocs, VLocs);
   EXPECT_EQ(Output[0].size(), 0ul);
@@ -2652,7 +2694,7 @@ TEST_F(InstrRefLDVTest, VLocDiamondBlocks) {
 
   // However: putting an assignment in the first block should propagate variable
   // values through to all other blocks, as it dominates.
-  VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  VLocs[0].Vars.insert({Var, DbgValue(LiveInRspID, EmptyProps)});
   buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
                     MOutLocs, MInLocs, VLocs);
   EXPECT_EQ(Output[0].size(), 0ul);
@@ -2660,11 +2702,11 @@ TEST_F(InstrRefLDVTest, VLocDiamondBlocks) {
   ASSERT_EQ(Output[2].size(), 1ul);
   ASSERT_EQ(Output[3].size(), 1ul);
   EXPECT_EQ(Output[1][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[1][0].second.ID, LiveInRsp);
+  EXPECT_EQ(Output[1][0].second.getDbgOpID(0), LiveInRspID);
   EXPECT_EQ(Output[2][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[2][0].second.ID, LiveInRsp);
+  EXPECT_EQ(Output[2][0].second.getDbgOpID(0), LiveInRspID);
   EXPECT_EQ(Output[3][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[3][0].second.ID, LiveInRsp);
+  EXPECT_EQ(Output[3][0].second.getDbgOpID(0), LiveInRspID);
   ClearOutputs();
   VLocs[0].Vars.clear();
 
@@ -2672,7 +2714,7 @@ TEST_F(InstrRefLDVTest, VLocDiamondBlocks) {
   // should still be propagated, as buildVLocValueMap shouldn't care about
   // what's in the registers (except for PHIs).
   // values through to all other blocks, as it dominates.
-  VLocs[0].Vars.insert({Var, DbgValue(LiveInRax, EmptyProps, DbgValue::Def)});
+  VLocs[0].Vars.insert({Var, DbgValue(LiveInRaxID, EmptyProps)});
   buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
                     MOutLocs, MInLocs, VLocs);
   EXPECT_EQ(Output[0].size(), 0ul);
@@ -2680,18 +2722,18 @@ TEST_F(InstrRefLDVTest, VLocDiamondBlocks) {
   ASSERT_EQ(Output[2].size(), 1ul);
   ASSERT_EQ(Output[3].size(), 1ul);
   EXPECT_EQ(Output[1][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[1][0].second.ID, LiveInRax);
+  EXPECT_EQ(Output[1][0].second.getDbgOpID(0), LiveInRaxID);
   EXPECT_EQ(Output[2][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[2][0].second.ID, LiveInRax);
+  EXPECT_EQ(Output[2][0].second.getDbgOpID(0), LiveInRaxID);
   EXPECT_EQ(Output[3][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[3][0].second.ID, LiveInRax);
+  EXPECT_EQ(Output[3][0].second.getDbgOpID(0), LiveInRaxID);
   ClearOutputs();
   VLocs[0].Vars.clear();
 
   // We should get a live-in to the merging block, if there are two assigns of
   // the same value in either side of the diamond.
-  VLocs[1].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
-  VLocs[2].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  VLocs[1].Vars.insert({Var, DbgValue(LiveInRspID, EmptyProps)});
+  VLocs[2].Vars.insert({Var, DbgValue(LiveInRspID, EmptyProps)});
   buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
                     MOutLocs, MInLocs, VLocs);
   EXPECT_EQ(Output[0].size(), 0ul);
@@ -2699,14 +2741,14 @@ TEST_F(InstrRefLDVTest, VLocDiamondBlocks) {
   EXPECT_EQ(Output[2].size(), 0ul);
   ASSERT_EQ(Output[3].size(), 1ul);
   EXPECT_EQ(Output[3][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[3][0].second.ID, LiveInRsp);
+  EXPECT_EQ(Output[3][0].second.getDbgOpID(0), LiveInRspID);
   ClearOutputs();
   VLocs[1].Vars.clear();
   VLocs[2].Vars.clear();
 
   // If we assign a value in the entry block, then 'undef' on a branch, we
   // shouldn't have a live-in in the merge block.
-  VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  VLocs[0].Vars.insert({Var, DbgValue(LiveInRspID, EmptyProps)});
   VLocs[1].Vars.insert({Var, DbgValue(EmptyProps, DbgValue::Undef)});
   buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
                     MOutLocs, MInLocs, VLocs);
@@ -2715,9 +2757,9 @@ TEST_F(InstrRefLDVTest, VLocDiamondBlocks) {
   ASSERT_EQ(Output[2].size(), 1ul);
   EXPECT_EQ(Output[3].size(), 0ul);
   EXPECT_EQ(Output[1][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[1][0].second.ID, LiveInRsp);
+  EXPECT_EQ(Output[1][0].second.getDbgOpID(0), LiveInRspID);
   EXPECT_EQ(Output[2][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[2][0].second.ID, LiveInRsp);
+  EXPECT_EQ(Output[2][0].second.getDbgOpID(0), LiveInRspID);
   ClearOutputs();
   VLocs[0].Vars.clear();
   VLocs[1].Vars.clear();
@@ -2725,8 +2767,8 @@ TEST_F(InstrRefLDVTest, VLocDiamondBlocks) {
   // Having 
diff erent values joining into the merge block should mean we have
   // no live-in in that block. Block ones LiveInRax value doesn't appear as a
   // live-in anywhere, it's block internal.
-  VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
-  VLocs[1].Vars.insert({Var, DbgValue(LiveInRax, EmptyProps, DbgValue::Def)});
+  VLocs[0].Vars.insert({Var, DbgValue(LiveInRspID, EmptyProps)});
+  VLocs[1].Vars.insert({Var, DbgValue(LiveInRaxID, EmptyProps)});
   buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
                     MOutLocs, MInLocs, VLocs);
   EXPECT_EQ(Output[0].size(), 0ul);
@@ -2734,9 +2776,9 @@ TEST_F(InstrRefLDVTest, VLocDiamondBlocks) {
   ASSERT_EQ(Output[2].size(), 1ul);
   EXPECT_EQ(Output[3].size(), 0ul);
   EXPECT_EQ(Output[1][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[1][0].second.ID, LiveInRsp);
+  EXPECT_EQ(Output[1][0].second.getDbgOpID(0), LiveInRspID);
   EXPECT_EQ(Output[2][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[2][0].second.ID, LiveInRsp);
+  EXPECT_EQ(Output[2][0].second.getDbgOpID(0), LiveInRspID);
   ClearOutputs();
   VLocs[0].Vars.clear();
   VLocs[1].Vars.clear();
@@ -2744,8 +2786,8 @@ TEST_F(InstrRefLDVTest, VLocDiamondBlocks) {
   // But on the other hand, if there's a location in the register file where
   // those two values can be joined, do so.
   MOutLocs[1][0] = LiveInRax;
-  VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
-  VLocs[1].Vars.insert({Var, DbgValue(LiveInRax, EmptyProps, DbgValue::Def)});
+  VLocs[0].Vars.insert({Var, DbgValue(LiveInRspID, EmptyProps)});
+  VLocs[1].Vars.insert({Var, DbgValue(LiveInRaxID, EmptyProps)});
   buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
                     MOutLocs, MInLocs, VLocs);
   EXPECT_EQ(Output[0].size(), 0ul);
@@ -2753,11 +2795,11 @@ TEST_F(InstrRefLDVTest, VLocDiamondBlocks) {
   ASSERT_EQ(Output[2].size(), 1ul);
   ASSERT_EQ(Output[3].size(), 1ul);
   EXPECT_EQ(Output[1][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[1][0].second.ID, LiveInRsp);
+  EXPECT_EQ(Output[1][0].second.getDbgOpID(0), LiveInRspID);
   EXPECT_EQ(Output[2][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[2][0].second.ID, LiveInRsp);
+  EXPECT_EQ(Output[2][0].second.getDbgOpID(0), LiveInRspID);
   EXPECT_EQ(Output[3][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[3][0].second.ID, RspPHIInBlk3);
+  EXPECT_EQ(Output[3][0].second.getDbgOpID(0), RspPHIInBlk3ID);
   ClearOutputs();
   VLocs[0].Vars.clear();
   VLocs[1].Vars.clear();
@@ -2785,6 +2827,11 @@ TEST_F(InstrRefLDVTest, VLocSimpleLoop) {
   ValueIDNum RspPHIInBlk1 = ValueIDNum(LoopBlk, 0, RspLoc);
   ValueIDNum RspDefInBlk1 = ValueIDNum(LoopBlk, 1, RspLoc);
   ValueIDNum RaxPHIInBlk1 = ValueIDNum(LoopBlk, 0, RaxLoc);
+  DbgOpID LiveInRspID = addValueDbgOp(LiveInRsp);
+  DbgOpID LiveInRaxID = addValueDbgOp(LiveInRax);
+  DbgOpID RspPHIInBlk1ID = addValueDbgOp(RspPHIInBlk1);
+  DbgOpID RspDefInBlk1ID = addValueDbgOp(RspDefInBlk1);
+  DbgOpID RaxPHIInBlk1ID = addValueDbgOp(RaxPHIInBlk1);
 
   FuncValueTable MInLocs, MOutLocs;
   std::tie(MInLocs, MOutLocs) = allocValueTables(3, 2);
@@ -2821,22 +2868,22 @@ TEST_F(InstrRefLDVTest, VLocSimpleLoop) {
   Output.resize(3);
 
   // Easy starter: a dominating assign should propagate to all blocks.
-  VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  VLocs[0].Vars.insert({Var, DbgValue(LiveInRspID, EmptyProps)});
   buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
                     MOutLocs, MInLocs, VLocs);
   EXPECT_EQ(Output[0].size(), 0ul);
   ASSERT_EQ(Output[1].size(), 1ul);
   ASSERT_EQ(Output[2].size(), 1ul);
   EXPECT_EQ(Output[1][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[1][0].second.ID, LiveInRsp);
+  EXPECT_EQ(Output[1][0].second.getDbgOpID(0), LiveInRspID);
   EXPECT_EQ(Output[2][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[2][0].second.ID, LiveInRsp);
+  EXPECT_EQ(Output[2][0].second.getDbgOpID(0), LiveInRspID);
   ClearOutputs();
   VLocs[0].Vars.clear();
   VLocs[1].Vars.clear();
 
   // Put an undef assignment in the loop. Should get no live-in value.
-  VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  VLocs[0].Vars.insert({Var, DbgValue(LiveInRspID, EmptyProps)});
   VLocs[1].Vars.insert({Var, DbgValue(EmptyProps, DbgValue::Undef)});
   buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
                     MOutLocs, MInLocs, VLocs);
@@ -2848,32 +2895,32 @@ TEST_F(InstrRefLDVTest, VLocSimpleLoop) {
   VLocs[1].Vars.clear();
 
   // Assignment of the same value should naturally join.
-  VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
-  VLocs[1].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  VLocs[0].Vars.insert({Var, DbgValue(LiveInRspID, EmptyProps)});
+  VLocs[1].Vars.insert({Var, DbgValue(LiveInRspID, EmptyProps)});
   buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
                     MOutLocs, MInLocs, VLocs);
   EXPECT_EQ(Output[0].size(), 0ul);
   ASSERT_EQ(Output[1].size(), 1ul);
   ASSERT_EQ(Output[2].size(), 1ul);
   EXPECT_EQ(Output[1][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[1][0].second.ID, LiveInRsp);
+  EXPECT_EQ(Output[1][0].second.getDbgOpID(0), LiveInRspID);
   EXPECT_EQ(Output[2][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[2][0].second.ID, LiveInRsp);
+  EXPECT_EQ(Output[2][0].second.getDbgOpID(0), LiveInRspID);
   ClearOutputs();
   VLocs[0].Vars.clear();
   VLocs[1].Vars.clear();
 
   // Assignment of 
diff erent values shouldn't join with no machine PHI vals.
   // Will be live-in to exit block as it's dominated.
-  VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
-  VLocs[1].Vars.insert({Var, DbgValue(LiveInRax, EmptyProps, DbgValue::Def)});
+  VLocs[0].Vars.insert({Var, DbgValue(LiveInRspID, EmptyProps)});
+  VLocs[1].Vars.insert({Var, DbgValue(LiveInRaxID, EmptyProps)});
   buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
                     MOutLocs, MInLocs, VLocs);
   EXPECT_EQ(Output[0].size(), 0ul);
   EXPECT_EQ(Output[1].size(), 0ul);
   ASSERT_EQ(Output[2].size(), 1ul);
   EXPECT_EQ(Output[2][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[2][0].second.ID, LiveInRax);
+  EXPECT_EQ(Output[2][0].second.getDbgOpID(0), LiveInRaxID);
   ClearOutputs();
   VLocs[0].Vars.clear();
   VLocs[1].Vars.clear();
@@ -2882,15 +2929,15 @@ TEST_F(InstrRefLDVTest, VLocSimpleLoop) {
   // with unrelated assign in loop block again.
   MInLocs[1][0] = RspPHIInBlk1;
   MOutLocs[1][0] = RspDefInBlk1;
-  VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
-  VLocs[1].Vars.insert({Var, DbgValue(LiveInRax, EmptyProps, DbgValue::Def)});
+  VLocs[0].Vars.insert({Var, DbgValue(LiveInRspID, EmptyProps)});
+  VLocs[1].Vars.insert({Var, DbgValue(LiveInRaxID, EmptyProps)});
   buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
                     MOutLocs, MInLocs, VLocs);
   EXPECT_EQ(Output[0].size(), 0ul);
   EXPECT_EQ(Output[1].size(), 0ul);
   ASSERT_EQ(Output[2].size(), 1ul);
   EXPECT_EQ(Output[2][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[2][0].second.ID, LiveInRax);
+  EXPECT_EQ(Output[2][0].second.getDbgOpID(0), LiveInRaxID);
   ClearOutputs();
   VLocs[0].Vars.clear();
   VLocs[1].Vars.clear();
@@ -2899,17 +2946,17 @@ TEST_F(InstrRefLDVTest, VLocSimpleLoop) {
   // find the appropriate PHI.
   MInLocs[1][0] = RspPHIInBlk1;
   MOutLocs[1][0] = RspDefInBlk1;
-  VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
-  VLocs[1].Vars.insert({Var, DbgValue(RspDefInBlk1, EmptyProps, DbgValue::Def)});
+  VLocs[0].Vars.insert({Var, DbgValue(LiveInRspID, EmptyProps)});
+  VLocs[1].Vars.insert({Var, DbgValue(RspDefInBlk1ID, EmptyProps)});
   buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
                     MOutLocs, MInLocs, VLocs);
   EXPECT_EQ(Output[0].size(), 0ul);
   ASSERT_EQ(Output[1].size(), 1ul);
   ASSERT_EQ(Output[2].size(), 1ul);
   EXPECT_EQ(Output[1][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[1][0].second.ID, RspPHIInBlk1);
+  EXPECT_EQ(Output[1][0].second.getDbgOpID(0), RspPHIInBlk1ID);
   EXPECT_EQ(Output[2][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[2][0].second.ID, RspDefInBlk1);
+  EXPECT_EQ(Output[2][0].second.getDbgOpID(0), RspDefInBlk1ID);
   ClearOutputs();
   VLocs[0].Vars.clear();
   VLocs[1].Vars.clear();
@@ -2920,17 +2967,17 @@ TEST_F(InstrRefLDVTest, VLocSimpleLoop) {
   MOutLocs[1][0] = LiveInRsp;
   MInLocs[1][1] = RaxPHIInBlk1;
   MOutLocs[1][1] = RspDefInBlk1;
-  VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
-  VLocs[1].Vars.insert({Var, DbgValue(RspDefInBlk1, EmptyProps, DbgValue::Def)});
+  VLocs[0].Vars.insert({Var, DbgValue(LiveInRspID, EmptyProps)});
+  VLocs[1].Vars.insert({Var, DbgValue(RspDefInBlk1ID, EmptyProps)});
   buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
                     MOutLocs, MInLocs, VLocs);
   EXPECT_EQ(Output[0].size(), 0ul);
   ASSERT_EQ(Output[1].size(), 1ul);
   ASSERT_EQ(Output[2].size(), 1ul);
   EXPECT_EQ(Output[1][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[1][0].second.ID, RaxPHIInBlk1);
+  EXPECT_EQ(Output[1][0].second.getDbgOpID(0), RaxPHIInBlk1ID);
   EXPECT_EQ(Output[2][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[2][0].second.ID, RspDefInBlk1);
+  EXPECT_EQ(Output[2][0].second.getDbgOpID(0), RspDefInBlk1ID);
   ClearOutputs();
   VLocs[0].Vars.clear();
   VLocs[1].Vars.clear();
@@ -2942,8 +2989,8 @@ TEST_F(InstrRefLDVTest, VLocSimpleLoop) {
   MOutLocs[1][0] = RspDefInBlk1;
   MInLocs[1][1] = RaxPHIInBlk1;
   MOutLocs[1][1] = RspDefInBlk1;
-  VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
-  VLocs[1].Vars.insert({Var, DbgValue(RspDefInBlk1, EmptyProps, DbgValue::Def)});
+  VLocs[0].Vars.insert({Var, DbgValue(LiveInRspID, EmptyProps)});
+  VLocs[1].Vars.insert({Var, DbgValue(RspDefInBlk1ID, EmptyProps)});
   buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
                     MOutLocs, MInLocs, VLocs);
   EXPECT_EQ(Output[0].size(), 0ul);
@@ -2951,9 +2998,9 @@ TEST_F(InstrRefLDVTest, VLocSimpleLoop) {
   ASSERT_EQ(Output[2].size(), 1ul);
   EXPECT_EQ(Output[1][0].second.Kind, DbgValue::Def);
   // Today, the first register is picked.
-  EXPECT_EQ(Output[1][0].second.ID, RspPHIInBlk1);
+  EXPECT_EQ(Output[1][0].second.getDbgOpID(0), RspPHIInBlk1ID);
   EXPECT_EQ(Output[2][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[2][0].second.ID, RspDefInBlk1);
+  EXPECT_EQ(Output[2][0].second.getDbgOpID(0), RspDefInBlk1ID);
   ClearOutputs();
   VLocs[0].Vars.clear();
   VLocs[1].Vars.clear();
@@ -2976,17 +3023,17 @@ TEST_F(InstrRefLDVTest, VLocSimpleLoop) {
   MOutLocs[1][0] = RspPHIInBlk1;
   MInLocs[1][1] = LiveInRax;
   MOutLocs[1][1] = LiveInRax;
-  VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
-  VLocs[1].Vars.insert({Var, DbgValue(RspPHIInBlk1, EmptyProps, DbgValue::Def)});
+  VLocs[0].Vars.insert({Var, DbgValue(LiveInRspID, EmptyProps)});
+  VLocs[1].Vars.insert({Var, DbgValue(RspPHIInBlk1ID, EmptyProps)});
   buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
                     MOutLocs, MInLocs, VLocs);
   EXPECT_EQ(Output[0].size(), 0ul);
   ASSERT_EQ(Output[1].size(), 1ul);
   ASSERT_EQ(Output[2].size(), 1ul);
   EXPECT_EQ(Output[1][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[1][0].second.ID, RspPHIInBlk1);
+  EXPECT_EQ(Output[1][0].second.getDbgOpID(0), RspPHIInBlk1ID);
   EXPECT_EQ(Output[2][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[2][0].second.ID, RspPHIInBlk1);
+  EXPECT_EQ(Output[2][0].second.getDbgOpID(0), RspPHIInBlk1ID);
   ClearOutputs();
   VLocs[0].Vars.clear();
   VLocs[1].Vars.clear();
@@ -2995,17 +3042,17 @@ TEST_F(InstrRefLDVTest, VLocSimpleLoop) {
   // because there's a def in in.
   MInLocs[1][0] = LiveInRsp;
   MOutLocs[1][0] = LiveInRsp;
-  VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
-  VLocs[1].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  VLocs[0].Vars.insert({Var, DbgValue(LiveInRspID, EmptyProps)});
+  VLocs[1].Vars.insert({Var, DbgValue(LiveInRspID, EmptyProps)});
   buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
                     MOutLocs, MInLocs, VLocs);
   EXPECT_EQ(Output[0].size(), 0ul);
   ASSERT_EQ(Output[1].size(), 1ul);
   ASSERT_EQ(Output[2].size(), 1ul);
   EXPECT_EQ(Output[1][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[1][0].second.ID, LiveInRsp);
+  EXPECT_EQ(Output[1][0].second.getDbgOpID(0), LiveInRspID);
   EXPECT_EQ(Output[2][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[2][0].second.ID, LiveInRsp);
+  EXPECT_EQ(Output[2][0].second.getDbgOpID(0), LiveInRspID);
   ClearOutputs();
   VLocs[0].Vars.clear();
   VLocs[1].Vars.clear();
@@ -3038,6 +3085,11 @@ TEST_F(InstrRefLDVTest, VLocNestedLoop) {
   ValueIDNum RspPHIInBlk1 = ValueIDNum(Loop1Blk, 0, RspLoc);
   ValueIDNum RspPHIInBlk2 = ValueIDNum(Loop2Blk, 0, RspLoc);
   ValueIDNum RspDefInBlk2 = ValueIDNum(Loop2Blk, 1, RspLoc);
+  DbgOpID LiveInRspID = addValueDbgOp(LiveInRsp);
+  DbgOpID LiveInRaxID = addValueDbgOp(LiveInRax);
+  DbgOpID RspPHIInBlk1ID = addValueDbgOp(RspPHIInBlk1);
+  DbgOpID RspPHIInBlk2ID = addValueDbgOp(RspPHIInBlk2);
+  DbgOpID RspDefInBlk2ID = addValueDbgOp(RspDefInBlk2);
 
   FuncValueTable MInLocs, MOutLocs;
   std::tie(MInLocs, MOutLocs) = allocValueTables(5, 2);
@@ -3076,7 +3128,7 @@ TEST_F(InstrRefLDVTest, VLocNestedLoop) {
   Output.resize(5);
 
   // A dominating assign should propagate to all blocks.
-  VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  VLocs[0].Vars.insert({Var, DbgValue(LiveInRspID, EmptyProps)});
   buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
                     MOutLocs, MInLocs, VLocs);
   EXPECT_EQ(Output[0].size(), 0ul);
@@ -3085,20 +3137,20 @@ TEST_F(InstrRefLDVTest, VLocNestedLoop) {
   ASSERT_EQ(Output[3].size(), 1ul);
   ASSERT_EQ(Output[4].size(), 1ul);
   EXPECT_EQ(Output[1][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[1][0].second.ID, LiveInRsp);
+  EXPECT_EQ(Output[1][0].second.getDbgOpID(0), LiveInRspID);
   EXPECT_EQ(Output[2][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[2][0].second.ID, LiveInRsp);
+  EXPECT_EQ(Output[2][0].second.getDbgOpID(0), LiveInRspID);
   EXPECT_EQ(Output[3][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[3][0].second.ID, LiveInRsp);
+  EXPECT_EQ(Output[3][0].second.getDbgOpID(0), LiveInRspID);
   EXPECT_EQ(Output[4][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[4][0].second.ID, LiveInRsp);
+  EXPECT_EQ(Output[4][0].second.getDbgOpID(0), LiveInRspID);
   ClearOutputs();
   VLocs[0].Vars.clear();
 
   // Test that an assign in the inner loop causes unresolved PHIs at the heads
   // of both loops, and no output location. Dominated blocks do get values.
-  VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
-  VLocs[2].Vars.insert({Var, DbgValue(LiveInRax, EmptyProps, DbgValue::Def)});
+  VLocs[0].Vars.insert({Var, DbgValue(LiveInRspID, EmptyProps)});
+  VLocs[2].Vars.insert({Var, DbgValue(LiveInRaxID, EmptyProps)});
   buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
                     MOutLocs, MInLocs, VLocs);
   EXPECT_EQ(Output[0].size(), 0ul);
@@ -3107,16 +3159,16 @@ TEST_F(InstrRefLDVTest, VLocNestedLoop) {
   ASSERT_EQ(Output[3].size(), 1ul);
   ASSERT_EQ(Output[4].size(), 1ul);
   EXPECT_EQ(Output[3][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[3][0].second.ID, LiveInRax);
+  EXPECT_EQ(Output[3][0].second.getDbgOpID(0), LiveInRaxID);
   EXPECT_EQ(Output[4][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[4][0].second.ID, LiveInRax);
+  EXPECT_EQ(Output[4][0].second.getDbgOpID(0), LiveInRaxID);
   ClearOutputs();
   VLocs[0].Vars.clear();
   VLocs[2].Vars.clear();
 
   // Same test, but with no assignment in block 0. We should still get values
   // in dominated blocks.
-  VLocs[2].Vars.insert({Var, DbgValue(LiveInRax, EmptyProps, DbgValue::Def)});
+  VLocs[2].Vars.insert({Var, DbgValue(LiveInRaxID, EmptyProps)});
   buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
                     MOutLocs, MInLocs, VLocs);
   EXPECT_EQ(Output[0].size(), 0ul);
@@ -3125,16 +3177,16 @@ TEST_F(InstrRefLDVTest, VLocNestedLoop) {
   ASSERT_EQ(Output[3].size(), 1ul);
   ASSERT_EQ(Output[4].size(), 1ul);
   EXPECT_EQ(Output[3][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[3][0].second.ID, LiveInRax);
+  EXPECT_EQ(Output[3][0].second.getDbgOpID(0), LiveInRaxID);
   EXPECT_EQ(Output[4][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[4][0].second.ID, LiveInRax);
+  EXPECT_EQ(Output[4][0].second.getDbgOpID(0), LiveInRaxID);
   ClearOutputs();
   VLocs[2].Vars.clear();
 
   // Similarly, assignments in the outer loop gives location to dominated
   // blocks, but no PHI locations are found at the outer loop head.
-  VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
-  VLocs[3].Vars.insert({Var, DbgValue(LiveInRax, EmptyProps, DbgValue::Def)});
+  VLocs[0].Vars.insert({Var, DbgValue(LiveInRspID, EmptyProps)});
+  VLocs[3].Vars.insert({Var, DbgValue(LiveInRaxID, EmptyProps)});
   buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
                     MOutLocs, MInLocs, VLocs);
   EXPECT_EQ(Output[0].size(), 0ul);
@@ -3143,13 +3195,13 @@ TEST_F(InstrRefLDVTest, VLocNestedLoop) {
   EXPECT_EQ(Output[3].size(), 0ul);
   ASSERT_EQ(Output[4].size(), 1ul);
   EXPECT_EQ(Output[4][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[4][0].second.ID, LiveInRax);
+  EXPECT_EQ(Output[4][0].second.getDbgOpID(0), LiveInRaxID);
   ClearOutputs();
   VLocs[0].Vars.clear();
   VLocs[3].Vars.clear();
 
-  VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
-  VLocs[1].Vars.insert({Var, DbgValue(LiveInRax, EmptyProps, DbgValue::Def)});
+  VLocs[0].Vars.insert({Var, DbgValue(LiveInRspID, EmptyProps)});
+  VLocs[1].Vars.insert({Var, DbgValue(LiveInRaxID, EmptyProps)});
   buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
                     MOutLocs, MInLocs, VLocs);
   EXPECT_EQ(Output[0].size(), 0ul);
@@ -3158,11 +3210,11 @@ TEST_F(InstrRefLDVTest, VLocNestedLoop) {
   ASSERT_EQ(Output[3].size(), 1ul);
   ASSERT_EQ(Output[4].size(), 1ul);
   EXPECT_EQ(Output[2][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[2][0].second.ID, LiveInRax);
+  EXPECT_EQ(Output[2][0].second.getDbgOpID(0), LiveInRaxID);
   EXPECT_EQ(Output[3][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[3][0].second.ID, LiveInRax);
+  EXPECT_EQ(Output[3][0].second.getDbgOpID(0), LiveInRaxID);
   EXPECT_EQ(Output[4][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[4][0].second.ID, LiveInRax);
+  EXPECT_EQ(Output[4][0].second.getDbgOpID(0), LiveInRaxID);
   ClearOutputs();
   VLocs[0].Vars.clear();
   VLocs[1].Vars.clear();
@@ -3170,8 +3222,8 @@ TEST_F(InstrRefLDVTest, VLocNestedLoop) {
   // With an assignment of the same value in the inner loop, we should work out
   // that all PHIs can be eliminated and the same value is live-through the
   // whole function.
-  VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
-  VLocs[2].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  VLocs[0].Vars.insert({Var, DbgValue(LiveInRspID, EmptyProps)});
+  VLocs[2].Vars.insert({Var, DbgValue(LiveInRspID, EmptyProps)});
   buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
                     MOutLocs, MInLocs, VLocs);
   EXPECT_EQ(Output[0].size(), 0ul);
@@ -3180,13 +3232,13 @@ TEST_F(InstrRefLDVTest, VLocNestedLoop) {
   ASSERT_EQ(Output[3].size(), 1ul);
   ASSERT_EQ(Output[4].size(), 1ul);
   EXPECT_EQ(Output[1][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[1][0].second.ID, LiveInRsp);
+  EXPECT_EQ(Output[1][0].second.getDbgOpID(0), LiveInRspID);
   EXPECT_EQ(Output[2][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[2][0].second.ID, LiveInRsp);
+  EXPECT_EQ(Output[2][0].second.getDbgOpID(0), LiveInRspID);
   EXPECT_EQ(Output[3][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[3][0].second.ID, LiveInRsp);
+  EXPECT_EQ(Output[3][0].second.getDbgOpID(0), LiveInRspID);
   EXPECT_EQ(Output[4][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[4][0].second.ID, LiveInRsp);
+  EXPECT_EQ(Output[4][0].second.getDbgOpID(0), LiveInRspID);
   ClearOutputs();
   VLocs[0].Vars.clear();
   VLocs[2].Vars.clear();
@@ -3201,8 +3253,8 @@ TEST_F(InstrRefLDVTest, VLocNestedLoop) {
   // one. Even though RspPHIInBlk2 isn't available later in the function, we
   // should still produce a live-in value. The fact it's unavailable is a
   // 
diff erent concern.
-  VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
-  VLocs[2].Vars.insert({Var, DbgValue(LiveInRax, EmptyProps, DbgValue::Def)});
+  VLocs[0].Vars.insert({Var, DbgValue(LiveInRspID, EmptyProps)});
+  VLocs[2].Vars.insert({Var, DbgValue(LiveInRaxID, EmptyProps)});
   buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
                     MOutLocs, MInLocs, VLocs);
   EXPECT_EQ(Output[0].size(), 0ul);
@@ -3211,9 +3263,9 @@ TEST_F(InstrRefLDVTest, VLocNestedLoop) {
   ASSERT_EQ(Output[3].size(), 1ul);
   ASSERT_EQ(Output[4].size(), 1ul);
   EXPECT_EQ(Output[3][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[3][0].second.ID, LiveInRax);
+  EXPECT_EQ(Output[3][0].second.getDbgOpID(0), LiveInRaxID);
   EXPECT_EQ(Output[4][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[4][0].second.ID, LiveInRax);
+  EXPECT_EQ(Output[4][0].second.getDbgOpID(0), LiveInRaxID);
   ClearOutputs();
   VLocs[0].Vars.clear();
   VLocs[2].Vars.clear();
@@ -3227,8 +3279,8 @@ TEST_F(InstrRefLDVTest, VLocNestedLoop) {
   MOutLocs[2][0] = RspDefInBlk2;
   MInLocs[3][0] = RspDefInBlk2;
   MOutLocs[3][0] = RspDefInBlk2;
-  VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
-  VLocs[2].Vars.insert({Var, DbgValue(RspDefInBlk2, EmptyProps, DbgValue::Def)});
+  VLocs[0].Vars.insert({Var, DbgValue(LiveInRspID, EmptyProps)});
+  VLocs[2].Vars.insert({Var, DbgValue(RspDefInBlk2ID, EmptyProps)});
   buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
                     MOutLocs, MInLocs, VLocs);
   EXPECT_EQ(Output[0].size(), 0ul);
@@ -3237,13 +3289,13 @@ TEST_F(InstrRefLDVTest, VLocNestedLoop) {
   ASSERT_EQ(Output[3].size(), 1ul);
   ASSERT_EQ(Output[4].size(), 1ul);
   EXPECT_EQ(Output[1][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[1][0].second.ID, RspPHIInBlk1);
+  EXPECT_EQ(Output[1][0].second.getDbgOpID(0), RspPHIInBlk1ID);
   EXPECT_EQ(Output[2][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[2][0].second.ID, RspPHIInBlk2);
+  EXPECT_EQ(Output[2][0].second.getDbgOpID(0), RspPHIInBlk2ID);
   EXPECT_EQ(Output[3][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[3][0].second.ID, RspDefInBlk2);
+  EXPECT_EQ(Output[3][0].second.getDbgOpID(0), RspDefInBlk2ID);
   EXPECT_EQ(Output[4][0].second.Kind, DbgValue::Def);
-  EXPECT_EQ(Output[4][0].second.ID, RspDefInBlk2);
+  EXPECT_EQ(Output[4][0].second.getDbgOpID(0), RspDefInBlk2ID);
   ClearOutputs();
   VLocs[0].Vars.clear();
   VLocs[2].Vars.clear();


        


More information about the llvm-commits mailing list