[llvm] b5426ce - [DebugInfo][InstrRef] Place variable-values PHI using LLVM utilities

Jeremy Morse via llvm-commits llvm-commits at lists.llvm.org
Thu Oct 14 06:43:53 PDT 2021


Author: Jeremy Morse
Date: 2021-10-14T14:43:43+01:00
New Revision: b5426ced71280c340b005acc661e0778541800a5

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

LOG: [DebugInfo][InstrRef] Place variable-values PHI using LLVM utilities

This patch is very similar to D110173 / a3936a6c19c, but for variable
values rather than machine values. This is for the second instr-ref
problem, calculating the correct variable value on entry to each block.
The previous lattice based implementation was broken; we now use LLVMs
existing PHI placement utilities to work out where values need to merge,
then eliminate un-necessary ones through value propagation.

Most of the deletions here happen in vlocJoin: it was trying to pick a
location for PHIs to happen in, badly, leading to an infinite loop in the
MIR test added, where it would repeatedly switch between register
locations. The new approach is simpler: either PHIs can be eliminated, or
they can't, and the location of the value is a different problem.

Various bits and pieces move to the header so that they can be tested in
the unit tests. The DbgValue class grows a "VPHI" kind to represent
variable value PHIS that haven't been eliminated yet.

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

Added: 
    llvm/test/DebugInfo/MIR/InstrRef/pick-vphi-in-shifting-loop.mir

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 7e828134764d1..32937666ea7a5 100644
--- a/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp
+++ b/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp
@@ -157,58 +157,6 @@ class SpillLocationNo {
   unsigned id() const { return SpillNo; }
 };
 
-/// Collection of DBG_VALUEs observed when traversing a block. Records each
-/// variable and the value the DBG_VALUE refers to. Requires the machine value
-/// location dataflow algorithm to have run already, so that values can be
-/// identified.
-class VLocTracker {
-public:
-  /// Map DebugVariable to the latest Value it's defined to have.
-  /// Needs to be a MapVector because we determine order-in-the-input-MIR from
-  /// the order in this container.
-  /// We only retain the last DbgValue in each block for each variable, to
-  /// determine the blocks live-out variable value. The Vars container forms the
-  /// transfer function for this block, as part of the dataflow analysis. The
-  /// movement of values between locations inside of a block is handled at a
-  /// much later stage, in the TransferTracker class.
-  MapVector<DebugVariable, DbgValue> Vars;
-  DenseMap<DebugVariable, const DILocation *> Scopes;
-  MachineBasicBlock *MBB;
-
-public:
-  VLocTracker() {}
-
-  void defVar(const MachineInstr &MI, const DbgValueProperties &Properties,
-              Optional<ValueIDNum> ID) {
-    assert(MI.isDebugValue() || MI.isDebugRef());
-    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();
-  }
-
-  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);
-
-    // 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();
-  }
-};
-
 /// 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.
@@ -360,7 +308,7 @@ class TransferTracker {
     for (auto Var : VLocs) {
       if (Var.second.Kind == DbgValue::Const) {
         PendingDbgValues.push_back(
-            emitMOLoc(Var.second.MO, Var.first, Var.second.Properties));
+            emitMOLoc(*Var.second.MO, Var.first, Var.second.Properties));
         continue;
       }
 
@@ -692,11 +640,11 @@ ValueIDNum ValueIDNum::EmptyValue = {UINT_MAX, UINT_MAX, UINT_MAX};
 #ifndef NDEBUG
 void DbgValue::dump(const MLocTracker *MTrack) const {
   if (Kind == Const) {
-    MO.dump();
+    MO->dump();
   } else if (Kind == NoVal) {
     dbgs() << "NoVal(" << BlockNo << ")";
-  } else if (Kind == Proposed) {
-    dbgs() << "VPHI(" << MTrack->IDAsString(ID) << ")";
+  } else if (Kind == VPHI) {
+    dbgs() << "VPHI(" << BlockNo << "," << MTrack->IDAsString(ID) << ")";
   } else {
     assert(Kind == Def);
     dbgs() << MTrack->IDAsString(ID);
@@ -2070,162 +2018,126 @@ void InstrRefBasedLDV::BlockPHIPlacement(
   IDF.calculate(PHIBlocks);
 }
 
-bool InstrRefBasedLDV::vlocDowngradeLattice(
-    const MachineBasicBlock &MBB, const DbgValue &OldLiveInLocation,
-    const SmallVectorImpl<InValueT> &Values, unsigned CurBlockRPONum) {
-  // Ranking value preference: see file level comment, the highest rank is
-  // a plain def, followed by PHI values in reverse post-order. Numerically,
-  // we assign all defs the rank '0', all PHIs their blocks RPO number plus
-  // one, and consider the lowest value the highest ranked.
-  int OldLiveInRank = BBNumToRPO[OldLiveInLocation.ID.getBlock()] + 1;
-  if (!OldLiveInLocation.ID.isPHI())
-    OldLiveInRank = 0;
-
-  // Allow any unresolvable conflict to be over-ridden.
-  if (OldLiveInLocation.Kind == DbgValue::NoVal) {
-    // Although if it was an unresolvable conflict from _this_ block, then
-    // all other seeking of downgrades and PHIs must have failed before hand.
-    if (OldLiveInLocation.BlockNo == (unsigned)MBB.getNumber())
-      return false;
-    OldLiveInRank = INT_MIN;
-  }
-
-  auto &InValue = *Values[0].second;
-
-  if (InValue.Kind == DbgValue::Const || InValue.Kind == DbgValue::NoVal)
-    return false;
-
-  unsigned ThisRPO = BBNumToRPO[InValue.ID.getBlock()];
-  int ThisRank = ThisRPO + 1;
-  if (!InValue.ID.isPHI())
-    ThisRank = 0;
-
-  // Too far down the lattice?
-  if (ThisRPO >= CurBlockRPONum)
-    return false;
-
-  // Higher in the lattice than what we've already explored?
-  if (ThisRank <= OldLiveInRank)
-    return false;
-
-  return true;
-}
-
-std::tuple<Optional<ValueIDNum>, bool> InstrRefBasedLDV::pickVPHILoc(
-    MachineBasicBlock &MBB, const DebugVariable &Var, const LiveIdxT &LiveOuts,
-    ValueIDNum **MOutLocs, ValueIDNum **MInLocs,
-    const SmallVectorImpl<MachineBasicBlock *> &BlockOrders) {
+Optional<ValueIDNum> InstrRefBasedLDV::pickVPHILoc(
+    const MachineBasicBlock &MBB, const DebugVariable &Var,
+    const LiveIdxT &LiveOuts, ValueIDNum **MOutLocs,
+    const SmallVectorImpl<const MachineBasicBlock *> &BlockOrders) {
   // Collect a set of locations from predecessor where its live-out value can
   // be found.
   SmallVector<SmallVector<LocIdx, 4>, 8> Locs;
+  SmallVector<const DbgValueProperties *, 4> Properties;
   unsigned NumLocs = MTracker->getNumLocs();
-  unsigned BackEdgesStart = 0;
 
-  for (auto p : BlockOrders) {
-    // Pick out where backedges start in the list of predecessors. Relies on
-    // BlockOrders being sorted by RPO.
-    if (BBToOrder[p] < BBToOrder[&MBB])
-      ++BackEdgesStart;
+  // No predecessors means no PHIs.
+  if (BlockOrders.empty())
+    return None;
 
-    // For each predecessor, create a new set of locations.
-    Locs.resize(Locs.size() + 1);
+  for (auto p : BlockOrders) {
     unsigned ThisBBNum = p->getNumber();
     auto LiveOutMap = LiveOuts.find(p);
     if (LiveOutMap == LiveOuts.end())
       // This predecessor isn't in scope, it must have no live-in/live-out
       // locations.
-      continue;
+      return None;
 
     auto It = LiveOutMap->second->find(Var);
     if (It == LiveOutMap->second->end())
       // There's no value recorded for this variable in this predecessor,
       // leave an empty set of locations.
-      continue;
+      return None;
 
     const DbgValue &OutVal = It->second;
 
     if (OutVal.Kind == DbgValue::Const || OutVal.Kind == DbgValue::NoVal)
       // Consts and no-values cannot have locations we can join on.
-      continue;
+      return None;
 
-    assert(OutVal.Kind == DbgValue::Proposed || OutVal.Kind == DbgValue::Def);
-    ValueIDNum ValToLookFor = OutVal.ID;
+    Properties.push_back(&OutVal.Properties);
 
-    // Search the live-outs of the predecessor for the specified value.
-    for (unsigned int I = 0; I < NumLocs; ++I) {
-      if (MOutLocs[ThisBBNum][I] == ValToLookFor)
-        Locs.back().push_back(LocIdx(I));
+    // Create new empty vector of locations.
+    Locs.resize(Locs.size() + 1);
+
+    // If the live-in value is a def, find the locations where that value is
+    // 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;
+      // Search the live-outs of the predecessor for the specified value.
+      for (unsigned int I = 0; I < NumLocs; ++I) {
+        if (MOutLocs[ThisBBNum][I] == ValToLookFor)
+          Locs.back().push_back(LocIdx(I));
+      }
+    } else {
+      assert(OutVal.Kind == DbgValue::VPHI);
+      // For VPHIs where we don't know the location, we definitely can't find
+      // a join loc.
+      if (OutVal.BlockNo != MBB.getNumber())
+        return None;
+
+      // Otherwise: this is a VPHI on a backedge feeding back into itself, i.e.
+      // a value that's live-through the whole loop. (It has to be a backedge,
+      // because a block can't dominate itself). We can accept as a PHI location
+      // any location where the other predecessors agree, _and_ the machine
+      // locations feed back into themselves. Therefore, add all self-looping
+      // machine-value PHI locations.
+      for (unsigned int I = 0; I < NumLocs; ++I) {
+        ValueIDNum MPHI(MBB.getNumber(), 0, LocIdx(I));
+        if (MOutLocs[ThisBBNum][I] == MPHI)
+          Locs.back().push_back(LocIdx(I));
+      }
     }
   }
 
-  // If there were no locations at all, return an empty result.
-  if (Locs.empty())
-    return std::tuple<Optional<ValueIDNum>, bool>(None, false);
-
-  // Lambda for seeking a common location within a range of location-sets.
-  using LocsIt = SmallVector<SmallVector<LocIdx, 4>, 8>::iterator;
-  auto SeekLocation =
-      [&Locs](llvm::iterator_range<LocsIt> SearchRange) -> Optional<LocIdx> {
-    // Starting with the first set of locations, take the intersection with
-    // subsequent sets.
-    SmallVector<LocIdx, 4> base = Locs[0];
-    for (auto &S : SearchRange) {
-      SmallVector<LocIdx, 4> new_base;
-      std::set_intersection(base.begin(), base.end(), S.begin(), S.end(),
-                            std::inserter(new_base, new_base.begin()));
-      base = new_base;
-    }
-    if (base.empty())
-      return None;
+  // We should have found locations for all predecessors, or returned.
+  assert(Locs.size() == BlockOrders.size());
 
-    // We now have a set of LocIdxes that contain the right output value in
-    // each of the predecessors. Pick the lowest; if there's a register loc,
-    // that'll be it.
-    return *base.begin();
-  };
+  // Check that all properties are the same. We can't pick a location if they're
+  // not.
+  const DbgValueProperties *Properties0 = Properties[0];
+  for (auto *Prop : Properties)
+    if (*Prop != *Properties0)
+      return None;
 
-  // Search for a common location for all predecessors. If we can't, then fall
-  // back to only finding a common location between non-backedge predecessors.
-  bool ValidForAllLocs = true;
-  auto TheLoc = SeekLocation(Locs);
-  if (!TheLoc) {
-    ValidForAllLocs = false;
-    TheLoc =
-        SeekLocation(make_range(Locs.begin(), Locs.begin() + BackEdgesStart));
-  }
+  // Starting with the first set of locations, take the intersection with
+  // subsequent sets.
+  SmallVector<LocIdx, 4> CandidateLocs = Locs[0];
+  for (unsigned int I = 1; I < Locs.size(); ++I) {
+    auto &LocVec = Locs[I];
+    SmallVector<LocIdx, 4> NewCandidates;
+    std::set_intersection(CandidateLocs.begin(), CandidateLocs.end(),
+                          LocVec.begin(), LocVec.end(), std::inserter(NewCandidates, NewCandidates.begin()));
+    CandidateLocs = NewCandidates;
+  }
+  if (CandidateLocs.empty())
+    return None;
 
-  if (!TheLoc)
-    return std::tuple<Optional<ValueIDNum>, bool>(None, false);
+  // We now have a set of LocIdxes that contain the right output value in
+  // each of the predecessors. Pick the lowest; if there's a register loc,
+  // that'll be it.
+  LocIdx L = *CandidateLocs.begin();
 
   // Return a PHI-value-number for the found location.
-  LocIdx L = *TheLoc;
   ValueIDNum PHIVal = {(unsigned)MBB.getNumber(), 0, L};
-  return std::tuple<Optional<ValueIDNum>, bool>(PHIVal, ValidForAllLocs);
+  return PHIVal;
 }
 
-std::tuple<bool, bool> InstrRefBasedLDV::vlocJoin(
+bool InstrRefBasedLDV::vlocJoin(
     MachineBasicBlock &MBB, LiveIdxT &VLOCOutLocs, LiveIdxT &VLOCInLocs,
-    SmallPtrSet<const MachineBasicBlock *, 16> *VLOCVisited, unsigned BBNum,
-    const SmallSet<DebugVariable, 4> &AllVars, ValueIDNum **MOutLocs,
-    ValueIDNum **MInLocs,
+    const SmallSet<DebugVariable, 4> &AllVars,
     SmallPtrSet<const MachineBasicBlock *, 8> &InScopeBlocks,
     SmallPtrSet<const MachineBasicBlock *, 8> &BlocksToExplore,
     DenseMap<DebugVariable, DbgValue> &InLocsT) {
-  bool DowngradeOccurred = false;
-
   // To emulate VarLocBasedImpl, process this block if it's not in scope but
   // _does_ assign a variable value. No live-ins for this scope are transferred
   // in though, so we can return immediately.
-  if (InScopeBlocks.count(&MBB) == 0 && !ArtificialBlocks.count(&MBB)) {
-    if (VLOCVisited)
-      return std::tuple<bool, bool>(true, false);
-    return std::tuple<bool, bool>(false, false);
-  }
+  if (InScopeBlocks.count(&MBB) == 0 && !ArtificialBlocks.count(&MBB))
+    return false;
 
   LLVM_DEBUG(dbgs() << "join MBB: " << MBB.getNumber() << "\n");
   bool Changed = false;
 
-  // Find any live-ins computed in a prior iteration.
+  // Pick out the live-ins from prior iterations.
   auto ILSIt = VLOCInLocs.find(&MBB);
   assert(ILSIt != VLOCInLocs.end());
   auto &ILS = *ILSIt->second;
@@ -2241,29 +2153,26 @@ std::tuple<bool, bool> InstrRefBasedLDV::vlocJoin(
 
   unsigned CurBlockRPONum = BBToOrder[&MBB];
 
-  // Force a re-visit to loop heads in the first dataflow iteration.
-  // FIXME: if we could "propose" Const values this wouldn't be needed,
-  // because they'd need to be confirmed before being emitted.
-  if (!BlockOrders.empty() &&
-      BBToOrder[BlockOrders[BlockOrders.size() - 1]] >= CurBlockRPONum &&
-      VLOCVisited)
-    DowngradeOccurred = true;
-
+  // We re-construct the live-in map each time we join. For each variable, call
+  // one of these "confirm" utilities, according to which flavour of variable
+  // value it is.
   auto ConfirmValue = [&InLocsT](const DebugVariable &DV, DbgValue VR) {
     auto Result = InLocsT.insert(std::make_pair(DV, VR));
     (void)Result;
     assert(Result.second);
   };
 
-  auto ConfirmNoVal = [&ConfirmValue, &MBB](const DebugVariable &Var, const DbgValueProperties &Properties) {
-    DbgValue NoLocPHIVal(MBB.getNumber(), Properties, DbgValue::NoVal);
-
+  auto ConfirmVPHI = [&ConfirmValue,
+                      &MBB](const DebugVariable &Var,
+                            const DbgValueProperties &Properties) {
+    DbgValue NoLocPHIVal(MBB.getNumber(), Properties, DbgValue::VPHI);
     ConfirmValue(Var, NoLocPHIVal);
   };
 
   // Attempt to join the values for each variable.
   for (auto &Var : AllVars) {
-    // Collect all the DbgValues for this variable.
+    // Collect all the incoming DbgValues for this variable, from predecessor
+    // live-out values.
     SmallVector<InValueT, 8> Values;
     bool Bail = false;
     unsigned BackEdgesStart = 0;
@@ -2275,11 +2184,6 @@ std::tuple<bool, bool> InstrRefBasedLDV::vlocJoin(
         break;
       }
 
-      // Don't attempt to handle unvisited predecessors: they're implicitly
-      // "unknown"s in the lattice.
-      if (VLOCVisited && !VLOCVisited->count(p))
-        continue;
-
       // If the predecessors OutLocs is absent, there's not much we can do.
       auto OL = VLOCOutLocs.find(p);
       if (OL == VLOCOutLocs.end()) {
@@ -2304,163 +2208,74 @@ std::tuple<bool, bool> InstrRefBasedLDV::vlocJoin(
       Values.push_back(std::make_pair(p, &VIt->second));
     }
 
+    // Pick out the live-in value from last time we vlocJoin'd this block.
+    auto LiveInIt = ILS.find(Var);
+    assert(LiveInIt != ILS.end() && "Uninitialized live-in vloc?");
+    const DbgValue &OldLiveInDbgValue = LiveInIt->second;
+
     // If there were no values, or one of the predecessors couldn't have a
     // value, then give up immediately. It's not safe to produce a live-in
-    // value.
-    if (Bail || Values.size() == 0)
+    // value. Leave as whatever it was before.
+    if (Bail || Values.size() == 0) {
+      ConfirmValue(Var, OldLiveInDbgValue);
       continue;
-
-    // Enumeration identifying the current state of the predecessors values.
-    enum {
-      Unset = 0,
-      Agreed,       // All preds agree on the variable value.
-      PropDisagree, // All preds agree, but the value kind is Proposed in some.
-      BEDisagree,   // Only back-edges disagree on variable value.
-      PHINeeded,    // Non-back-edge predecessors have conflicing values.
-      NoSolution    // Conflicting Value metadata makes solution impossible.
-    } OurState = Unset;
+    }
 
     // All (non-entry) blocks have at least one non-backedge predecessor.
     // Pick the variable value from the first of these, to compare against
     // all others.
     const DbgValue &FirstVal = *Values[0].second;
-    const ValueIDNum &FirstID = FirstVal.ID;
 
-    // Scan for variable values that can't be resolved: if they have 
diff erent
-    // DIExpressions, 
diff erent indirectness, or are mixed constants /
+    // If the old live-in value is not a PHI then either a) no PHI is needed
+    // here, or b) we eliminated the PHI that was here. If so, we can just
+    // propagate in the first parents incoming value.
+    if (OldLiveInDbgValue.Kind != DbgValue::VPHI ||
+        OldLiveInDbgValue.BlockNo != MBB.getNumber()) {
+      ConfirmValue(Var, FirstVal);
+      continue;
+    }
+
+    // Scan for variable values that can never be resolved: if they have
+    // 
diff erent DIExpressions, 
diff erent indirectness, or are mixed constants /
     // non-constants.
+    bool AlwaysIncompatible = false;
     for (auto &V : Values) {
       if (V.second->Properties != FirstVal.Properties)
-        OurState = NoSolution;
+        AlwaysIncompatible = true;
+      if (V.second->Kind == DbgValue::NoVal)
+        AlwaysIncompatible = true;
       if (V.second->Kind == DbgValue::Const && FirstVal.Kind != DbgValue::Const)
-        OurState = NoSolution;
+        AlwaysIncompatible = true;
     }
 
-    // Flags diagnosing _how_ the values disagree.
-    bool NonBackEdgeDisagree = false;
-    bool DisagreeOnPHINess = false;
-    bool IDDisagree = false;
-    bool Disagree = false;
-    if (OurState == Unset) {
-      for (auto &V : Values) {
-        if (*V.second == FirstVal)
-          continue; // No disagreement.
-
-        Disagree = true;
-
-        // Flag whether the value number actually diagrees.
-        if (V.second->ID != FirstID)
-          IDDisagree = true;
-
-        // Distinguish whether disagreement happens in backedges or not.
-        // Relies on Values (and BlockOrders) being sorted by RPO.
-        unsigned ThisBBRPONum = BBToOrder[V.first];
-        if (ThisBBRPONum < CurBlockRPONum)
-          NonBackEdgeDisagree = true;
-
-        // Is there a 
diff erence in whether the value is definite or only
-        // proposed?
-        if (V.second->Kind != FirstVal.Kind &&
-            (V.second->Kind == DbgValue::Proposed ||
-             V.second->Kind == DbgValue::Def) &&
-            (FirstVal.Kind == DbgValue::Proposed ||
-             FirstVal.Kind == DbgValue::Def))
-          DisagreeOnPHINess = true;
-      }
-
-      // Collect those flags together and determine an overall state for
-      // what extend the predecessors agree on a live-in value.
-      if (!Disagree)
-        OurState = Agreed;
-      else if (!IDDisagree && DisagreeOnPHINess)
-        OurState = PropDisagree;
-      else if (!NonBackEdgeDisagree)
-        OurState = BEDisagree;
-      else
-        OurState = PHINeeded;
+    if (AlwaysIncompatible) {
+      // Leave this as a VPHI.
+      ConfirmVPHI(Var, OldLiveInDbgValue.Properties);
+      continue;
     }
 
-    // An extra indicator: if we only disagree on whether the value is a
-    // Def, or proposed, then also flag whether that disagreement happens
-    // in backedges only.
-    bool PropOnlyInBEs = Disagree && !IDDisagree && DisagreeOnPHINess &&
-                         !NonBackEdgeDisagree && FirstVal.Kind == DbgValue::Def;
-
-    const auto &Properties = FirstVal.Properties;
-
-    auto OldLiveInIt = ILS.find(Var);
-    const DbgValue *OldLiveInLocation =
-        (OldLiveInIt != ILS.end()) ? &OldLiveInIt->second : nullptr;
-
-    bool OverRide = false;
-    if (OurState == BEDisagree && OldLiveInLocation) {
-      // Only backedges disagree: we can consider downgrading. If there was a
-      // previous live-in value, use it to work out whether the current
-      // incoming value represents a lattice downgrade or not.
-      OverRide =
-          vlocDowngradeLattice(MBB, *OldLiveInLocation, Values, CurBlockRPONum);
+    // Try to eliminate this PHI. Do the incoming values all agree?
+    bool Disagree = false;
+    for (auto &V : Values) {
+      if (*V.second == FirstVal)
+        continue; // No disagreement.
+
+      // Eliminate if a backedge feeds a VPHI back into itself.
+      if (V.second->Kind == DbgValue::VPHI &&
+          V.second->BlockNo == MBB.getNumber() &&
+          // Is this a backedge?
+          std::distance(Values.begin(), &V) >= BackEdgesStart)
+        continue;
+
+      Disagree = true;
     }
 
-    // Use the current state of predecessor agreement and other flags to work
-    // out what to do next. Possibilities include:
-    //  * Accept a value all predecessors agree on, or accept one that
-    //    represents a step down the exploration lattice,
-    //  * Use a PHI value number, if one can be found,
-    //  * Propose a PHI value number, and see if it gets confirmed later,
-    //  * Emit a 'NoVal' value, indicating we couldn't resolve anything.
-    if (OurState == Agreed) {
-      // Easiest solution: all predecessors agree on the variable value.
-      ConfirmValue(Var, FirstVal);
-    } else if (OurState == BEDisagree && OverRide) {
-      // Only backedges disagree, and the other predecessors have produced
-      // a new live-in value further down the exploration lattice.
-      DowngradeOccurred = true;
+    // No disagreement -> live-through value.
+    if (!Disagree) {
       ConfirmValue(Var, FirstVal);
-    } else if (OurState == PropDisagree) {
-      // Predecessors agree on value, but some say it's only a proposed value.
-      // Propagate it as proposed: unless it was proposed in this block, in
-      // which case we're able to confirm the value.
-      if (FirstID.getBlock() == (uint64_t)MBB.getNumber() && FirstID.isPHI()) {
-        ConfirmValue(Var, DbgValue(FirstID, Properties, DbgValue::Def));
-      } else if (PropOnlyInBEs) {
-        // If only backedges disagree, a higher (in RPO) block confirmed this
-        // location, and we need to propagate it into this loop.
-        ConfirmValue(Var, DbgValue(FirstID, Properties, DbgValue::Def));
-      } else {
-        // Otherwise; a Def meeting a Proposed is still a Proposed.
-        ConfirmValue(Var, DbgValue(FirstID, Properties, DbgValue::Proposed));
-      }
-    } else if ((OurState == PHINeeded || OurState == BEDisagree)) {
-      // Predecessors disagree and can't be downgraded: this can only be
-      // solved with a PHI. Use pickVPHILoc to go look for one.
-      Optional<ValueIDNum> VPHI;
-      bool AllEdgesVPHI = false;
-      std::tie(VPHI, AllEdgesVPHI) =
-          pickVPHILoc(MBB, Var, VLOCOutLocs, MOutLocs, MInLocs, BlockOrders);
-
-      if (VPHI && AllEdgesVPHI) {
-        // There's a PHI value that's valid for all predecessors -- we can use
-        // it. If any of the non-backedge predecessors have proposed values
-        // though, this PHI is also only proposed, until the predecessors are
-        // confirmed.
-        DbgValue::KindT K = DbgValue::Def;
-        for (unsigned int I = 0; I < BackEdgesStart; ++I)
-          if (Values[I].second->Kind == DbgValue::Proposed)
-            K = DbgValue::Proposed;
-
-        ConfirmValue(Var, DbgValue(*VPHI, Properties, K));
-      } else if (VPHI) {
-        // There's a PHI value, but it's only legal for backedges. Leave this
-        // as a proposed PHI value: it might come back on the backedges,
-        // and allow us to confirm it in the future.
-        DbgValue NoBEValue = DbgValue(*VPHI, Properties, DbgValue::Proposed);
-        ConfirmValue(Var, NoBEValue);
-      } else {
-        ConfirmNoVal(Var, Properties);
-      }
     } else {
-      // Otherwise: we don't know. Emit a "phi but no real loc" phi.
-      ConfirmNoVal(Var, Properties);
+      // Otherwise use a VPHI.
+      ConfirmVPHI(Var, FirstVal.Properties);
     }
   }
 
@@ -2469,16 +2284,15 @@ std::tuple<bool, bool> InstrRefBasedLDV::vlocJoin(
   if (Changed)
     ILS = InLocsT;
 
-  return std::tuple<bool, bool>(Changed, DowngradeOccurred);
+  return Changed;
 }
 
-void InstrRefBasedLDV::vlocDataflow(
-    const LexicalScope *Scope, const DILocation *DILoc,
+void InstrRefBasedLDV::buildVLocValueMap(const DILocation *DILoc,
     const SmallSet<DebugVariable, 4> &VarsWeCareAbout,
     SmallPtrSetImpl<MachineBasicBlock *> &AssignBlocks, LiveInsT &Output,
     ValueIDNum **MOutLocs, ValueIDNum **MInLocs,
     SmallVectorImpl<VLocTracker> &AllTheVLocs) {
-  // This method is much like mlocDataflow: but focuses on a single
+  // This method is much like buildMLocValueMap: but focuses on a single
   // LexicalScope at a time. Pick out a set of blocks and variables that are
   // to have their value assignments solved, then run our dataflow algorithm
   // until a fixedpoint is reached.
@@ -2587,6 +2401,18 @@ void InstrRefBasedLDV::vlocDataflow(
   LiveIns.resize(NumBlocks);
   LiveOuts.resize(NumBlocks);
 
+  // Initialize all values to start as NoVals. This signifies "it's live
+  // through, but we don't know what it is".
+  DbgValueProperties EmptyProperties(EmptyExpr, false);
+  unsigned int BlockIdx = 0;
+  for (auto &VarMap : LiveIns) {
+    for (const DebugVariable &Var : VarsWeCareAbout)
+      VarMap.insert(
+          {Var, DbgValue(BlockIdx, EmptyProperties, DbgValue::NoVal)});
+
+    ++BlockIdx;
+  }
+
   // Produce by-MBB indexes of live-in/live-outs, to ease lookup within
   // vlocJoin.
   LiveIdxT LiveOutIdx, LiveInIdx;
@@ -2597,14 +2423,51 @@ void InstrRefBasedLDV::vlocDataflow(
     LiveInIdx[BlockOrders[I]] = &LiveIns[I];
   }
 
+  // Convert a const set to a non-const set. LexicalScopes
+  // getMachineBasicBlocks returns const MBB pointers, IDF wants mutable ones.
+  // (Neither of them mutate anything).
+  SmallPtrSet<MachineBasicBlock *, 8> MutBlocksToExplore;
+  for (const auto *MBB : BlocksToExplore)
+    MutBlocksToExplore.insert(const_cast<MachineBasicBlock*>(MBB));
+
+  // Place PHIs for variable values, using the LLVM IDF calculator.
+  for (const DebugVariable &Var : VarsWeCareAbout) {
+    // Collect the set of blocks where variables are def'd.
+    SmallPtrSet<MachineBasicBlock *, 32> DefBlocks;
+    for (const MachineBasicBlock *ExpMBB : BlocksToExplore) {
+      auto &TransferFunc = AllTheVLocs[ExpMBB->getNumber()].Vars;
+      if (TransferFunc.find(Var) != TransferFunc.end())
+        DefBlocks.insert(const_cast<MachineBasicBlock *>(ExpMBB));
+    }
+
+    SmallVector<MachineBasicBlock *, 32> PHIBlocks;
+
+    // Request the set of PHIs we should insert for this variable.
+    BlockPHIPlacement(MutBlocksToExplore, DefBlocks, PHIBlocks);
+
+    // Insert PHIs into the per-block live-in tables for this variable.
+    for (MachineBasicBlock *PHIMBB : PHIBlocks) {
+      unsigned BlockNo = PHIMBB->getNumber();
+      auto *BlockLiveIns = LiveInIdx[PHIMBB];
+      auto It = BlockLiveIns->find(Var);
+      assert(It != BlockLiveIns->end() && "Uninitialized live-in?");
+      It->second = DbgValue(BlockNo, EmptyProperties, DbgValue::VPHI);
+    }
+  }
+
   for (auto *MBB : BlockOrders) {
     Worklist.push(BBToOrder[MBB]);
     OnWorklist.insert(MBB);
   }
 
-  // Iterate over all the blocks we selected, propagating variable values.
+  // Iterate over all the blocks we selected, propagating variable values. This
+  // loop does two things:
+  //  * Eliminates un-necessary VPHIs in vlocJoin,
+  //  * Evaluates the blocks transfer function (i.e. variable assignments) and
+  //    stores the result to the blocks live-outs.
+  // Always evaluate the transfer function on the first iteration, and when the
+  // live-ins change thereafter.
   bool FirstTrip = true;
-  SmallPtrSet<const MachineBasicBlock *, 16> VLOCVisited;
   while (!Worklist.empty() || !Pending.empty()) {
     while (!Worklist.empty()) {
       auto *MBB = OrderToBB[Worklist.top()];
@@ -2615,24 +2478,42 @@ void InstrRefBasedLDV::vlocDataflow(
 
       // Join values from predecessors. Updates LiveInIdx, and writes output
       // into JoinedInLocs.
-      bool InLocsChanged, DowngradeOccurred;
-      std::tie(InLocsChanged, DowngradeOccurred) = vlocJoin(
-          *MBB, LiveOutIdx, LiveInIdx, (FirstTrip) ? &VLOCVisited : nullptr,
-          CurBB, VarsWeCareAbout, MOutLocs, MInLocs, InScopeBlocks,
-          BlocksToExplore, JoinedInLocs);
-
-      bool FirstVisit = VLOCVisited.insert(MBB).second;
-
-      // Always explore transfer function if inlocs changed, or if we've not
-      // visited this block before.
-      InLocsChanged |= FirstVisit;
+      bool InLocsChanged;
+      InLocsChanged = vlocJoin(*MBB, LiveOutIdx, LiveInIdx,
+                               VarsWeCareAbout, InScopeBlocks, BlocksToExplore,
+                               JoinedInLocs);
+
+      SmallVector<const MachineBasicBlock *, 8> Preds;
+      for (const auto *Pred : MBB->predecessors())
+        Preds.push_back(Pred);
+
+      // Opportunistically pick a machine-value for any VPHIs starting in this
+      // block. This makes their machine-value available and propagated through
+      // all blocks by the time value propagation finishes. We can't do this any
+      // earlier as it needs to read the block live-outs.
+      for (auto &Var : VarsWeCareAbout) {
+        DbgValue &Val = JoinedInLocs.find(Var)->second;
+        if (Val.Kind != DbgValue::VPHI || Val.BlockNo != (int)CurBB)
+          continue;
 
-      // If a downgrade occurred, book us in for re-examination on the next
-      // iteration.
-      if (DowngradeOccurred && OnPending.insert(MBB).second)
-        Pending.push(BBToOrder[MBB]);
+        // There's a small possibility that on a preceeding path, a VPHI is
+        // eliminated and transitions from VPHI-with-location to
+        // live-through-value. As a result, the selected location of any VPHI
+        // might change, so we need to re-compute it on each iteration.
+        Optional<ValueIDNum> ValueNum = pickVPHILoc(
+                  *MBB, Var, LiveOutIdx, MOutLocs, Preds);
+
+        if (ValueNum) {
+          InLocsChanged |= Val.ID != *ValueNum;
+          Val.ID = *ValueNum;
+          // FIXME: it's stupid to have two 
diff erent live-in maps at this
+          // stage, one for evaluating the transfer func in, one for storage.
+          // Fix this in the future.
+          LiveInIdx[MBB]->find(Var)->second.ID = *ValueNum;
+        }
+      }
 
-      if (!InLocsChanged)
+      if (!InLocsChanged && !FirstTrip)
         continue;
 
       // Do transfer function.
@@ -2642,7 +2523,9 @@ void InstrRefBasedLDV::vlocDataflow(
         if (VarsWeCareAbout.count(Transfer.first)) {
           // Erase on empty transfer (DBG_VALUE $noreg).
           if (Transfer.second.Kind == DbgValue::Undef) {
-            JoinedInLocs.erase(Transfer.first);
+            auto InLocIt = JoinedInLocs.find(Transfer.first);
+            assert(InLocIt != JoinedInLocs.end());
+            InLocIt->second.Kind = DbgValue::NoVal;
           } else {
             // Insert new variable value; or overwrite.
             auto NewValuePair = std::make_pair(Transfer.first, Transfer.second);
@@ -2686,16 +2569,18 @@ void InstrRefBasedLDV::vlocDataflow(
     FirstTrip = false;
   }
 
-  // Dataflow done. Now what? Save live-ins. Ignore any that are still marked
-  // as being variable-PHIs, because those did not have their machine-PHI
-  // value confirmed. Such variable values are places that could have been
-  // PHIs, but are not.
+  // Save live-ins to output vector. Ignore any that are still marked as being
+  // VPHIs with no location -- those are variables that we know the value of,
+  // but are not actually available in the register file.
   for (auto *MBB : BlockOrders) {
     auto &VarMap = *LiveInIdx[MBB];
     for (auto &P : VarMap) {
-      if (P.second.Kind == DbgValue::Proposed ||
-          P.second.Kind == DbgValue::NoVal)
+      if (P.second.Kind == DbgValue::NoVal)
         continue;
+      if (P.second.Kind == DbgValue::VPHI && P.second.ID == ValueIDNum::EmptyValue)
+        continue;
+      if (P.second.Kind == DbgValue::VPHI)
+        P.second.Kind = DbgValue::Def;
       Output[MBB->getNumber()].push_back(P);
     }
   }
@@ -2782,6 +2667,10 @@ void InstrRefBasedLDV::emitLocations(
 
 void InstrRefBasedLDV::initialSetup(MachineFunction &MF) {
   // Build some useful data structures.
+
+  LLVMContext &Context = MF.getFunction().getContext();
+  EmptyExpr = DIExpression::get(Context, {});
+
   auto hasNonArtificialLocation = [](const MachineInstr &MI) -> bool {
     if (const DebugLoc &DL = MI.getDebugLoc())
       return DL.getLine() != 0;
@@ -2967,7 +2856,7 @@ bool InstrRefBasedLDV::ExtendRanges(MachineFunction &MF,
     // the future. For each scope, solve the variable value problem, producing
     // a map of variables to values in SavedLiveIns.
     for (auto &P : ScopeToVars) {
-      vlocDataflow(P.first, ScopeToDILocation[P.first], P.second,
+      buildVLocValueMap(ScopeToDILocation[P.first], P.second,
                    ScopeToBlocks[P.first], SavedLiveIns, MOutLocs, MInLocs,
                    vlocs);
     }

diff  --git a/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h b/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h
index e85d4d05f810c..cc0a1d7d65aea 100644
--- a/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h
+++ b/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h
@@ -25,7 +25,6 @@
 
 #include "LiveDebugValues.h"
 
-class VLocTracker;
 class TransferTracker;
 
 // Forward dec of unit test class, so that we can peer into the LDV object.
@@ -191,46 +190,50 @@ class DbgValueProperties {
 /// (DebugVariable specific) dataflow analysis.
 class DbgValue {
 public:
-  union {
-    /// If Kind is Def, the value number that this value is based on.
-    ValueIDNum ID;
-    /// If Kind is Const, the MachineOperand defining this value.
-    MachineOperand MO;
-    /// For a NoVal DbgValue, which block it was generated in.
-    unsigned BlockNo;
-  };
+  /// 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;
+
   /// Qualifiers for the ValueIDNum above.
   DbgValueProperties Properties;
 
   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.
-    Proposed, // This is a tentative PHI value, which may be confirmed or
-              // invalidated later.
-    NoVal     // Empty DbgValue, generated during dataflow. BlockNo stores
-              // which block this was generated in.
+    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.
+    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,
+           // before dominating blocks values are propagated in.
   } KindT;
   /// 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), Properties(Prop), Kind(Kind) {
-    assert(Kind == Def || Kind == Proposed);
+      : ID(Val), MO(None), BlockNo(0), Properties(Prop), Kind(Kind) {
+    assert(Kind == Def);
   }
 
   DbgValue(unsigned BlockNo, const DbgValueProperties &Prop, KindT Kind)
-      : BlockNo(BlockNo), Properties(Prop), Kind(Kind) {
-    assert(Kind == NoVal);
+      : ID(ValueIDNum::EmptyValue), MO(None), BlockNo(BlockNo),
+        Properties(Prop), Kind(Kind) {
+    assert(Kind == NoVal || Kind == VPHI);
   }
 
   DbgValue(const MachineOperand &MO, const DbgValueProperties &Prop, KindT Kind)
-      : MO(MO), Properties(Prop), Kind(Kind) {
+      : ID(ValueIDNum::EmptyValue), MO(MO), BlockNo(0), Properties(Prop),
+        Kind(Kind) {
     assert(Kind == Const);
   }
 
   DbgValue(const DbgValueProperties &Prop, KindT Kind)
-      : Properties(Prop), Kind(Kind) {
+    : ID(ValueIDNum::EmptyValue), MO(None), BlockNo(0), Properties(Prop),
+      Kind(Kind) {
     assert(Kind == Undef &&
            "Empty DbgValue constructor must pass in Undef kind");
   }
@@ -242,14 +245,16 @@ class DbgValue {
   bool operator==(const DbgValue &Other) const {
     if (std::tie(Kind, Properties) != std::tie(Other.Kind, Other.Properties))
       return false;
-    else if (Kind == Proposed && ID != Other.ID)
-      return false;
     else if (Kind == Def && ID != Other.ID)
       return false;
     else if (Kind == NoVal && BlockNo != Other.BlockNo)
       return false;
     else if (Kind == Const)
-      return MO.isIdenticalTo(Other.MO);
+      return MO->isIdenticalTo(*Other.MO);
+    else if (Kind == VPHI && BlockNo != Other.BlockNo)
+      return false;
+    else if (Kind == VPHI && ID != Other.ID)
+      return false;
 
     return true;
   }
@@ -553,6 +558,58 @@ class MLocTracker {
                               const DbgValueProperties &Properties);
 };
 
+/// Collection of DBG_VALUEs observed when traversing a block. Records each
+/// variable and the value the DBG_VALUE refers to. Requires the machine value
+/// location dataflow algorithm to have run already, so that values can be
+/// identified.
+class VLocTracker {
+public:
+  /// Map DebugVariable to the latest Value it's defined to have.
+  /// Needs to be a MapVector because we determine order-in-the-input-MIR from
+  /// the order in this container.
+  /// We only retain the last DbgValue in each block for each variable, to
+  /// determine the blocks live-out variable value. The Vars container forms the
+  /// transfer function for this block, as part of the dataflow analysis. The
+  /// movement of values between locations inside of a block is handled at a
+  /// much later stage, in the TransferTracker class.
+  MapVector<DebugVariable, DbgValue> Vars;
+  DenseMap<DebugVariable, const DILocation *> Scopes;
+  MachineBasicBlock *MBB;
+
+public:
+  VLocTracker() {}
+
+  void defVar(const MachineInstr &MI, const DbgValueProperties &Properties,
+              Optional<ValueIDNum> ID) {
+    assert(MI.isDebugValue() || MI.isDebugRef());
+    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();
+  }
+
+  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);
+
+    // 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();
+  }
+};
+
 /// Types for recording sets of variable fragments that overlap. For a given
 /// local variable, we record all other fragments of that variable that could
 /// overlap it, to reduce search time.
@@ -563,7 +620,7 @@ using OverlapMap =
 
 // XXX XXX docs
 class InstrRefBasedLDV : public LDVImpl {
-private:
+public:
   friend class ::InstrRefLDVTest;
 
   using FragmentInfo = DIExpression::FragmentInfo;
@@ -592,6 +649,7 @@ class InstrRefBasedLDV : public LDVImpl {
   /// Used as the result type for the variable value dataflow problem.
   using LiveInsT = SmallVector<SmallVector<VarAndLoc, 8>, 8>;
 
+private:
   MachineDominatorTree *DomTree;
   const TargetRegisterInfo *TRI;
   const TargetInstrInfo *TII;
@@ -601,6 +659,10 @@ class InstrRefBasedLDV : public LDVImpl {
   LexicalScopes LS;
   TargetPassConfig *TPC;
 
+  // An empty DIExpression. Used default / placeholder DbgValueProperties
+  // objects, as we can't have null expressions.
+  const DIExpression *EmptyExpr;
+
   /// Object to track machine locations as we step through a block. Could
   /// probably be a field rather than a pointer, as it's always used.
   MLocTracker *MTracker;
@@ -627,7 +689,7 @@ class InstrRefBasedLDV : public LDVImpl {
 
   // Mapping of blocks to and from their RPOT order.
   DenseMap<unsigned int, MachineBasicBlock *> OrderToBB;
-  DenseMap<MachineBasicBlock *, unsigned int> BBToOrder;
+  DenseMap<const MachineBasicBlock *, unsigned int> BBToOrder;
   DenseMap<unsigned, unsigned> BBNumToRPO;
 
   /// Pair of MachineInstr, and its 1-based offset into the containing block.
@@ -773,69 +835,48 @@ class InstrRefBasedLDV : public LDVImpl {
                 ValueIDNum **OutLocs, ValueIDNum *InLocs);
 
   /// Solve the variable value dataflow problem, for a single lexical scope.
-  /// Uses the algorithm from the file comment to resolve control flow joins,
-  /// although there are extra hacks, see vlocJoin. Reads the
-  /// locations of values from the \p MInLocs and \p MOutLocs arrays (see
-  /// buildMLocValueMap) and reads the variable values transfer function from
-  /// \p AllTheVlocs. Live-in and Live-out variable values are stored locally,
-  /// with the live-ins permanently stored to \p Output once the fixedpoint is
-  /// reached.
+  /// Uses the algorithm from the file comment to resolve control flow joins
+  /// using PHI placement and value propagation. Reads the locations of machine
+  /// values from the \p MInLocs and \p MOutLocs arrays (see buildMLocValueMap)
+  /// and reads the variable values transfer function from \p AllTheVlocs.
+  /// Live-in and Live-out variable values are stored locally, with the live-ins
+  /// permanently stored to \p Output once a fixedpoint is reached.
   /// \p VarsWeCareAbout contains a collection of the variables in \p Scope
   /// that we should be tracking.
-  /// \p AssignBlocks contains the set of blocks that aren't in \p Scope, but
-  /// which do contain DBG_VALUEs, which VarLocBasedImpl tracks locations
-  /// through.
-  void vlocDataflow(const LexicalScope *Scope, const DILocation *DILoc,
+  /// \p AssignBlocks contains the set of blocks that aren't in \p DILoc's
+  /// scope, but which do contain DBG_VALUEs, which VarLocBasedImpl tracks
+  /// locations through.
+  void buildVLocValueMap(const DILocation *DILoc,
                     const SmallSet<DebugVariable, 4> &VarsWeCareAbout,
                     SmallPtrSetImpl<MachineBasicBlock *> &AssignBlocks,
                     LiveInsT &Output, ValueIDNum **MOutLocs,
                     ValueIDNum **MInLocs,
                     SmallVectorImpl<VLocTracker> &AllTheVLocs);
 
-  /// Compute the live-ins to a block, considering control flow merges according
-  /// to the method in the file comment. Live out and live in variable values
-  /// are stored in \p VLOCOutLocs and \p VLOCInLocs. The live-ins for \p MBB
-  /// are computed and stored into \p VLOCInLocs. \returns true if the live-ins
-  /// are modified.
-  /// \p InLocsT Output argument, storage for calculated live-ins.
-  /// \returns two bools -- the first indicates whether a change
-  /// was made, the second whether a lattice downgrade occurred. If the latter
-  /// is true, revisiting this block is necessary.
-  std::tuple<bool, bool>
-  vlocJoin(MachineBasicBlock &MBB, LiveIdxT &VLOCOutLocs, LiveIdxT &VLOCInLocs,
-           SmallPtrSet<const MachineBasicBlock *, 16> *VLOCVisited,
-           unsigned BBNum, const SmallSet<DebugVariable, 4> &AllVars,
-           ValueIDNum **MOutLocs, ValueIDNum **MInLocs,
-           SmallPtrSet<const MachineBasicBlock *, 8> &InScopeBlocks,
-           SmallPtrSet<const MachineBasicBlock *, 8> &BlocksToExplore,
-           DenseMap<DebugVariable, DbgValue> &InLocsT);
-
-  /// Continue exploration of the variable-value lattice, as explained in the
-  /// file-level comment. \p OldLiveInLocation contains the current
-  /// exploration position, from which we need to descend further. \p Values
-  /// contains the set of live-in values, \p CurBlockRPONum the RPO number of
-  /// the current block, and \p CandidateLocations a set of locations that
-  /// should be considered as PHI locations, if we reach the bottom of the
-  /// lattice. \returns true if we should downgrade; the value is the agreeing
-  /// value number in a non-backedge predecessor.
-  bool vlocDowngradeLattice(const MachineBasicBlock &MBB,
-                            const DbgValue &OldLiveInLocation,
-                            const SmallVectorImpl<InValueT> &Values,
-                            unsigned CurBlockRPONum);
+  /// Attempt to eliminate un-necessary PHIs on entry to a block. Examines the
+  /// live-in values coming from predecessors live-outs, and replaces any PHIs
+  /// already present in this blocks live-ins with a live-through value if the
+  /// PHI isn't needed. Live out and live in variable values are stored in
+  /// \p VLOCOutLocs and \p VLOCInLocs. The live-ins for \p MBB are computed and
+  /// stored into \p VLOCInLocs.
+  /// \p InLocsT Output argument, where calculated live-in values are also
+  ///          stored.
+  /// \returns true if any live-ins change value, either from value propagation
+  ///          or PHI elimination.
+  bool vlocJoin(MachineBasicBlock &MBB, LiveIdxT &VLOCOutLocs,
+                LiveIdxT &VLOCInLocs,
+                const SmallSet<DebugVariable, 4> &AllVars,
+                SmallPtrSet<const MachineBasicBlock *, 8> &InScopeBlocks,
+                SmallPtrSet<const MachineBasicBlock *, 8> &BlocksToExplore,
+                DenseMap<DebugVariable, DbgValue> &InLocsT);
 
   /// For the given block and live-outs feeding into it, try to find a
-  /// machine location where they all join. If a solution for all predecessors
-  /// can't be found, a location where all non-backedge-predecessors join
-  /// will be returned instead. While this method finds a join location, this
-  /// says nothing as to whether it should be used.
-  /// \returns Pair of value ID if found, and true when the correct value
-  /// is available on all predecessor edges, or false if it's only available
-  /// for non-backedge predecessors.
-  std::tuple<Optional<ValueIDNum>, bool>
-  pickVPHILoc(MachineBasicBlock &MBB, const DebugVariable &Var,
+  /// machine location where all the variable values join together.
+  /// \returns Value ID of a machine PHI if an appropriate one is available.
+  Optional<ValueIDNum>
+  pickVPHILoc(const MachineBasicBlock &MBB, const DebugVariable &Var,
               const LiveIdxT &LiveOuts, ValueIDNum **MOutLocs,
-              ValueIDNum **MInLocs,
-              const SmallVectorImpl<MachineBasicBlock *> &BlockOrders);
+              const SmallVectorImpl<const MachineBasicBlock *> &BlockOrders);
 
   /// Given the solutions to the two dataflow problems, machine value locations
   /// in \p MInLocs and live-in variable values in \p SavedLiveIns, runs the

diff  --git a/llvm/test/DebugInfo/MIR/InstrRef/pick-vphi-in-shifting-loop.mir b/llvm/test/DebugInfo/MIR/InstrRef/pick-vphi-in-shifting-loop.mir
new file mode 100644
index 0000000000000..e1d00597057a1
--- /dev/null
+++ b/llvm/test/DebugInfo/MIR/InstrRef/pick-vphi-in-shifting-loop.mir
@@ -0,0 +1,128 @@
+# RUN: llc %s -o - -run-pass=livedebugvalues \
+# RUN:     -experimental-debug-variable-locations \
+# RUN: | FileCheck %s
+#
+# This test used to cause an infinite loop in InstrRefBasedLDV. Observe block
+# five: on the first entry the desired variable value is in $rdx and $rcx, on
+# both paths to the block. However, the block rotates values between those
+# registers, and feeds a zero-value in too. Ultimately, there is no correct
+# location for the variable in that block.
+# This caused an infinite loop in a previous implementation of LiveDebugValues.
+# Keep this around as a regression test, and check that no location is picked
+# in block 5.
+#
+# CHECK-LABEL: bb.3:
+# CHECK:       DBG_INSTR_REF 7, 0
+# CHECK-NEXT:  DBG_VALUE $rdx
+# CHECK-NEXT:  $rcx = MOV64rr $rdx
+# CHECK-LABEL: bb.4:
+# CHECK:       DBG_VALUE $rcx
+# CHECK-NEXT:  $rdx = MOV64rr killed $rcx
+# CHECK-LABEL: bb.5:
+# CHEKC-NOT:   DBG_VALUE
+--- |
+  target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+  target triple = "x86_64-unknown-linux-gnu"
+  
+  ; Function Attrs: nofree norecurse nosync nounwind readonly uwtable
+  define dso_local void @bees() local_unnamed_addr !dbg !13 {
+  entry:
+    ret void, !dbg !22
+  }
+  
+  ; Function Attrs: nofree nosync nounwind readnone speculatable willreturn
+  declare void @llvm.dbg.value(metadata, metadata, metadata)
+  
+  !llvm.dbg.cu = !{!2}
+  !llvm.module.flags = !{!8, !9, !10, !11}
+  !llvm.ident = !{!12}
+  
+  !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+  !1 = distinct !DIGlobalVariable(name: "xmlNormalizeURIPath_path", scope: !2, file: !3, line: 1, type: !6, isLocal: false, isDefinition: true)
+  !2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang version 10.0 (git at github.com:llvm/llvm-project 9d8de79d09c9560c094d90b010e8315fe2712ec2)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5, splitDebugInlining: false, nameTableKind: None)
+  !3 = !DIFile(filename: "test4.c", directory: "/tmp")
+  !4 = !{}
+  !5 = !{!0}
+  !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64)
+  !7 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char)
+  !8 = !{i32 7, !"Dwarf Version", i32 4}
+  !9 = !{i32 2, !"Debug Info Version", i32 3}
+  !10 = !{i32 1, !"wchar_size", i32 4}
+  !11 = !{i32 7, !"uwtable", i32 1}
+  !12 = !{!"clang version 10.0 (git at github.com:llvm/llvm-project 9d8de79d09c9560c094d90b010e8315fe2712ec2)"}
+  !13 = distinct !DISubprogram(name: "xmlNormalizeURIPath", scope: !3, file: !3, line: 2, type: !14, scopeLine: 2, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !16)
+  !14 = !DISubroutineType(types: !15)
+  !15 = !{null}
+  !16 = !{!17, !18}
+  !17 = !DILocalVariable(name: "cur", scope: !13, file: !3, line: 3, type: !6)
+  !18 = !DILocalVariable(name: "segp", scope: !19, file: !3, line: 5, type: !6)
+  !19 = distinct !DILexicalBlock(scope: !13, file: !3, line: 4, column: 13)
+  !20 = !DILocation(line: 4, column: 3, scope: !13)
+  !21 = !DILocation(line: 0, scope: !13)
+  !22 = !DILocation(line: 0, scope: !19)
+  !23 = !DILocation(line: 6, column: 5, scope: !19)
+  !24 = !DILocation(line: 6, column: 12, scope: !19)
+  !25 = !{!26, !26, i64 0}
+  !26 = !{!"omnipotent char", !27, i64 0}
+  !27 = !{!"Simple C/C++ TBAA"}
+  !28 = !DILocation(line: 6, column: 27, scope: !19)
+  !29 = !DILocation(line: 7, column: 7, scope: !19)
+  !30 = distinct !{!30, !23, !31, !32, !33}
+  !31 = !DILocation(line: 7, column: 9, scope: !19)
+  !32 = !{!"llvm.loop.mustprogress"}
+  !33 = !{!"llvm.loop.unroll.disable"}
+
+...
+---
+name:            bees
+alignment:       16
+tracksRegLiveness: true
+frameInfo:
+  maxAlignment:    1
+  maxCallFrameSize: 0
+machineFunctionInfo: {}
+body:             |
+  bb.0:
+    $rdx = MOV64ri 0, implicit-def $eflags
+    $esi = MOV32ri 0, implicit-def $eflags
+  
+  bb.1:
+    successors: %bb.2(0x03e0f83e), %bb.1(0x7c1f07c2)
+    liveins: $rcx, $rdi, $rdx, $eflags
+  
+    DBG_PHI $rcx, 2
+    DBG_INSTR_REF 2, 0, !18, !DIExpression(), debug-location !22
+    JCC_1 %bb.1, 4, implicit $eflags, debug-location !22
+  
+  bb.2:
+    successors: %bb.4(0x30000000), %bb.3(0x50000000)
+    liveins: $rcx, $rdi, $eflags
+  
+    JCC_1 %bb.4, 4, implicit $eflags, debug-location !22
+  
+  bb.3:
+    successors: %bb.5(0x04000000)
+    liveins: $rcx, $rdi, $eflags
+  
+    $rdx = MOV64ri 0, debug-instr-number 7, debug-location !22
+    DBG_INSTR_REF 7, 0, !18, !DIExpression(), debug-location !22
+    $rcx = MOV64rr $rdx
+    JMP_1 %bb.5, debug-location !22
+  
+  bb.4:
+    liveins: $rcx, $rdi, $eflags
+  
+    $rdx = MOV64rr killed $rcx, debug-location !22
+  
+  bb.5:
+    successors: %bb.5(0x7c000000), %bb.6(0x04000000)
+    liveins: $rdi, $rdx, $eflags
+  
+    $rcx = MOV64rr $rdx, debug-location !22
+    $rdx = MOV64ri 0 ; jmorse -- disabling this makes variable live-through
+    JCC_1 %bb.5, 6, implicit $eflags, debug-location !22
+  
+  bb.6:
+    RETQ debug-location !22
+
+...

diff  --git a/llvm/unittests/CodeGen/InstrRefLDVTest.cpp b/llvm/unittests/CodeGen/InstrRefLDVTest.cpp
index 8a4405e458e59..f085886eceabb 100644
--- a/llvm/unittests/CodeGen/InstrRefLDVTest.cpp
+++ b/llvm/unittests/CodeGen/InstrRefLDVTest.cpp
@@ -43,13 +43,17 @@ class InstrRefLDVTest : public testing::Test {
   DILexicalBlock *OurBlock, *AnotherBlock;
   DISubprogram *ToInlineFunc;
   DILexicalBlock *ToInlineBlock;
+  DILocalVariable *FuncVariable;
+  DIBasicType *LongInt;
+  DIExpression *EmptyExpr;
 
   DebugLoc OutermostLoc, InBlockLoc, NotNestedBlockLoc, InlinedLoc;
 
-  MachineBasicBlock *MBB1, *MBB2, *MBB3, *MBB4, *MBB5;
+  MachineBasicBlock *MBB0, *MBB1, *MBB2, *MBB3, *MBB4;
 
   std::unique_ptr<InstrRefBasedLDV> LDV;
   std::unique_ptr<MLocTracker> MTracker;
+  std::unique_ptr<VLocTracker> VTracker;
 
   InstrRefLDVTest() : Ctx(), Mod("beehives", Ctx) {
   }
@@ -102,6 +106,10 @@ class InstrRefLDVTest : public testing::Test {
     // Make a scope that isn't nested within the others.
     NotNestedBlockLoc = DILocation::get(Ctx, 4, 1, AnotherBlock);
 
+    LongInt = DIB.createBasicType("long", 64, llvm::dwarf::DW_ATE_unsigned);
+    FuncVariable = DIB.createAutoVariable(OurFunc, "lala", OurFile, 1, LongInt);
+    EmptyExpr = DIExpression::get(Ctx, {});
+
     DIB.finalize();
   }
 
@@ -135,7 +143,9 @@ class InstrRefLDVTest : public testing::Test {
     // Setup things like the artifical block map, and BlockNo <=> RPO Order
     // mappings.
     LDV->initialSetup(*MF);
+    LDV->LS.initialize(*MF);
     addMTracker();
+    addVTracker();
     return &*LDV;
   }
 
@@ -149,32 +159,273 @@ class InstrRefLDVTest : public testing::Test {
     LDV->MTracker = &*MTracker;
   }
 
+  void addVTracker() {
+    ASSERT_TRUE(LDV);
+    VTracker = std::make_unique<VLocTracker>();
+    LDV->VTracker = &*VTracker;
+  }
+
   // Some routines for bouncing into LDV,
   void buildMLocValueMap(ValueIDNum **MInLocs, ValueIDNum **MOutLocs,
                          SmallVectorImpl<MLocTransferMap> &MLocTransfer) {
     LDV->buildMLocValueMap(*MF, MInLocs, MOutLocs, MLocTransfer);
   }
 
+  Optional<ValueIDNum>
+  pickVPHILoc(const MachineBasicBlock &MBB, const DebugVariable &Var,
+              const InstrRefBasedLDV::LiveIdxT &LiveOuts, ValueIDNum **MOutLocs,
+              const SmallVectorImpl<const MachineBasicBlock *> &BlockOrders) {
+    return LDV->pickVPHILoc(MBB, Var, LiveOuts, MOutLocs, BlockOrders);
+  }
+
+  bool vlocJoin(MachineBasicBlock &MBB, InstrRefBasedLDV::LiveIdxT &VLOCOutLocs,
+                InstrRefBasedLDV::LiveIdxT &VLOCInLocs,
+                const SmallSet<DebugVariable, 4> &AllVars,
+                SmallPtrSet<const MachineBasicBlock *, 8> &InScopeBlocks,
+                SmallPtrSet<const MachineBasicBlock *, 8> &BlocksToExplore,
+                DenseMap<DebugVariable, DbgValue> &InLocsT) {
+    return LDV->vlocJoin(MBB, VLOCOutLocs, VLOCInLocs, AllVars,
+                         InScopeBlocks, BlocksToExplore, InLocsT);
+  }
+
+  void buildVLocValueMap(const DILocation *DILoc,
+                    const SmallSet<DebugVariable, 4> &VarsWeCareAbout,
+                    SmallPtrSetImpl<MachineBasicBlock *> &AssignBlocks,
+                    InstrRefBasedLDV::LiveInsT &Output, ValueIDNum **MOutLocs,
+                    ValueIDNum **MInLocs,
+                    SmallVectorImpl<VLocTracker> &AllTheVLocs) {
+    LDV->buildVLocValueMap(DILoc, VarsWeCareAbout, AssignBlocks, Output,
+                           MOutLocs, MInLocs, AllTheVLocs);
+  }
+
   void initValueArray(ValueIDNum **Nums, unsigned Blks, unsigned Locs) {
     for (unsigned int I = 0; I < Blks; ++I)
       for (unsigned int J = 0; J < Locs; ++J)
         Nums[I][J] = ValueIDNum::EmptyValue;
   }
+
+  void setupSingleBlock() {
+    // Add an entry block with nothing but 'ret void' in it.
+    Function &F = const_cast<llvm::Function &>(MF->getFunction());
+    auto *BB0 = BasicBlock::Create(Ctx, "entry", &F);
+    IRBuilder<> IRB(BB0);
+    IRB.CreateRetVoid();
+    MBB0 = MF->CreateMachineBasicBlock(BB0);
+    MF->insert(MF->end(), MBB0);
+    MF->RenumberBlocks();
+
+    setupLDVObj();
+  }
+
+  void setupDiamondBlocks() {
+    //        entry
+    //        /  \
+    //      br1  br2
+    //        \  /
+    //         ret
+    llvm::Function &F = const_cast<llvm::Function &>(MF->getFunction());
+    auto *BB0 = BasicBlock::Create(Ctx, "a", &F);
+    auto *BB1 = BasicBlock::Create(Ctx, "b", &F);
+    auto *BB2 = BasicBlock::Create(Ctx, "c", &F);
+    auto *BB3 = BasicBlock::Create(Ctx, "d", &F);
+    IRBuilder<> IRB0(BB0), IRB1(BB1), IRB2(BB2), IRB3(BB3);
+    IRB0.CreateBr(BB1);
+    IRB1.CreateBr(BB2);
+    IRB2.CreateBr(BB3);
+    IRB3.CreateRetVoid();
+    MBB0 = MF->CreateMachineBasicBlock(BB0);
+    MF->insert(MF->end(), MBB0);
+    MBB1 = MF->CreateMachineBasicBlock(BB1);
+    MF->insert(MF->end(), MBB1);
+    MBB2 = MF->CreateMachineBasicBlock(BB2);
+    MF->insert(MF->end(), MBB2);
+    MBB3 = MF->CreateMachineBasicBlock(BB3);
+    MF->insert(MF->end(), MBB3);
+    MBB0->addSuccessor(MBB1);
+    MBB0->addSuccessor(MBB2);
+    MBB1->addSuccessor(MBB3);
+    MBB2->addSuccessor(MBB3);
+    MF->RenumberBlocks();
+
+    setupLDVObj();
+  }
+
+  void setupSimpleLoop() {
+    //    entry
+    //     |
+    //     |/-----\
+    //    loopblk |
+    //     |\-----/
+    //     |
+    //     ret
+    llvm::Function &F = const_cast<llvm::Function &>(MF->getFunction());
+    auto *BB0 = BasicBlock::Create(Ctx, "entry", &F);
+    auto *BB1 = BasicBlock::Create(Ctx, "loop", &F);
+    auto *BB2 = BasicBlock::Create(Ctx, "ret", &F);
+    IRBuilder<> IRB0(BB0), IRB1(BB1), IRB2(BB2);
+    IRB0.CreateBr(BB1);
+    IRB1.CreateBr(BB2);
+    IRB2.CreateRetVoid();
+    MBB0 = MF->CreateMachineBasicBlock(BB0);
+    MF->insert(MF->end(), MBB0);
+    MBB1 = MF->CreateMachineBasicBlock(BB1);
+    MF->insert(MF->end(), MBB1);
+    MBB2 = MF->CreateMachineBasicBlock(BB2);
+    MF->insert(MF->end(), MBB2);
+    MBB0->addSuccessor(MBB1);
+    MBB1->addSuccessor(MBB2);
+    MBB1->addSuccessor(MBB1);
+    MF->RenumberBlocks();
+
+    setupLDVObj();
+  }
+
+  void setupNestedLoops() {
+    //    entry
+    //     |
+    //    loop1
+    //     ^\
+    //     | \    /-\
+    //     |  loop2  |
+    //     |  /   \-/
+    //     ^ /
+    //     join
+    //     |
+    //     ret
+    llvm::Function &F = const_cast<llvm::Function &>(MF->getFunction());
+    auto *BB0 = BasicBlock::Create(Ctx, "entry", &F);
+    auto *BB1 = BasicBlock::Create(Ctx, "loop1", &F);
+    auto *BB2 = BasicBlock::Create(Ctx, "loop2", &F);
+    auto *BB3 = BasicBlock::Create(Ctx, "join", &F);
+    auto *BB4 = BasicBlock::Create(Ctx, "ret", &F);
+    IRBuilder<> IRB0(BB0), IRB1(BB1), IRB2(BB2), IRB3(BB3), IRB4(BB4);
+    IRB0.CreateBr(BB1);
+    IRB1.CreateBr(BB2);
+    IRB2.CreateBr(BB3);
+    IRB3.CreateBr(BB4);
+    IRB4.CreateRetVoid();
+    MBB0 = MF->CreateMachineBasicBlock(BB0);
+    MF->insert(MF->end(), MBB0);
+    MBB1 = MF->CreateMachineBasicBlock(BB1);
+    MF->insert(MF->end(), MBB1);
+    MBB2 = MF->CreateMachineBasicBlock(BB2);
+    MF->insert(MF->end(), MBB2);
+    MBB3 = MF->CreateMachineBasicBlock(BB3);
+    MF->insert(MF->end(), MBB3);
+    MBB4 = MF->CreateMachineBasicBlock(BB4);
+    MF->insert(MF->end(), MBB4);
+    MBB0->addSuccessor(MBB1);
+    MBB1->addSuccessor(MBB2);
+    MBB2->addSuccessor(MBB2);
+    MBB2->addSuccessor(MBB3);
+    MBB3->addSuccessor(MBB1);
+    MBB3->addSuccessor(MBB4);
+    MF->RenumberBlocks();
+
+    setupLDVObj();
+  }
+
+  void setupNoDominatingLoop() {
+    //           entry
+    //            / \
+    //           /   \
+    //          /     \
+    //        head1   head2
+    //        ^  \   /   ^
+    //        ^   \ /    ^
+    //        \-joinblk -/
+    //             |
+    //            ret
+    llvm::Function &F = const_cast<llvm::Function &>(MF->getFunction());
+    auto *BB0 = BasicBlock::Create(Ctx, "entry", &F);
+    auto *BB1 = BasicBlock::Create(Ctx, "head1", &F);
+    auto *BB2 = BasicBlock::Create(Ctx, "head2", &F);
+    auto *BB3 = BasicBlock::Create(Ctx, "joinblk", &F);
+    auto *BB4 = BasicBlock::Create(Ctx, "ret", &F);
+    IRBuilder<> IRB0(BB0), IRB1(BB1), IRB2(BB2), IRB3(BB3), IRB4(BB4);
+    IRB0.CreateBr(BB1);
+    IRB1.CreateBr(BB2);
+    IRB2.CreateBr(BB3);
+    IRB3.CreateBr(BB4);
+    IRB4.CreateRetVoid();
+    MBB0 = MF->CreateMachineBasicBlock(BB0);
+    MF->insert(MF->end(), MBB0);
+    MBB1 = MF->CreateMachineBasicBlock(BB1);
+    MF->insert(MF->end(), MBB1);
+    MBB2 = MF->CreateMachineBasicBlock(BB2);
+    MF->insert(MF->end(), MBB2);
+    MBB3 = MF->CreateMachineBasicBlock(BB3);
+    MF->insert(MF->end(), MBB3);
+    MBB4 = MF->CreateMachineBasicBlock(BB4);
+    MF->insert(MF->end(), MBB4);
+    MBB0->addSuccessor(MBB1);
+    MBB0->addSuccessor(MBB2);
+    MBB1->addSuccessor(MBB3);
+    MBB2->addSuccessor(MBB3);
+    MBB3->addSuccessor(MBB1);
+    MBB3->addSuccessor(MBB2);
+    MBB3->addSuccessor(MBB4);
+    MF->RenumberBlocks();
+
+    setupLDVObj();
+  }
+
+  void setupBadlyNestedLoops() {
+    //           entry
+    //             |
+    //           loop1 -o
+    //             | ^
+    //             | ^
+    //           loop2 -o
+    //             | ^
+    //             | ^
+    //           loop3 -o
+    //             |
+    //            ret
+    //
+    // NB: the loop blocks self-loop, which is a bit too fiddly to draw on
+    // accurately.
+    llvm::Function &F = const_cast<llvm::Function &>(MF->getFunction());
+    auto *BB0 = BasicBlock::Create(Ctx, "entry", &F);
+    auto *BB1 = BasicBlock::Create(Ctx, "loop1", &F);
+    auto *BB2 = BasicBlock::Create(Ctx, "loop2", &F);
+    auto *BB3 = BasicBlock::Create(Ctx, "loop3", &F);
+    auto *BB4 = BasicBlock::Create(Ctx, "ret", &F);
+    IRBuilder<> IRB0(BB0), IRB1(BB1), IRB2(BB2), IRB3(BB3), IRB4(BB4);
+    IRB0.CreateBr(BB1);
+    IRB1.CreateBr(BB2);
+    IRB2.CreateBr(BB3);
+    IRB3.CreateBr(BB4);
+    IRB4.CreateRetVoid();
+    MBB0 = MF->CreateMachineBasicBlock(BB0);
+    MF->insert(MF->end(), MBB0);
+    MBB1 = MF->CreateMachineBasicBlock(BB1);
+    MF->insert(MF->end(), MBB1);
+    MBB2 = MF->CreateMachineBasicBlock(BB2);
+    MF->insert(MF->end(), MBB2);
+    MBB3 = MF->CreateMachineBasicBlock(BB3);
+    MF->insert(MF->end(), MBB3);
+    MBB4 = MF->CreateMachineBasicBlock(BB4);
+    MF->insert(MF->end(), MBB4);
+    MBB0->addSuccessor(MBB1);
+    MBB1->addSuccessor(MBB1);
+    MBB1->addSuccessor(MBB2);
+    MBB2->addSuccessor(MBB1);
+    MBB2->addSuccessor(MBB2);
+    MBB2->addSuccessor(MBB3);
+    MBB3->addSuccessor(MBB2);
+    MBB3->addSuccessor(MBB3);
+    MBB3->addSuccessor(MBB4);
+    MF->RenumberBlocks();
+
+    setupLDVObj();
+  }
 };
 
 TEST_F(InstrRefLDVTest, MLocSingleBlock) {
   // Test some very simple properties about interpreting the transfer function.
+  setupSingleBlock();
 
-  // Add an entry block with nothing but 'ret void' in it.
-  Function &F = const_cast<llvm::Function &>(MF->getFunction());
-  auto *BB1 = BasicBlock::Create(Ctx, "entry", &F);
-  IRBuilder<> IRB(BB1);
-  IRB.CreateRetVoid();
-  MBB1 = MF->CreateMachineBasicBlock(BB1);
-  MF->insert(MF->end(), MBB1);
-  MF->RenumberBlocks();
-
-  setupLDVObj();
   // We should start with a single location, the stack pointer.
   ASSERT_TRUE(MTracker->getNumLocs() == 1);
   LocIdx RspLoc(0);
@@ -235,37 +486,12 @@ TEST_F(InstrRefLDVTest, MLocSingleBlock) {
 
 TEST_F(InstrRefLDVTest, MLocDiamondBlocks) {
   // Test that information flows from the entry block to two successors.
-
   //        entry
   //        /  \
   //      br1  br2
   //        \  /
   //         ret
-  llvm::Function &F = const_cast<llvm::Function &>(MF->getFunction());
-  auto *BB1 = BasicBlock::Create(Ctx, "a", &F);
-  auto *BB2 = BasicBlock::Create(Ctx, "b", &F);
-  auto *BB3 = BasicBlock::Create(Ctx, "c", &F);
-  auto *BB4 = BasicBlock::Create(Ctx, "d", &F);
-  IRBuilder<> IRB1(BB1), IRB2(BB2), IRB3(BB3), IRB4(BB4);
-  IRB1.CreateBr(BB2);
-  IRB2.CreateBr(BB3);
-  IRB3.CreateBr(BB4);
-  IRB4.CreateRetVoid();
-  MBB1 = MF->CreateMachineBasicBlock(BB1);
-  MF->insert(MF->end(), MBB1);
-  MBB2 = MF->CreateMachineBasicBlock(BB2);
-  MF->insert(MF->end(), MBB2);
-  MBB3 = MF->CreateMachineBasicBlock(BB3);
-  MF->insert(MF->end(), MBB3);
-  MBB4 = MF->CreateMachineBasicBlock(BB4);
-  MF->insert(MF->end(), MBB4);
-  MBB1->addSuccessor(MBB2);
-  MBB1->addSuccessor(MBB3);
-  MBB2->addSuccessor(MBB4);
-  MBB3->addSuccessor(MBB4);
-  MF->RenumberBlocks();
-
-  setupLDVObj();
+  setupDiamondBlocks();
 
   ASSERT_TRUE(MTracker->getNumLocs() == 1);
   LocIdx RspLoc(0);
@@ -281,13 +507,15 @@ TEST_F(InstrRefLDVTest, MLocDiamondBlocks) {
   TransferFunc.resize(4);
 
   // Name some values.
-  ValueIDNum LiveInRsp(0, 0, RspLoc);
-  ValueIDNum RspDefInBlk0(0, 1, RspLoc);
-  ValueIDNum RspDefInBlk1(1, 1, RspLoc);
-  ValueIDNum RspDefInBlk2(2, 1, RspLoc);
-  ValueIDNum RspPHIInBlk3(3, 0, RspLoc);
-  ValueIDNum RaxLiveInBlk1(1, 0, RaxLoc);
-  ValueIDNum RaxLiveInBlk2(2, 0, RaxLoc);
+  unsigned EntryBlk = 0, BrBlk1 = 1, BrBlk2 = 2, RetBlk = 3;
+
+  ValueIDNum LiveInRsp(EntryBlk, 0, RspLoc);
+  ValueIDNum RspDefInBlk0(EntryBlk, 1, RspLoc);
+  ValueIDNum RspDefInBlk1(BrBlk1, 1, RspLoc);
+  ValueIDNum RspDefInBlk2(BrBlk2, 1, RspLoc);
+  ValueIDNum RspPHIInBlk3(RetBlk, 0, RspLoc);
+  ValueIDNum RaxLiveInBlk1(BrBlk1, 0, RaxLoc);
+  ValueIDNum RaxLiveInBlk2(BrBlk2, 0, RaxLoc);
 
   // With no transfer function, the live-in values to the entry block should
   // propagate to all live-outs and the live-ins to the two successor blocks.
@@ -394,26 +622,7 @@ TEST_F(InstrRefLDVTest, MLocSimpleLoop) {
   //     |\-----/
   //     |
   //     ret
-  llvm::Function &F = const_cast<llvm::Function &>(MF->getFunction());
-  auto *BB1 = BasicBlock::Create(Ctx, "entry", &F);
-  auto *BB2 = BasicBlock::Create(Ctx, "loop", &F);
-  auto *BB3 = BasicBlock::Create(Ctx, "ret", &F);
-  IRBuilder<> IRB1(BB1), IRB2(BB2), IRB3(BB3);
-  IRB1.CreateBr(BB2);
-  IRB2.CreateBr(BB3);
-  IRB3.CreateRetVoid();
-  MBB1 = MF->CreateMachineBasicBlock(BB1);
-  MF->insert(MF->end(), MBB1);
-  MBB2 = MF->CreateMachineBasicBlock(BB2);
-  MF->insert(MF->end(), MBB2);
-  MBB3 = MF->CreateMachineBasicBlock(BB3);
-  MF->insert(MF->end(), MBB3);
-  MBB1->addSuccessor(MBB2);
-  MBB2->addSuccessor(MBB3);
-  MBB2->addSuccessor(MBB2);
-  MF->RenumberBlocks();
-
-  setupLDVObj();
+  setupSimpleLoop();
 
   ASSERT_TRUE(MTracker->getNumLocs() == 1);
   LocIdx RspLoc(0);
@@ -428,12 +637,14 @@ TEST_F(InstrRefLDVTest, MLocSimpleLoop) {
   TransferFunc.resize(3);
 
   // Name some values.
-  ValueIDNum LiveInRsp(0, 0, RspLoc);
-  ValueIDNum RspPHIInBlk1(1, 0, RspLoc);
-  ValueIDNum RspDefInBlk1(1, 1, RspLoc);
-  ValueIDNum LiveInRax(0, 0, RaxLoc);
-  ValueIDNum RaxPHIInBlk1(1, 0, RaxLoc);
-  ValueIDNum RaxPHIInBlk2(2, 0, RaxLoc);
+  unsigned EntryBlk = 0, LoopBlk = 1, RetBlk = 2;
+
+  ValueIDNum LiveInRsp(EntryBlk, 0, RspLoc);
+  ValueIDNum RspPHIInBlk1(LoopBlk, 0, RspLoc);
+  ValueIDNum RspDefInBlk1(LoopBlk, 1, RspLoc);
+  ValueIDNum LiveInRax(EntryBlk, 0, RaxLoc);
+  ValueIDNum RaxPHIInBlk1(LoopBlk, 0, RaxLoc);
+  ValueIDNum RaxPHIInBlk2(RetBlk, 0, RaxLoc);
 
   // Begin test with all locations being live-through.
   initValueArray(InLocsPtr, 3, 2);
@@ -511,37 +722,7 @@ TEST_F(InstrRefLDVTest, MLocNestedLoop) {
   //     join
   //     |
   //     ret
-  llvm::Function &F = const_cast<llvm::Function &>(MF->getFunction());
-  auto *BB1 = BasicBlock::Create(Ctx, "entry", &F);
-  auto *BB2 = BasicBlock::Create(Ctx, "loop1", &F);
-  auto *BB3 = BasicBlock::Create(Ctx, "loop2", &F);
-  auto *BB4 = BasicBlock::Create(Ctx, "join", &F);
-  auto *BB5 = BasicBlock::Create(Ctx, "ret", &F);
-  IRBuilder<> IRB1(BB1), IRB2(BB2), IRB3(BB3), IRB4(BB4), IRB5(BB5);
-  IRB1.CreateBr(BB2);
-  IRB2.CreateBr(BB3);
-  IRB3.CreateBr(BB4);
-  IRB4.CreateBr(BB5);
-  IRB5.CreateRetVoid();
-  MBB1 = MF->CreateMachineBasicBlock(BB1);
-  MF->insert(MF->end(), MBB1);
-  MBB2 = MF->CreateMachineBasicBlock(BB2);
-  MF->insert(MF->end(), MBB2);
-  MBB3 = MF->CreateMachineBasicBlock(BB3);
-  MF->insert(MF->end(), MBB3);
-  MBB4 = MF->CreateMachineBasicBlock(BB4);
-  MF->insert(MF->end(), MBB4);
-  MBB5 = MF->CreateMachineBasicBlock(BB5);
-  MF->insert(MF->end(), MBB5);
-  MBB1->addSuccessor(MBB2);
-  MBB2->addSuccessor(MBB3);
-  MBB3->addSuccessor(MBB3);
-  MBB3->addSuccessor(MBB4);
-  MBB4->addSuccessor(MBB2);
-  MBB4->addSuccessor(MBB5);
-  MF->RenumberBlocks();
-
-  setupLDVObj();
+  setupNestedLoops();
 
   ASSERT_TRUE(MTracker->getNumLocs() == 1);
   LocIdx RspLoc(0);
@@ -557,15 +738,17 @@ TEST_F(InstrRefLDVTest, MLocNestedLoop) {
   SmallVector<MLocTransferMap, 1> TransferFunc;
   TransferFunc.resize(5);
 
-  ValueIDNum LiveInRsp(0, 0, RspLoc);
-  ValueIDNum RspPHIInBlk1(1, 0, RspLoc);
-  ValueIDNum RspDefInBlk1(1, 1, RspLoc);
-  ValueIDNum RspPHIInBlk2(2, 0, RspLoc);
-  ValueIDNum RspDefInBlk2(2, 1, RspLoc);
-  ValueIDNum RspDefInBlk3(3, 1, RspLoc);
-  ValueIDNum LiveInRax(0, 0, RaxLoc);
-  ValueIDNum RaxPHIInBlk1(1, 0, RaxLoc);
-  ValueIDNum RaxPHIInBlk2(2, 0, RaxLoc);
+  unsigned EntryBlk = 0, Loop1Blk = 1, Loop2Blk = 2, JoinBlk = 3;
+
+  ValueIDNum LiveInRsp(EntryBlk, 0, RspLoc);
+  ValueIDNum RspPHIInBlk1(Loop1Blk, 0, RspLoc);
+  ValueIDNum RspDefInBlk1(Loop1Blk, 1, RspLoc);
+  ValueIDNum RspPHIInBlk2(Loop2Blk, 0, RspLoc);
+  ValueIDNum RspDefInBlk2(Loop2Blk, 1, RspLoc);
+  ValueIDNum RspDefInBlk3(JoinBlk, 1, RspLoc);
+  ValueIDNum LiveInRax(EntryBlk, 0, RaxLoc);
+  ValueIDNum RaxPHIInBlk1(Loop1Blk, 0, RaxLoc);
+  ValueIDNum RaxPHIInBlk2(Loop2Blk, 0, RaxLoc);
 
   // Like the other tests: first ensure that if there's nothing in the transfer
   // function, then everything is live-through (check $rsp).
@@ -744,38 +927,7 @@ TEST_F(InstrRefLDVTest, MLocNoDominatingLoop) {
   //        \-joinblk -/
   //             |
   //            ret
-  llvm::Function &F = const_cast<llvm::Function &>(MF->getFunction());
-  auto *BB1 = BasicBlock::Create(Ctx, "entry", &F);
-  auto *BB2 = BasicBlock::Create(Ctx, "head1", &F);
-  auto *BB3 = BasicBlock::Create(Ctx, "head2", &F);
-  auto *BB4 = BasicBlock::Create(Ctx, "joinblk", &F);
-  auto *BB5 = BasicBlock::Create(Ctx, "ret", &F);
-  IRBuilder<> IRB1(BB1), IRB2(BB2), IRB3(BB3), IRB4(BB4), IRB5(BB5);
-  IRB1.CreateBr(BB2);
-  IRB2.CreateBr(BB3);
-  IRB3.CreateBr(BB4);
-  IRB4.CreateBr(BB5);
-  IRB5.CreateRetVoid();
-  MBB1 = MF->CreateMachineBasicBlock(BB1);
-  MF->insert(MF->end(), MBB1);
-  MBB2 = MF->CreateMachineBasicBlock(BB2);
-  MF->insert(MF->end(), MBB2);
-  MBB3 = MF->CreateMachineBasicBlock(BB3);
-  MF->insert(MF->end(), MBB3);
-  MBB4 = MF->CreateMachineBasicBlock(BB4);
-  MF->insert(MF->end(), MBB4);
-  MBB5 = MF->CreateMachineBasicBlock(BB5);
-  MF->insert(MF->end(), MBB5);
-  MBB1->addSuccessor(MBB2);
-  MBB1->addSuccessor(MBB3);
-  MBB2->addSuccessor(MBB4);
-  MBB3->addSuccessor(MBB4);
-  MBB4->addSuccessor(MBB2);
-  MBB4->addSuccessor(MBB3);
-  MBB4->addSuccessor(MBB5);
-  MF->RenumberBlocks();
-
-  setupLDVObj();
+  setupNoDominatingLoop();
 
   ASSERT_TRUE(MTracker->getNumLocs() == 1);
   LocIdx RspLoc(0);
@@ -791,15 +943,17 @@ TEST_F(InstrRefLDVTest, MLocNoDominatingLoop) {
   SmallVector<MLocTransferMap, 1> TransferFunc;
   TransferFunc.resize(5);
 
-  ValueIDNum LiveInRsp(0, 0, RspLoc);
-  ValueIDNum RspPHIInBlk1(1, 0, RspLoc);
-  ValueIDNum RspDefInBlk1(1, 1, RspLoc);
-  ValueIDNum RspPHIInBlk2(2, 0, RspLoc);
-  ValueIDNum RspDefInBlk2(2, 1, RspLoc);
-  ValueIDNum RspPHIInBlk3(3, 0, RspLoc);
-  ValueIDNum RspDefInBlk3(3, 1, RspLoc);
-  ValueIDNum RaxPHIInBlk1(1, 0, RaxLoc);
-  ValueIDNum RaxPHIInBlk2(2, 0, RaxLoc);
+  unsigned EntryBlk = 0, Head1Blk = 1, Head2Blk = 2, JoinBlk = 3;
+
+  ValueIDNum LiveInRsp(EntryBlk, 0, RspLoc);
+  ValueIDNum RspPHIInBlk1(Head1Blk, 0, RspLoc);
+  ValueIDNum RspDefInBlk1(Head1Blk, 1, RspLoc);
+  ValueIDNum RspPHIInBlk2(Head2Blk, 0, RspLoc);
+  ValueIDNum RspDefInBlk2(Head2Blk, 1, RspLoc);
+  ValueIDNum RspPHIInBlk3(JoinBlk, 0, RspLoc);
+  ValueIDNum RspDefInBlk3(JoinBlk, 1, RspLoc);
+  ValueIDNum RaxPHIInBlk1(Head1Blk, 0, RaxLoc);
+  ValueIDNum RaxPHIInBlk2(Head2Blk, 0, RaxLoc);
 
   // As ever, test that everything is live-through if there are no defs.
   initValueArray(InLocsPtr, 5, 2);
@@ -932,43 +1086,7 @@ TEST_F(InstrRefLDVTest, MLocBadlyNestedLoops) {
   //           loop3 -o
   //             |
   //            ret
-  //
-  // NB: the loop blocks self-loop, which is a bit too fiddly to draw on
-  // accurately.
-  llvm::Function &F = const_cast<llvm::Function &>(MF->getFunction());
-  auto *BB1 = BasicBlock::Create(Ctx, "entry", &F);
-  auto *BB2 = BasicBlock::Create(Ctx, "loop1", &F);
-  auto *BB3 = BasicBlock::Create(Ctx, "loop2", &F);
-  auto *BB4 = BasicBlock::Create(Ctx, "loop3", &F);
-  auto *BB5 = BasicBlock::Create(Ctx, "ret", &F);
-  IRBuilder<> IRB1(BB1), IRB2(BB2), IRB3(BB3), IRB4(BB4), IRB5(BB5);
-  IRB1.CreateBr(BB2);
-  IRB2.CreateBr(BB3);
-  IRB3.CreateBr(BB4);
-  IRB4.CreateBr(BB5);
-  IRB5.CreateRetVoid();
-  MBB1 = MF->CreateMachineBasicBlock(BB1);
-  MF->insert(MF->end(), MBB1);
-  MBB2 = MF->CreateMachineBasicBlock(BB2);
-  MF->insert(MF->end(), MBB2);
-  MBB3 = MF->CreateMachineBasicBlock(BB3);
-  MF->insert(MF->end(), MBB3);
-  MBB4 = MF->CreateMachineBasicBlock(BB4);
-  MF->insert(MF->end(), MBB4);
-  MBB5 = MF->CreateMachineBasicBlock(BB5);
-  MF->insert(MF->end(), MBB5);
-  MBB1->addSuccessor(MBB2);
-  MBB2->addSuccessor(MBB2);
-  MBB2->addSuccessor(MBB3);
-  MBB3->addSuccessor(MBB2);
-  MBB3->addSuccessor(MBB3);
-  MBB3->addSuccessor(MBB4);
-  MBB4->addSuccessor(MBB3);
-  MBB4->addSuccessor(MBB4);
-  MBB4->addSuccessor(MBB5);
-  MF->RenumberBlocks();
-
-  setupLDVObj();
+  setupBadlyNestedLoops();
 
   ASSERT_TRUE(MTracker->getNumLocs() == 1);
   LocIdx RspLoc(0);
@@ -984,14 +1102,16 @@ TEST_F(InstrRefLDVTest, MLocBadlyNestedLoops) {
   SmallVector<MLocTransferMap, 1> TransferFunc;
   TransferFunc.resize(5);
 
-  ValueIDNum LiveInRsp(0, 0, RspLoc);
-  ValueIDNum RspPHIInBlk1(1, 0, RspLoc);
-  ValueIDNum RspDefInBlk1(1, 1, RspLoc);
-  ValueIDNum RspPHIInBlk2(2, 0, RspLoc);
-  ValueIDNum RspPHIInBlk3(3, 0, RspLoc);
-  ValueIDNum RspDefInBlk3(3, 1, RspLoc);
-  ValueIDNum LiveInRax(0, 0, RaxLoc);
-  ValueIDNum RaxPHIInBlk3(3, 0, RaxLoc);
+  unsigned EntryBlk = 0, Loop1Blk = 1, Loop2Blk = 2, Loop3Blk = 3;
+
+  ValueIDNum LiveInRsp(EntryBlk, 0, RspLoc);
+  ValueIDNum RspPHIInBlk1(Loop1Blk, 0, RspLoc);
+  ValueIDNum RspDefInBlk1(Loop1Blk, 1, RspLoc);
+  ValueIDNum RspPHIInBlk2(Loop2Blk, 0, RspLoc);
+  ValueIDNum RspPHIInBlk3(Loop3Blk, 0, RspLoc);
+  ValueIDNum RspDefInBlk3(Loop3Blk, 1, RspLoc);
+  ValueIDNum LiveInRax(EntryBlk, 0, RaxLoc);
+  ValueIDNum RaxPHIInBlk3(Loop3Blk, 0, RaxLoc);
 
   // As ever, test that everything is live-through if there are no defs.
   initValueArray(InLocsPtr, 5, 2);
@@ -1095,3 +1215,1582 @@ TEST_F(InstrRefLDVTest, MLocBadlyNestedLoops) {
   EXPECT_EQ(OutLocs[3][1], LiveInRsp);
   EXPECT_EQ(OutLocs[4][1], LiveInRsp);
 }
+
+TEST_F(InstrRefLDVTest, pickVPHILocDiamond) {
+  //        entry
+  //        /  \
+  //      br1  br2
+  //        \  /
+  //         ret
+  setupDiamondBlocks();
+
+  ASSERT_TRUE(MTracker->getNumLocs() == 1);
+  LocIdx RspLoc(0);
+  Register RAX = getRegByName("RAX");
+  LocIdx RaxLoc = MTracker->lookupOrTrackRegister(RAX);
+
+  ValueIDNum OutLocs[4][2];
+  ValueIDNum *OutLocsPtr[4] = {OutLocs[0], OutLocs[1], OutLocs[2], OutLocs[3]};
+
+  initValueArray(OutLocsPtr, 4, 2);
+
+  unsigned EntryBlk = 0, Br2Blk = 2, RetBlk = 3;
+
+  ValueIDNum LiveInRsp(EntryBlk, 0, RspLoc);
+  ValueIDNum LiveInRax(EntryBlk, 0, RaxLoc);
+  ValueIDNum RspPHIInBlk2(Br2Blk, 0, RspLoc);
+  ValueIDNum RspPHIInBlk3(RetBlk, 0, RspLoc);
+
+  DebugVariable Var(FuncVariable, None, nullptr);
+  DbgValueProperties EmptyProps(EmptyExpr, false);
+  SmallVector<DenseMap<DebugVariable, DbgValue>, 32> VLiveOuts;
+  VLiveOuts.resize(4);
+  InstrRefBasedLDV::LiveIdxT VLiveOutIdx;
+  VLiveOutIdx[MBB0] = &VLiveOuts[0];
+  VLiveOutIdx[MBB1] = &VLiveOuts[1];
+  VLiveOutIdx[MBB2] = &VLiveOuts[2];
+  VLiveOutIdx[MBB3] = &VLiveOuts[3];
+
+  SmallVector<const MachineBasicBlock *, 2> Preds;
+  for (const auto *Pred : MBB3->predecessors())
+    Preds.push_back(Pred);
+
+  // Specify the live-outs around the joining block.
+  OutLocs[1][0] = LiveInRsp;
+  OutLocs[2][0] = LiveInRax;
+
+  Optional<ValueIDNum> Result;
+
+  // Simple case: join two distinct values on entry to the block.
+  VLiveOuts[1].insert({Var,  DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  VLiveOuts[2].insert({Var,  DbgValue(LiveInRax, EmptyProps, DbgValue::Def)});
+  Result = pickVPHILoc(*MBB3, Var, VLiveOutIdx, OutLocsPtr, Preds);
+  // Should have picked a PHI in $rsp in block 3.
+  EXPECT_TRUE(Result);
+  if (Result)
+    EXPECT_EQ(*Result, RspPHIInBlk3);
+
+  // If the incoming values are swapped between blocks, we should not
+  // successfully join. The CFG merge would select the right values, but in
+  // the wrong conditions.
+  std::swap(VLiveOuts[1], VLiveOuts[2]);
+  Result = pickVPHILoc(*MBB3, Var, VLiveOutIdx, OutLocsPtr, Preds);
+  EXPECT_FALSE(Result);
+
+  // Swap back,
+  std::swap(VLiveOuts[1], VLiveOuts[2]);
+  // Setting one of these to being a constant should prohibit merging.
+  VLiveOuts[1].find(Var)->second.Kind = DbgValue::Const;
+  VLiveOuts[1].find(Var)->second.MO = MachineOperand::CreateImm(0);
+  Result = pickVPHILoc(*MBB3, Var, VLiveOutIdx, OutLocsPtr, Preds);
+  EXPECT_FALSE(Result);
+
+  // Seeing both to being a constant -> still prohibit, it shouldn't become
+  // a value in the register file anywhere.
+  VLiveOuts[2].find(Var)->second = VLiveOuts[1].find(Var)->second;
+  Result = pickVPHILoc(*MBB3, Var, VLiveOutIdx, OutLocsPtr, Preds);
+  EXPECT_FALSE(Result);
+
+  // NoVals shouldn't join with anything else.
+  VLiveOuts[1].clear();
+  VLiveOuts[2].clear();
+  VLiveOuts[1].insert({Var,  DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  VLiveOuts[2].insert({Var,  DbgValue(2, EmptyProps, DbgValue::NoVal)});
+  Result = pickVPHILoc(*MBB3, Var, VLiveOutIdx, OutLocsPtr, Preds);
+  EXPECT_FALSE(Result);
+
+  // We might merge in another VPHI in such a join. Present pickVPHILoc with
+  // 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[2].clear();
+  VLiveOuts[2].insert({Var,  DbgValue(2, EmptyProps, DbgValue::VPHI)});
+  EXPECT_EQ(VLiveOuts[2].find(Var)->second.ID, ValueIDNum::EmptyValue);
+  Result = pickVPHILoc(*MBB3, Var, VLiveOutIdx, OutLocsPtr, Preds);
+  EXPECT_FALSE(Result);
+
+  // However, if we know the value of the incoming VPHI, we can search for its
+  // location. Use a PHI machine-value for doing this, as VPHIs should always
+  // have PHI values, or they should have been eliminated.
+  OutLocs[2][0] = RspPHIInBlk2;
+  VLiveOuts[2].find(Var)->second.ID = RspPHIInBlk2;
+  Result = pickVPHILoc(*MBB3, Var, VLiveOutIdx, OutLocsPtr, Preds);
+  EXPECT_TRUE(Result);
+  if (Result)
+    EXPECT_EQ(*Result, RspPHIInBlk3);
+
+  // If that value isn't available from that block, don't join.
+  OutLocs[2][0] = LiveInRsp;
+  Result = pickVPHILoc(*MBB3, Var, VLiveOutIdx, OutLocsPtr, Preds);
+  EXPECT_FALSE(Result);
+
+  // Check that we don't pick values when the properties disagree, for example
+  // 
diff erent indirectness or DIExpression.
+  DIExpression *NewExpr = DIExpression::prepend(EmptyExpr, DIExpression::ApplyOffset, 4);
+  DbgValueProperties PropsWithExpr(NewExpr, false);
+  VLiveOuts[2].clear();
+  VLiveOuts[2].insert({Var,  DbgValue(LiveInRsp, PropsWithExpr, DbgValue::Def)});
+  Result = pickVPHILoc(*MBB3, Var, VLiveOutIdx, OutLocsPtr, Preds);
+  EXPECT_FALSE(Result);
+
+  DbgValueProperties PropsWithIndirect(EmptyExpr, true);
+  VLiveOuts[2].clear();
+  VLiveOuts[2].insert({Var,  DbgValue(LiveInRsp, PropsWithIndirect, DbgValue::Def)});
+  Result = pickVPHILoc(*MBB3, Var, VLiveOutIdx, OutLocsPtr, Preds);
+  EXPECT_FALSE(Result);
+}
+
+TEST_F(InstrRefLDVTest, pickVPHILocLoops) {
+  setupSimpleLoop();
+  //    entry
+  //     |
+  //     |/-----\
+  //    loopblk |
+  //     |\-----/
+  //     |
+  //     ret
+
+  ASSERT_TRUE(MTracker->getNumLocs() == 1);
+  LocIdx RspLoc(0);
+  Register RAX = getRegByName("RAX");
+  LocIdx RaxLoc = MTracker->lookupOrTrackRegister(RAX);
+
+  ValueIDNum OutLocs[3][2];
+  ValueIDNum *OutLocsPtr[4] = {OutLocs[0], OutLocs[1], OutLocs[2]};
+
+  initValueArray(OutLocsPtr, 3, 2);
+
+  unsigned EntryBlk = 0, LoopBlk = 1;
+
+  ValueIDNum LiveInRsp(EntryBlk, 0, RspLoc);
+  ValueIDNum LiveInRax(EntryBlk, 0, RaxLoc);
+  ValueIDNum RspPHIInBlk1(LoopBlk, 0, RspLoc);
+  ValueIDNum RaxPHIInBlk1(LoopBlk, 0, RaxLoc);
+
+  DebugVariable Var(FuncVariable, None, nullptr);
+  DbgValueProperties EmptyProps(EmptyExpr, false);
+  SmallVector<DenseMap<DebugVariable, DbgValue>, 32> VLiveOuts;
+  VLiveOuts.resize(3);
+  InstrRefBasedLDV::LiveIdxT VLiveOutIdx;
+  VLiveOutIdx[MBB0] = &VLiveOuts[0];
+  VLiveOutIdx[MBB1] = &VLiveOuts[1];
+  VLiveOutIdx[MBB2] = &VLiveOuts[2];
+
+  SmallVector<const MachineBasicBlock *, 2> Preds;
+  for (const auto *Pred : MBB1->predecessors())
+    Preds.push_back(Pred);
+
+  // Specify the live-outs around the joining block.
+  OutLocs[0][0] = LiveInRsp;
+  OutLocs[1][0] = LiveInRax;
+
+  Optional<ValueIDNum> Result;
+
+  // See that we can merge as normal on a backedge.
+  VLiveOuts[0].insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  VLiveOuts[1].insert({Var, DbgValue(LiveInRax, EmptyProps, DbgValue::Def)});
+  Result = pickVPHILoc(*MBB1, Var, VLiveOutIdx, OutLocsPtr, Preds);
+  // Should have picked a PHI in $rsp in block 1.
+  EXPECT_TRUE(Result);
+  if (Result)
+    EXPECT_EQ(*Result, RspPHIInBlk1);
+
+  // And that, if the desired values aren't available, we don't merge.
+  OutLocs[1][0] = LiveInRsp;
+  Result = pickVPHILoc(*MBB1, Var, VLiveOutIdx, OutLocsPtr, Preds);
+  EXPECT_FALSE(Result);
+
+  // Test the backedge behaviour: PHIs that feed back into themselves can
+  // carry this variables value. Feed in LiveInRsp in both $rsp and $rax
+  // from the entry block, but only put an appropriate backedge PHI in $rax.
+  // Only the $rax location can form the correct PHI.
+  OutLocs[0][0] = LiveInRsp;
+  OutLocs[0][1] = LiveInRsp;
+  OutLocs[1][0] = RaxPHIInBlk1;
+  OutLocs[1][1] = RaxPHIInBlk1;
+  VLiveOuts[0].clear();
+  VLiveOuts[1].clear();
+  VLiveOuts[0].insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  // Crucially, a VPHI originating in this block:
+  VLiveOuts[1].insert({Var, DbgValue(1, EmptyProps, DbgValue::VPHI)});
+  Result = pickVPHILoc(*MBB1, Var, VLiveOutIdx, OutLocsPtr, Preds);
+  EXPECT_TRUE(Result);
+  if (Result)
+    EXPECT_EQ(*Result, RaxPHIInBlk1);
+
+  // Merging should not be permitted if there's a usable PHI on the backedge,
+  // but it's in the wrong place. (Overwrite $rax).
+  OutLocs[1][1] = LiveInRax;
+  Result = pickVPHILoc(*MBB1, Var, VLiveOutIdx, OutLocsPtr, Preds);
+  EXPECT_FALSE(Result);
+
+  // Additionally, if the VPHI coming back on the loop backedge isn't from
+  // this block (block 1), we can't merge it.
+  OutLocs[1][1] = RaxPHIInBlk1;
+  VLiveOuts[1].clear();
+  VLiveOuts[1].insert({Var, DbgValue(0, EmptyProps, DbgValue::VPHI)});
+  Result = pickVPHILoc(*MBB1, Var, VLiveOutIdx, OutLocsPtr, Preds);
+  EXPECT_FALSE(Result);
+}
+
+TEST_F(InstrRefLDVTest, pickVPHILocBadlyNestedLoops) {
+  // Run some tests similar to pickVPHILocLoops, with more than one backedge,
+  // and check that we merge correctly over many candidate locations.
+  setupBadlyNestedLoops();
+  //           entry
+  //             |
+  //           loop1 -o
+  //             | ^
+  //             | ^
+  //           loop2 -o
+  //             | ^
+  //             | ^
+  //           loop3 -o
+  //             |
+  //            ret
+  ASSERT_TRUE(MTracker->getNumLocs() == 1);
+  LocIdx RspLoc(0);
+  Register RAX = getRegByName("RAX");
+  LocIdx RaxLoc = MTracker->lookupOrTrackRegister(RAX);
+  Register RBX = getRegByName("RBX");
+  LocIdx RbxLoc = MTracker->lookupOrTrackRegister(RBX);
+
+  ValueIDNum OutLocs[5][3];
+  ValueIDNum *OutLocsPtr[5] = {OutLocs[0], OutLocs[1], OutLocs[2], OutLocs[3], OutLocs[4]};
+
+  initValueArray(OutLocsPtr, 5, 3);
+
+  unsigned EntryBlk = 0, Loop1Blk = 1;
+
+  ValueIDNum LiveInRsp(EntryBlk, 0, RspLoc);
+  ValueIDNum LiveInRax(EntryBlk, 0, RaxLoc);
+  ValueIDNum LiveInRbx(EntryBlk, 0, RbxLoc);
+  ValueIDNum RspPHIInBlk1(Loop1Blk, 0, RspLoc);
+  ValueIDNum RaxPHIInBlk1(Loop1Blk, 0, RaxLoc);
+  ValueIDNum RbxPHIInBlk1(Loop1Blk, 0, RbxLoc);
+
+  DebugVariable Var(FuncVariable, None, nullptr);
+  DbgValueProperties EmptyProps(EmptyExpr, false);
+  SmallVector<DenseMap<DebugVariable, DbgValue>, 32> VLiveOuts;
+  VLiveOuts.resize(5);
+  InstrRefBasedLDV::LiveIdxT VLiveOutIdx;
+  VLiveOutIdx[MBB0] = &VLiveOuts[0];
+  VLiveOutIdx[MBB1] = &VLiveOuts[1];
+  VLiveOutIdx[MBB2] = &VLiveOuts[2];
+  VLiveOutIdx[MBB3] = &VLiveOuts[3];
+  VLiveOutIdx[MBB4] = &VLiveOuts[4];
+
+  // We're going to focus on block 1.
+  SmallVector<const MachineBasicBlock *, 2> Preds;
+  for (const auto *Pred : MBB1->predecessors())
+    Preds.push_back(Pred);
+
+  // Specify the live-outs around the joining block. Incoming edges from the
+  // entry block, self, and loop2.
+  OutLocs[0][0] = LiveInRsp;
+  OutLocs[1][0] = LiveInRax;
+  OutLocs[2][0] = LiveInRbx;
+
+  Optional<ValueIDNum> Result;
+
+  // See that we can merge as normal on a backedge.
+  VLiveOuts[0].insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  VLiveOuts[1].insert({Var, DbgValue(LiveInRax, EmptyProps, DbgValue::Def)});
+  VLiveOuts[2].insert({Var, DbgValue(LiveInRbx, EmptyProps, DbgValue::Def)});
+  Result = pickVPHILoc(*MBB1, Var, VLiveOutIdx, OutLocsPtr, Preds);
+  // Should have picked a PHI in $rsp in block 1.
+  EXPECT_TRUE(Result);
+  if (Result)
+    EXPECT_EQ(*Result, RspPHIInBlk1);
+
+  // Check too that permuting the live-out locations prevents merging
+  OutLocs[0][0] = LiveInRax;
+  OutLocs[1][0] = LiveInRbx;
+  OutLocs[2][0] = LiveInRsp;
+  Result = pickVPHILoc(*MBB1, Var, VLiveOutIdx, OutLocsPtr, Preds);
+  EXPECT_FALSE(Result);
+
+  OutLocs[0][0] = LiveInRsp;
+  OutLocs[1][0] = LiveInRax;
+  OutLocs[2][0] = LiveInRbx;
+
+  // Feeding a PHI back on one backedge shouldn't merge (block 1 self backedge
+  // wants LiveInRax).
+  OutLocs[1][0] = RspPHIInBlk1;
+  Result = pickVPHILoc(*MBB1, Var, VLiveOutIdx, OutLocsPtr, Preds);
+  EXPECT_FALSE(Result);
+
+  // If the variables value on that edge is a VPHI feeding into itself, that's
+  // fine.
+  VLiveOuts[1].clear();
+  VLiveOuts[1].insert({Var, DbgValue(1, EmptyProps, DbgValue::VPHI)});
+  Result = pickVPHILoc(*MBB1, Var, VLiveOutIdx, OutLocsPtr, Preds);
+  EXPECT_TRUE(Result);
+  if (Result)
+    EXPECT_EQ(*Result, RspPHIInBlk1);
+
+  // Likewise: the other backedge being a VPHI from block 1 should be accepted.
+  OutLocs[2][0] = RspPHIInBlk1;
+  VLiveOuts[2].clear();
+  VLiveOuts[2].insert({Var, DbgValue(1, EmptyProps, DbgValue::VPHI)});
+  Result = pickVPHILoc(*MBB1, Var, VLiveOutIdx, OutLocsPtr, Preds);
+  EXPECT_TRUE(Result);
+  if (Result)
+    EXPECT_EQ(*Result, RspPHIInBlk1);
+
+  // Here's where it becomes tricky: we should not merge if there are two
+  // _distinct_ backedge PHIs. We can't have a PHI that happens in both rsp
+  // and rax for example. We can only pick one location as the live-in.
+  OutLocs[2][0] = RaxPHIInBlk1;
+  Result = pickVPHILoc(*MBB1, Var, VLiveOutIdx, OutLocsPtr, Preds);
+  EXPECT_FALSE(Result);
+
+  // The above test sources correct machine-PHI-value from two places. Now
+  // try with one machine-PHI-value, but placed in two 
diff erent locations
+  // on the backedge. Again, we can't merge a location here, there's no
+  // location that works on all paths.
+  OutLocs[0][0] = LiveInRsp;
+  OutLocs[1][0] = RspPHIInBlk1;
+  OutLocs[2][0] = LiveInRsp;
+  OutLocs[2][1] = RspPHIInBlk1;
+  Result = pickVPHILoc(*MBB1, Var, VLiveOutIdx, OutLocsPtr, Preds);
+  EXPECT_FALSE(Result);
+
+  // Scatter various PHI values across the available locations. Only rbx (loc 2)
+  // has the right value in both backedges -- that's the loc that should be
+  // picked.
+  OutLocs[0][2] = LiveInRsp;
+  OutLocs[1][0] = RspPHIInBlk1;
+  OutLocs[1][1] = RaxPHIInBlk1;
+  OutLocs[1][2] = RbxPHIInBlk1;
+  OutLocs[2][0] = LiveInRsp;
+  OutLocs[2][1] = RspPHIInBlk1;
+  OutLocs[2][2] = RbxPHIInBlk1;
+  Result = pickVPHILoc(*MBB1, Var, VLiveOutIdx, OutLocsPtr, Preds);
+  EXPECT_TRUE(Result);
+  if (Result)
+    EXPECT_EQ(*Result, RbxPHIInBlk1);
+}
+
+TEST_F(InstrRefLDVTest, vlocJoinDiamond) {
+  //        entry
+  //        /  \
+  //      br1  br2
+  //        \  /
+  //         ret
+  setupDiamondBlocks();
+
+  ASSERT_TRUE(MTracker->getNumLocs() == 1);
+  LocIdx RspLoc(0);
+  Register RAX = getRegByName("RAX");
+  LocIdx RaxLoc = MTracker->lookupOrTrackRegister(RAX);
+
+  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);
+
+  DebugVariable Var(FuncVariable, None, nullptr);
+  DbgValueProperties EmptyProps(EmptyExpr, false);
+  SmallVector<DenseMap<DebugVariable, DbgValue>, 32> VLiveOuts, VLiveIns;
+  VLiveOuts.resize(4);
+  VLiveIns.resize(4);
+  InstrRefBasedLDV::LiveIdxT VLiveOutIdx, VLiveInIdx;
+  VLiveOutIdx[MBB0] = &VLiveOuts[0];
+  VLiveOutIdx[MBB1] = &VLiveOuts[1];
+  VLiveOutIdx[MBB2] = &VLiveOuts[2];
+  VLiveOutIdx[MBB3] = &VLiveOuts[3];
+  VLiveInIdx[MBB0] = &VLiveIns[0];
+  VLiveInIdx[MBB1] = &VLiveIns[1];
+  VLiveInIdx[MBB2] = &VLiveIns[2];
+  VLiveInIdx[MBB3] = &VLiveIns[3];
+
+  SmallPtrSet<const MachineBasicBlock *, 8> AllBlocks;
+  AllBlocks.insert(MBB0);
+  AllBlocks.insert(MBB1);
+  AllBlocks.insert(MBB2);
+  AllBlocks.insert(MBB3);
+
+  SmallVector<const MachineBasicBlock *, 2> Preds;
+  for (const auto *Pred : MBB3->predecessors())
+    Preds.push_back(Pred);
+
+  SmallSet<DebugVariable, 4> AllVars;
+  AllVars.insert(Var);
+
+  DenseMap<DebugVariable, DbgValue> JoinedLocs;
+
+  // vlocJoin is here to propagate incoming values, and eliminate PHIs. Start
+  // off by propagating a value into the merging block, number 3.
+  VLiveIns[3].insert({Var, DbgValue(3, EmptyProps, DbgValue::NoVal)});
+  VLiveOuts[1].insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  VLiveOuts[2].insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  bool Result = vlocJoin(*MBB3, VLiveOutIdx, VLiveInIdx, AllVars,
+                         AllBlocks, AllBlocks, JoinedLocs);
+  EXPECT_TRUE(Result); // Output locs should have changed.
+  auto It = VLiveIns[3].find(Var);
+  EXPECT_EQ(It->second.Kind, DbgValue::Def);
+  EXPECT_EQ(It->second.ID, LiveInRsp);
+  JoinedLocs.clear();
+
+  // And if we did it a second time, leaving the live-ins as it was, then
+  // we should report no change.
+  Result = vlocJoin(*MBB3, VLiveOutIdx, VLiveInIdx, AllVars,
+                    AllBlocks, AllBlocks, JoinedLocs);
+  EXPECT_FALSE(Result);
+  JoinedLocs.clear();
+  VLiveIns[3].clear();
+
+  // 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[2].clear();
+  VLiveOuts[2].insert({Var, DbgValue(LiveInRax, EmptyProps, DbgValue::Def)});
+  VLiveIns[3].insert({Var, DbgValue(3, EmptyProps, DbgValue::NoVal)});
+  Result = vlocJoin(*MBB3, VLiveOutIdx, VLiveInIdx, AllVars,
+                    AllBlocks, AllBlocks, JoinedLocs);
+  EXPECT_TRUE(Result);
+  It = VLiveIns[3].find(Var);
+  EXPECT_EQ(It->second.Kind, DbgValue::Def);
+  // RPO is blocks 0 2 1 3, so LiveInRax is picked as the first predecessor
+  // of this join.
+  EXPECT_EQ(It->second.ID, LiveInRax);
+  JoinedLocs.clear();
+  VLiveIns[3].clear();
+
+  // No tests for whether vlocJoin will pass-through a variable with 
diff ering
+  // expressions / properties. Those can only come about due to assignments; and
+  // for any assignment at all, a PHI should have been placed at the dominance
+  // frontier. We rely on the IDF calculator being accurate (which is OK,
+  // because so does the rest of LLVM).
+
+  // Try placing a PHI. With 
diff ering input values (LiveInRsp, LiveInRax),
+  // this PHI should not be eliminated.
+  VLiveIns[3].insert({Var, DbgValue(3, EmptyProps, DbgValue::VPHI)});
+  Result = vlocJoin(*MBB3, VLiveOutIdx, VLiveInIdx, AllVars,
+                    AllBlocks, AllBlocks, JoinedLocs);
+  // Expect no change.
+  EXPECT_FALSE(Result);
+  It = VLiveIns[3].find(Var);
+  EXPECT_EQ(It->second.Kind, DbgValue::VPHI);
+  // This should not have been assigned a fixed value.
+  EXPECT_EQ(It->second.ID, ValueIDNum::EmptyValue);
+  EXPECT_EQ(It->second.BlockNo, 3);
+  JoinedLocs.clear();
+  VLiveIns[3].clear();
+
+  // 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].clear();
+  VLiveOuts[2].clear();
+  VLiveOuts[1].insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  VLiveOuts[2].insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  VLiveIns[3].insert({Var, DbgValue(3, EmptyProps, DbgValue::VPHI)});
+  Result = vlocJoin(*MBB3, VLiveOutIdx, VLiveInIdx, AllVars,
+                    AllBlocks, AllBlocks, JoinedLocs);
+  EXPECT_TRUE(Result);
+  It = VLiveIns[3].find(Var);
+  EXPECT_EQ(It->second.Kind, DbgValue::Def);
+  EXPECT_EQ(It->second.ID, LiveInRsp);
+  JoinedLocs.clear();
+  VLiveIns[3].clear();
+
+  // 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].clear();
+  VLiveOuts[2].clear();
+  VLiveOuts[1].insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  VLiveOuts[2].insert({Var, DbgValue(LiveInRax, EmptyProps, DbgValue::Def)});
+  VLiveIns[3].insert({Var, DbgValue(2, EmptyProps, DbgValue::VPHI)});
+  Result = vlocJoin(*MBB3, VLiveOutIdx, VLiveInIdx, AllVars,
+                    AllBlocks, AllBlocks, JoinedLocs);
+  EXPECT_TRUE(Result);
+  It = VLiveIns[3].find(Var);
+  EXPECT_EQ(It->second.Kind, DbgValue::Def);
+  EXPECT_EQ(It->second.ID, LiveInRax); // from block 2
+  JoinedLocs.clear();
+  VLiveIns[3].clear();
+
+  // The above test, but test that we will install one value-propagated VPHI
+  // over another.
+  VLiveOuts[1].clear();
+  VLiveOuts[2].clear();
+  VLiveOuts[1].insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  VLiveOuts[2].insert({Var, DbgValue(0, EmptyProps, DbgValue::VPHI)});
+  VLiveIns[3].insert({Var, DbgValue(2, EmptyProps, DbgValue::VPHI)});
+  Result = vlocJoin(*MBB3, VLiveOutIdx, VLiveInIdx, AllVars,
+                    AllBlocks, AllBlocks, JoinedLocs);
+  EXPECT_TRUE(Result);
+  It = VLiveIns[3].find(Var);
+  EXPECT_EQ(It->second.Kind, DbgValue::VPHI);
+  EXPECT_EQ(It->second.BlockNo, 0);
+  JoinedLocs.clear();
+  VLiveIns[3].clear();
+
+  // We shouldn't eliminate PHIs when properties disagree.
+  DbgValueProperties PropsWithIndirect(EmptyExpr, true);
+  VLiveOuts[1].clear();
+  VLiveOuts[2].clear();
+  VLiveOuts[1].insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  VLiveOuts[2].insert({Var, DbgValue(LiveInRsp, PropsWithIndirect, DbgValue::Def)});
+  VLiveIns[3].insert({Var, DbgValue(3, EmptyProps, DbgValue::VPHI)});
+  Result = vlocJoin(*MBB3, VLiveOutIdx, VLiveInIdx, AllVars,
+                    AllBlocks, AllBlocks, JoinedLocs);
+  EXPECT_FALSE(Result);
+  It = VLiveIns[3].find(Var);
+  EXPECT_EQ(It->second.Kind, DbgValue::VPHI);
+  EXPECT_EQ(It->second.BlockNo, 3);
+  JoinedLocs.clear();
+  VLiveIns[3].clear();
+
+  // 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].clear();
+  VLiveOuts[2].clear();
+  VLiveOuts[1].insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  VLiveOuts[2].insert({Var, DbgValue(LiveInRsp, PropsWithIndirect, DbgValue::Def)});
+  VLiveIns[3].insert({Var, DbgValue(2, EmptyProps, DbgValue::VPHI)});
+  Result = vlocJoin(*MBB3, VLiveOutIdx, VLiveInIdx, AllVars,
+                    AllBlocks, AllBlocks, JoinedLocs);
+  EXPECT_TRUE(Result);
+  It = VLiveIns[3].find(Var);
+  EXPECT_EQ(It->second.Kind, DbgValue::Def);
+  EXPECT_EQ(It->second.ID, LiveInRsp);
+  // Also check properties come from block 2, the first RPO predecessor to block
+  // three.
+  EXPECT_EQ(It->second.Properties, PropsWithIndirect);
+  JoinedLocs.clear();
+  VLiveIns[3].clear();
+
+  // Again, disagreeing properties, this time the expr, should cause a PHI to
+  // not be eliminated.
+  DIExpression *NewExpr = DIExpression::prepend(EmptyExpr, DIExpression::ApplyOffset, 4);
+  DbgValueProperties PropsWithExpr(NewExpr, false);
+  VLiveOuts[1].clear();
+  VLiveOuts[2].clear();
+  VLiveOuts[1].insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  VLiveOuts[2].insert({Var, DbgValue(LiveInRsp, PropsWithExpr, DbgValue::Def)});
+  VLiveIns[3].insert({Var, DbgValue(3, EmptyProps, DbgValue::VPHI)});
+  Result = vlocJoin(*MBB3, VLiveOutIdx, VLiveInIdx, AllVars,
+                    AllBlocks, AllBlocks, JoinedLocs);
+  EXPECT_FALSE(Result);
+  It = VLiveIns[3].find(Var);
+  EXPECT_EQ(It->second.Kind, DbgValue::VPHI);
+  EXPECT_EQ(It->second.BlockNo, 3);
+  EXPECT_EQ(It->second.Properties, EmptyProps);
+  JoinedLocs.clear();
+  VLiveIns[3].clear();
+}
+
+TEST_F(InstrRefLDVTest, vlocJoinLoops) {
+  setupSimpleLoop();
+  //    entry
+  //     |
+  //     |/-----\
+  //    loopblk |
+  //     |\-----/
+  //     |
+  //     ret
+  ASSERT_TRUE(MTracker->getNumLocs() == 1);
+  LocIdx RspLoc(0);
+  Register RAX = getRegByName("RAX");
+  LocIdx RaxLoc = MTracker->lookupOrTrackRegister(RAX);
+
+  unsigned EntryBlk = 0, LoopBlk = 1;
+
+  ValueIDNum LiveInRsp(EntryBlk, 0, RspLoc);
+  ValueIDNum LiveInRax(EntryBlk, 0, RaxLoc);
+  ValueIDNum RspPHIInBlk1(LoopBlk, 0, RspLoc);
+
+  DebugVariable Var(FuncVariable, None, nullptr);
+  DbgValueProperties EmptyProps(EmptyExpr, false);
+  SmallVector<DenseMap<DebugVariable, DbgValue>, 32> VLiveOuts, VLiveIns;
+  VLiveOuts.resize(3);
+  VLiveIns.resize(3);
+  InstrRefBasedLDV::LiveIdxT VLiveOutIdx, VLiveInIdx;
+  VLiveOutIdx[MBB0] = &VLiveOuts[0];
+  VLiveOutIdx[MBB1] = &VLiveOuts[1];
+  VLiveOutIdx[MBB2] = &VLiveOuts[2];
+  VLiveInIdx[MBB0] = &VLiveIns[0];
+  VLiveInIdx[MBB1] = &VLiveIns[1];
+  VLiveInIdx[MBB2] = &VLiveIns[2];
+
+  SmallPtrSet<const MachineBasicBlock *, 8> AllBlocks;
+  AllBlocks.insert(MBB0);
+  AllBlocks.insert(MBB1);
+  AllBlocks.insert(MBB2);
+
+  SmallVector<const MachineBasicBlock *, 2> Preds;
+  for (const auto *Pred : MBB1->predecessors())
+    Preds.push_back(Pred);
+
+  SmallSet<DebugVariable, 4> AllVars;
+  AllVars.insert(Var);
+
+  DenseMap<DebugVariable, DbgValue> JoinedLocs;
+
+  // Test some back-edge-specific behaviours of vloc join. Mostly: the fact that
+  // VPHIs that arrive on backedges can be eliminated, despite having 
diff erent
+  // values to the predecessor.
+
+  // First: when there's no VPHI placed already, propagate the live-in value of
+  // the first RPO predecessor.
+  VLiveOuts[0].insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  VLiveOuts[1].insert({Var, DbgValue(LiveInRax, EmptyProps, DbgValue::Def)});
+  VLiveIns[1].insert({Var, DbgValue(LiveInRax, EmptyProps, DbgValue::Def)});
+  bool Result = vlocJoin(*MBB1, VLiveOutIdx, VLiveInIdx, AllVars,
+                    AllBlocks, AllBlocks, JoinedLocs);
+  EXPECT_TRUE(Result);
+  auto It = VLiveIns[1].find(Var);
+  EXPECT_EQ(It->second.Kind, DbgValue::Def);
+  EXPECT_EQ(It->second.ID, LiveInRsp);
+  JoinedLocs.clear();
+  VLiveIns[1].clear();
+
+  // If there is a VPHI: don't elimiante it if there are disagreeing values.
+  VLiveOuts[0].insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  VLiveOuts[1].insert({Var, DbgValue(LiveInRax, EmptyProps, DbgValue::Def)});
+  VLiveIns[1].insert({Var, DbgValue(1, EmptyProps, DbgValue::VPHI)});
+  Result = vlocJoin(*MBB1, VLiveOutIdx, VLiveInIdx, AllVars,
+                    AllBlocks, AllBlocks, JoinedLocs);
+  EXPECT_FALSE(Result);
+  It = VLiveIns[1].find(Var);
+  EXPECT_EQ(It->second.Kind, DbgValue::VPHI);
+  EXPECT_EQ(It->second.BlockNo, 1);
+  JoinedLocs.clear();
+  VLiveIns[1].clear();
+
+  // If we feed this VPHI back into itself though, we can eliminate it.
+  VLiveOuts[0].clear();
+  VLiveOuts[1].clear();
+  VLiveOuts[0].insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  VLiveOuts[1].insert({Var, DbgValue(1, EmptyProps, DbgValue::VPHI)});
+  VLiveIns[1].insert({Var, DbgValue(1, EmptyProps, DbgValue::VPHI)});
+  Result = vlocJoin(*MBB1, VLiveOutIdx, VLiveInIdx, AllVars,
+                    AllBlocks, AllBlocks, JoinedLocs);
+  EXPECT_TRUE(Result);
+  It = VLiveIns[1].find(Var);
+  EXPECT_EQ(It->second.Kind, DbgValue::Def);
+  EXPECT_EQ(It->second.ID, LiveInRsp);
+  JoinedLocs.clear();
+  VLiveIns[1].clear();
+
+  // Don't eliminate backedge VPHIs if the predecessors have 
diff erent
+  // properties.
+  DIExpression *NewExpr = DIExpression::prepend(EmptyExpr, DIExpression::ApplyOffset, 4);
+  DbgValueProperties PropsWithExpr(NewExpr, false);
+  VLiveOuts[1].clear();
+  VLiveOuts[1].insert({Var, DbgValue(1, PropsWithExpr, DbgValue::VPHI)});
+  VLiveIns[1].insert({Var, DbgValue(1, EmptyProps, DbgValue::VPHI)});
+  Result = vlocJoin(*MBB1, VLiveOutIdx, VLiveInIdx, AllVars,
+                    AllBlocks, AllBlocks, JoinedLocs);
+  EXPECT_FALSE(Result);
+  It = VLiveIns[1].find(Var);
+  EXPECT_EQ(It->second.Kind, DbgValue::VPHI);
+  EXPECT_EQ(It->second.BlockNo, 1);
+  JoinedLocs.clear();
+  VLiveIns[1].clear();
+
+  // Backedges with VPHIs, but from the wrong block, shouldn't be eliminated.
+  VLiveOuts[1].clear();
+  VLiveOuts[1].insert({Var, DbgValue(0, EmptyProps, DbgValue::VPHI)});
+  VLiveIns[1].insert({Var, DbgValue(1, EmptyProps, DbgValue::VPHI)});
+  Result = vlocJoin(*MBB1, VLiveOutIdx, VLiveInIdx, AllVars,
+                    AllBlocks, AllBlocks, JoinedLocs);
+  EXPECT_FALSE(Result);
+  It = VLiveIns[1].find(Var);
+  EXPECT_EQ(It->second.Kind, DbgValue::VPHI);
+  EXPECT_EQ(It->second.BlockNo, 1);
+  JoinedLocs.clear();
+  VLiveIns[1].clear();
+}
+
+TEST_F(InstrRefLDVTest, vlocJoinBadlyNestedLoops) {
+  // Test PHI elimination in the presence of multiple backedges.
+  setupBadlyNestedLoops();
+  //           entry
+  //             |
+  //           loop1 -o
+  //             | ^
+  //             | ^
+  //           loop2 -o
+  //             | ^
+  //             | ^
+  //           loop3 -o
+  //             |
+  //            ret
+  ASSERT_TRUE(MTracker->getNumLocs() == 1);
+  LocIdx RspLoc(0);
+  Register RAX = getRegByName("RAX");
+  LocIdx RaxLoc = MTracker->lookupOrTrackRegister(RAX);
+  Register RBX = getRegByName("RBX");
+  LocIdx RbxLoc = MTracker->lookupOrTrackRegister(RBX);
+
+  unsigned EntryBlk = 0;
+
+  ValueIDNum LiveInRsp(EntryBlk, 0, RspLoc);
+  ValueIDNum LiveInRax(EntryBlk, 0, RaxLoc);
+  ValueIDNum LiveInRbx(EntryBlk, 0, RbxLoc);
+
+  DebugVariable Var(FuncVariable, None, nullptr);
+  DbgValueProperties EmptyProps(EmptyExpr, false);
+  SmallVector<DenseMap<DebugVariable, DbgValue>, 32> VLiveOuts, VLiveIns;
+  VLiveOuts.resize(5);
+  VLiveIns.resize(5);
+  InstrRefBasedLDV::LiveIdxT VLiveOutIdx, VLiveInIdx;
+  VLiveOutIdx[MBB0] = &VLiveOuts[0];
+  VLiveOutIdx[MBB1] = &VLiveOuts[1];
+  VLiveOutIdx[MBB2] = &VLiveOuts[2];
+  VLiveOutIdx[MBB3] = &VLiveOuts[3];
+  VLiveOutIdx[MBB4] = &VLiveOuts[4];
+  VLiveInIdx[MBB0] = &VLiveIns[0];
+  VLiveInIdx[MBB1] = &VLiveIns[1];
+  VLiveInIdx[MBB2] = &VLiveIns[2];
+  VLiveInIdx[MBB3] = &VLiveIns[3];
+  VLiveInIdx[MBB4] = &VLiveIns[4];
+
+  SmallPtrSet<const MachineBasicBlock *, 8> AllBlocks;
+  AllBlocks.insert(MBB0);
+  AllBlocks.insert(MBB1);
+  AllBlocks.insert(MBB2);
+  AllBlocks.insert(MBB3);
+  AllBlocks.insert(MBB4);
+
+  // We're going to focus on block 1.
+  SmallVector<const MachineBasicBlock *, 3> Preds;
+  for (const auto *Pred : MBB1->predecessors())
+    Preds.push_back(Pred);
+
+  SmallSet<DebugVariable, 4> AllVars;
+  AllVars.insert(Var);
+
+  DenseMap<DebugVariable, DbgValue> JoinedLocs;
+
+  // Test a normal VPHI isn't eliminated.
+  VLiveOuts[0].insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  VLiveOuts[1].insert({Var, DbgValue(LiveInRax, EmptyProps, DbgValue::Def)});
+  VLiveOuts[2].insert({Var, DbgValue(LiveInRbx, EmptyProps, DbgValue::Def)});
+  VLiveIns[1].insert({Var, DbgValue(1, EmptyProps, DbgValue::VPHI)});
+  bool Result = vlocJoin(*MBB1, VLiveOutIdx, VLiveInIdx, AllVars,
+                    AllBlocks, AllBlocks, JoinedLocs);
+  EXPECT_FALSE(Result);
+  auto It = VLiveIns[1].find(Var);
+  EXPECT_EQ(It->second.Kind, DbgValue::VPHI);
+  EXPECT_EQ(It->second.BlockNo, 1);
+  JoinedLocs.clear();
+  VLiveIns[1].clear();
+
+  // Common VPHIs on backedges should merge.
+  VLiveOuts[0].clear();
+  VLiveOuts[1].clear();
+  VLiveOuts[2].clear();
+  VLiveOuts[0].insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  VLiveOuts[1].insert({Var, DbgValue(1, EmptyProps, DbgValue::VPHI)});
+  VLiveOuts[2].insert({Var, DbgValue(1, EmptyProps, DbgValue::VPHI)});
+  VLiveIns[1].insert({Var, DbgValue(1, EmptyProps, DbgValue::VPHI)});
+  Result = vlocJoin(*MBB1, VLiveOutIdx, VLiveInIdx, AllVars,
+                    AllBlocks, AllBlocks, JoinedLocs);
+  EXPECT_TRUE(Result);
+  It = VLiveIns[1].find(Var);
+  EXPECT_EQ(It->second.Kind, DbgValue::Def);
+  EXPECT_EQ(It->second.ID, LiveInRsp);
+  JoinedLocs.clear();
+  VLiveIns[1].clear();
+
+  // They shouldn't merge if one of their properties is 
diff erent.
+  DbgValueProperties PropsWithIndirect(EmptyExpr, true);
+  VLiveOuts[0].clear();
+  VLiveOuts[1].clear();
+  VLiveOuts[2].clear();
+  VLiveOuts[0].insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  VLiveOuts[1].insert({Var, DbgValue(1, EmptyProps, DbgValue::VPHI)});
+  VLiveOuts[2].insert({Var, DbgValue(1, PropsWithIndirect, DbgValue::VPHI)});
+  VLiveIns[1].insert({Var, DbgValue(1, EmptyProps, DbgValue::VPHI)});
+  Result = vlocJoin(*MBB1, VLiveOutIdx, VLiveInIdx, AllVars,
+                    AllBlocks, AllBlocks, JoinedLocs);
+  EXPECT_FALSE(Result);
+  It = VLiveIns[1].find(Var);
+  EXPECT_EQ(It->second.Kind, DbgValue::VPHI);
+  EXPECT_EQ(It->second.BlockNo, 1);
+  JoinedLocs.clear();
+  VLiveIns[1].clear();
+
+  // VPHIs from 
diff erent blocks should not merge.
+  VLiveOuts[0].clear();
+  VLiveOuts[1].clear();
+  VLiveOuts[2].clear();
+  VLiveOuts[0].insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  VLiveOuts[1].insert({Var, DbgValue(1, EmptyProps, DbgValue::VPHI)});
+  VLiveOuts[2].insert({Var, DbgValue(2, EmptyProps, DbgValue::VPHI)});
+  VLiveIns[1].insert({Var, DbgValue(1, EmptyProps, DbgValue::VPHI)});
+  Result = vlocJoin(*MBB1, VLiveOutIdx, VLiveInIdx, AllVars,
+                    AllBlocks, AllBlocks, JoinedLocs);
+  EXPECT_FALSE(Result);
+  It = VLiveIns[1].find(Var);
+  EXPECT_EQ(It->second.Kind, DbgValue::VPHI);
+  EXPECT_EQ(It->second.BlockNo, 1);
+  JoinedLocs.clear();
+  VLiveIns[1].clear();
+}
+
+// Above are tests for picking VPHI locations, and eliminating VPHIs. No
+// unit-tests are written for evaluating the transfer function as that's
+// pretty straight forwards, or applying VPHI-location-picking to live-ins.
+// Instead, pre-set some machine locations and apply buildVLocValueMap to the
+// existing CFG patterns.
+TEST_F(InstrRefLDVTest, VLocSingleBlock) {
+  setupSingleBlock();
+
+  ASSERT_TRUE(MTracker->getNumLocs() == 1);
+  LocIdx RspLoc(0);
+
+  ValueIDNum InLocs[2], OutLocs[2];
+  ValueIDNum *InLocsPtr[1] = {&InLocs[0]};
+  ValueIDNum *OutLocsPtr[1] = {&OutLocs[0]};
+
+  ValueIDNum LiveInRsp = ValueIDNum(0, 0, RspLoc);
+  InLocs[0] = OutLocs[0] = LiveInRsp;
+
+  DebugVariable Var(FuncVariable, None, nullptr);
+  DbgValueProperties EmptyProps(EmptyExpr, false);
+
+  SmallSet<DebugVariable, 4> AllVars;
+  AllVars.insert(Var);
+
+  // Mild hack: rather than constructing machine instructions in each block
+  // and creating lexical scopes across them, instead just tell
+  // buildVLocValueMap that there's an assignment in every block. That makes
+  // every block in scope.
+  SmallPtrSet<MachineBasicBlock *, 4> AssignBlocks;
+  AssignBlocks.insert(MBB0);
+
+  SmallVector<VLocTracker, 1> VLocs;
+  VLocs.resize(1);
+
+  InstrRefBasedLDV::LiveInsT Output;
+
+  // Test that, with no assignments at all, no mappings are created for the
+  // variable in this function.
+  buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
+                    OutLocsPtr, InLocsPtr, VLocs);
+  EXPECT_EQ(Output.size(), 0ul);
+
+  // 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)});
+  buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
+                    OutLocsPtr, InLocsPtr, VLocs);
+  EXPECT_EQ(Output.size(), 0ul);
+
+  // There is pretty much nothing else of interest to test with a single block.
+  // It's not relevant to the SSA-construction parts of variable values.
+}
+
+TEST_F(InstrRefLDVTest, VLocDiamondBlocks) {
+  setupDiamondBlocks();
+  //        entry
+  //        /  \
+  //      br1  br2
+  //        \  /
+  //         ret
+
+  ASSERT_TRUE(MTracker->getNumLocs() == 1);
+  LocIdx RspLoc(0);
+  Register RAX = getRegByName("RAX");
+  LocIdx RaxLoc = MTracker->lookupOrTrackRegister(RAX);
+
+  unsigned EntryBlk = 0, RetBlk = 3;
+
+  ValueIDNum LiveInRsp = ValueIDNum(EntryBlk, 0, RspLoc);
+  ValueIDNum LiveInRax = ValueIDNum(EntryBlk, 0, RaxLoc);
+  ValueIDNum RspPHIInBlk3 = ValueIDNum(RetBlk, 0, RspLoc);
+
+  ValueIDNum InLocs[4][2], OutLocs[4][2];
+  ValueIDNum *InLocsPtr[4] = {InLocs[0], InLocs[1], InLocs[2], InLocs[3]};
+  ValueIDNum *OutLocsPtr[4] = {OutLocs[0], OutLocs[1], OutLocs[2], OutLocs[3]};
+
+  initValueArray(InLocsPtr, 4, 2);
+  initValueArray(OutLocsPtr, 4, 2);
+
+  DebugVariable Var(FuncVariable, None, nullptr);
+  DbgValueProperties EmptyProps(EmptyExpr, false);
+
+  SmallSet<DebugVariable, 4> AllVars;
+  AllVars.insert(Var);
+
+  // Mild hack: rather than constructing machine instructions in each block
+  // and creating lexical scopes across them, instead just tell
+  // buildVLocValueMap that there's an assignment in every block. That makes
+  // every block in scope.
+  SmallPtrSet<MachineBasicBlock *, 4> AssignBlocks;
+  AssignBlocks.insert(MBB0);
+  AssignBlocks.insert(MBB1);
+  AssignBlocks.insert(MBB2);
+  AssignBlocks.insert(MBB3);
+
+  SmallVector<VLocTracker, 1> VLocs;
+  VLocs.resize(4);
+
+  InstrRefBasedLDV::LiveInsT Output;
+
+  // Start off with LiveInRsp in every location.
+  for (unsigned int I = 0; I < 4; ++I) {
+    InLocs[I][0] = InLocs[I][1] = LiveInRsp;
+    OutLocs[I][0] = OutLocs[I][1] = LiveInRsp;
+  }
+
+  auto ClearOutputs = [&]() {
+    for (auto &Elem : Output)
+      Elem.clear();
+  };
+  Output.resize(4);
+
+  // No assignments -> no values.
+  buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
+                    OutLocsPtr, InLocsPtr, VLocs);
+  EXPECT_EQ(Output[0].size(), 0ul);
+  EXPECT_EQ(Output[1].size(), 0ul);
+  EXPECT_EQ(Output[2].size(), 0ul);
+  EXPECT_EQ(Output[3].size(), 0ul);
+
+  // 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)});
+  buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
+                    OutLocsPtr, InLocsPtr, VLocs);
+  EXPECT_EQ(Output[0].size(), 0ul);
+  EXPECT_EQ(Output[1].size(), 0ul);
+  EXPECT_EQ(Output[2].size(), 0ul);
+  EXPECT_EQ(Output[3].size(), 0ul);
+  ClearOutputs();
+
+  // 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)});
+  buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
+                    OutLocsPtr, InLocsPtr, VLocs);
+  EXPECT_EQ(Output[0].size(), 0ul);
+  EXPECT_EQ(Output[1].size(), 0ul);
+  EXPECT_EQ(Output[2].size(), 0ul);
+  EXPECT_EQ(Output[3].size(), 0ul);
+  VLocs[2].Vars.clear();
+  ClearOutputs();
+
+  VLocs[1].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
+                    OutLocsPtr, InLocsPtr, VLocs);
+  EXPECT_EQ(Output[0].size(), 0ul);
+  EXPECT_EQ(Output[1].size(), 0ul);
+  EXPECT_EQ(Output[2].size(), 0ul);
+  EXPECT_EQ(Output[3].size(), 0ul);
+  VLocs[1].Vars.clear();
+  ClearOutputs();
+
+  // 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)});
+  buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
+                    OutLocsPtr, InLocsPtr, VLocs);
+  EXPECT_EQ(Output[0].size(), 0ul);
+  ASSERT_EQ(Output[1].size(), 1ul);
+  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[2][0].second.Kind, DbgValue::Def);
+  EXPECT_EQ(Output[2][0].second.ID, LiveInRsp);
+  EXPECT_EQ(Output[3][0].second.Kind, DbgValue::Def);
+  EXPECT_EQ(Output[3][0].second.ID, LiveInRsp);
+  ClearOutputs();
+  VLocs[0].Vars.clear();
+
+  // Additionally, even if that value isn't available in the register file, it
+  // 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)});
+  buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
+                    OutLocsPtr, InLocsPtr, VLocs);
+  EXPECT_EQ(Output[0].size(), 0ul);
+  ASSERT_EQ(Output[1].size(), 1ul);
+  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[2][0].second.Kind, DbgValue::Def);
+  EXPECT_EQ(Output[2][0].second.ID, LiveInRax);
+  EXPECT_EQ(Output[3][0].second.Kind, DbgValue::Def);
+  EXPECT_EQ(Output[3][0].second.ID, LiveInRax);
+  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)});
+  buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
+                    OutLocsPtr, InLocsPtr, VLocs);
+  EXPECT_EQ(Output[0].size(), 0ul);
+  EXPECT_EQ(Output[1].size(), 0ul);
+  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);
+  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[1].Vars.insert({Var, DbgValue(EmptyProps, DbgValue::Undef)});
+  buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
+                    OutLocsPtr, InLocsPtr, VLocs);
+  EXPECT_EQ(Output[0].size(), 0ul);
+  ASSERT_EQ(Output[1].size(), 1ul);
+  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[2][0].second.Kind, DbgValue::Def);
+  EXPECT_EQ(Output[2][0].second.ID, LiveInRsp);
+  ClearOutputs();
+  VLocs[0].Vars.clear();
+  VLocs[1].Vars.clear();
+
+  // 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)});
+  buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
+                    OutLocsPtr, InLocsPtr, VLocs);
+  EXPECT_EQ(Output[0].size(), 0ul);
+  ASSERT_EQ(Output[1].size(), 1ul);
+  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[2][0].second.Kind, DbgValue::Def);
+  EXPECT_EQ(Output[2][0].second.ID, LiveInRsp);
+  ClearOutputs();
+  VLocs[0].Vars.clear();
+  VLocs[1].Vars.clear();
+
+  // But on the other hand, if there's a location in the register file where
+  // those two values can be joined, do so.
+  OutLocs[1][0] = LiveInRax;
+  VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  VLocs[1].Vars.insert({Var, DbgValue(LiveInRax, EmptyProps, DbgValue::Def)});
+  buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
+                    OutLocsPtr, InLocsPtr, VLocs);
+  EXPECT_EQ(Output[0].size(), 0ul);
+  ASSERT_EQ(Output[1].size(), 1ul);
+  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[2][0].second.Kind, DbgValue::Def);
+  EXPECT_EQ(Output[2][0].second.ID, LiveInRsp);
+  EXPECT_EQ(Output[3][0].second.Kind, DbgValue::Def);
+  EXPECT_EQ(Output[3][0].second.ID, RspPHIInBlk3);
+  ClearOutputs();
+  VLocs[0].Vars.clear();
+  VLocs[1].Vars.clear();
+}
+
+TEST_F(InstrRefLDVTest, VLocSimpleLoop) {
+  setupSimpleLoop();
+  //    entry
+  //     |
+  //     |/-----\
+  //    loopblk |
+  //     |\-----/
+  //     |
+  //     ret
+
+  ASSERT_TRUE(MTracker->getNumLocs() == 1);
+  LocIdx RspLoc(0);
+  Register RAX = getRegByName("RAX");
+  LocIdx RaxLoc = MTracker->lookupOrTrackRegister(RAX);
+
+  unsigned EntryBlk = 0, LoopBlk = 1;
+
+  ValueIDNum LiveInRsp = ValueIDNum(EntryBlk, 0, RspLoc);
+  ValueIDNum LiveInRax = ValueIDNum(EntryBlk, 0, RaxLoc);
+  ValueIDNum RspPHIInBlk1 = ValueIDNum(LoopBlk, 0, RspLoc);
+  ValueIDNum RspDefInBlk1 = ValueIDNum(LoopBlk, 1, RspLoc);
+  ValueIDNum RaxPHIInBlk1 = ValueIDNum(LoopBlk, 0, RaxLoc);
+
+  ValueIDNum InLocs[3][2], OutLocs[3][2];
+  ValueIDNum *InLocsPtr[3] = {InLocs[0], InLocs[1], InLocs[2]};
+  ValueIDNum *OutLocsPtr[3] = {OutLocs[0], OutLocs[1], OutLocs[2]};
+
+  initValueArray(InLocsPtr, 3, 2);
+  initValueArray(OutLocsPtr, 3, 2);
+
+  DebugVariable Var(FuncVariable, None, nullptr);
+  DbgValueProperties EmptyProps(EmptyExpr, false);
+
+  SmallSet<DebugVariable, 4> AllVars;
+  AllVars.insert(Var);
+
+  SmallPtrSet<MachineBasicBlock *, 4> AssignBlocks;
+  AssignBlocks.insert(MBB0);
+  AssignBlocks.insert(MBB1);
+  AssignBlocks.insert(MBB2);
+
+  SmallVector<VLocTracker, 3> VLocs;
+  VLocs.resize(3);
+
+  InstrRefBasedLDV::LiveInsT Output;
+
+  // Start off with LiveInRsp in every location.
+  for (unsigned int I = 0; I < 3; ++I) {
+    InLocs[I][0] = InLocs[I][1] = LiveInRsp;
+    OutLocs[I][0] = OutLocs[I][1] = LiveInRsp;
+  }
+
+  auto ClearOutputs = [&]() {
+    for (auto &Elem : Output)
+      Elem.clear();
+  };
+  Output.resize(3);
+
+  // Easy starter: a dominating assign should propagate to all blocks.
+  VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
+                    OutLocsPtr, InLocsPtr, 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[2][0].second.Kind, DbgValue::Def);
+  EXPECT_EQ(Output[2][0].second.ID, LiveInRsp);
+  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[1].Vars.insert({Var, DbgValue(EmptyProps, DbgValue::Undef)});
+  buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
+                    OutLocsPtr, InLocsPtr, VLocs);
+  EXPECT_EQ(Output[0].size(), 0ul);
+  EXPECT_EQ(Output[1].size(), 0ul);
+  EXPECT_EQ(Output[2].size(), 0ul);
+  ClearOutputs();
+  VLocs[0].Vars.clear();
+  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)});
+  buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
+                    OutLocsPtr, InLocsPtr, 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[2][0].second.Kind, DbgValue::Def);
+  EXPECT_EQ(Output[2][0].second.ID, LiveInRsp);
+  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)});
+  buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
+                    OutLocsPtr, InLocsPtr, 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);
+  ClearOutputs();
+  VLocs[0].Vars.clear();
+  VLocs[1].Vars.clear();
+
+  // Install a completely unrelated PHI value, that we should not join on. Try
+  // with unrelated assign in loop block again.
+  InLocs[1][0] = RspPHIInBlk1;
+  OutLocs[1][0] = RspDefInBlk1;
+  VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  VLocs[1].Vars.insert({Var, DbgValue(LiveInRax, EmptyProps, DbgValue::Def)});
+  buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
+                    OutLocsPtr, InLocsPtr, 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);
+  ClearOutputs();
+  VLocs[0].Vars.clear();
+  VLocs[1].Vars.clear();
+
+  // Now, if we assign RspDefInBlk1 in the loop block, we should be able to
+  // find the appropriate PHI.
+  InLocs[1][0] = RspPHIInBlk1;
+  OutLocs[1][0] = RspDefInBlk1;
+  VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  VLocs[1].Vars.insert({Var, DbgValue(RspDefInBlk1, EmptyProps, DbgValue::Def)});
+  buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
+                    OutLocsPtr, InLocsPtr, 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[2][0].second.Kind, DbgValue::Def);
+  EXPECT_EQ(Output[2][0].second.ID, RspDefInBlk1);
+  ClearOutputs();
+  VLocs[0].Vars.clear();
+  VLocs[1].Vars.clear();
+
+  // If the PHI happens in a 
diff erent location, the live-in should happen
+  // there.
+  InLocs[1][0] = LiveInRsp;
+  OutLocs[1][0] = LiveInRsp;
+  InLocs[1][1] = RaxPHIInBlk1;
+  OutLocs[1][1] = RspDefInBlk1;
+  VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  VLocs[1].Vars.insert({Var, DbgValue(RspDefInBlk1, EmptyProps, DbgValue::Def)});
+  buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
+                    OutLocsPtr, InLocsPtr, 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[2][0].second.Kind, DbgValue::Def);
+  EXPECT_EQ(Output[2][0].second.ID, RspDefInBlk1);
+  ClearOutputs();
+  VLocs[0].Vars.clear();
+  VLocs[1].Vars.clear();
+
+  // The PHI happening in both places should be handled too. Exactly where
+  // isn't important, but if the location picked changes, this test will let
+  // you know.
+  InLocs[1][0] = RaxPHIInBlk1;
+  OutLocs[1][0] = RspDefInBlk1;
+  InLocs[1][1] = RaxPHIInBlk1;
+  OutLocs[1][1] = RspDefInBlk1;
+  VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  VLocs[1].Vars.insert({Var, DbgValue(RspDefInBlk1, EmptyProps, DbgValue::Def)});
+  buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
+                    OutLocsPtr, InLocsPtr, 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);
+  // Today, the first register is picked.
+  EXPECT_EQ(Output[1][0].second.ID, RspPHIInBlk1);
+  EXPECT_EQ(Output[2][0].second.Kind, DbgValue::Def);
+  EXPECT_EQ(Output[2][0].second.ID, RspDefInBlk1);
+  ClearOutputs();
+  VLocs[0].Vars.clear();
+  VLocs[1].Vars.clear();
+
+  // If the loop block looked a bit like this:
+  //    %0 = PHI %1, %2
+  //    [...]
+  //    DBG_VALUE %0
+  // Then with instr-ref it becomes:
+  //    DBG_PHI %0
+  //    [...]
+  //    DBG_INSTR_REF
+  // And we would be feeding a machine PHI-value back around the loop. However:
+  // this does not mean we can eliminate the variable value PHI and use the
+  // variable value from the entry block: they are distinct values that must be
+  // joined at some location by the control flow.
+  // [This test input would never occur naturally, the machine-PHI would be
+  //  eliminated]
+  InLocs[1][0] = RspPHIInBlk1;
+  OutLocs[1][0] = RspPHIInBlk1;
+  InLocs[1][1] = LiveInRax;
+  OutLocs[1][1] = LiveInRax;
+  VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  VLocs[1].Vars.insert({Var, DbgValue(RspPHIInBlk1, EmptyProps, DbgValue::Def)});
+  buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
+                    OutLocsPtr, InLocsPtr, 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[2][0].second.Kind, DbgValue::Def);
+  EXPECT_EQ(Output[2][0].second.ID, RspPHIInBlk1);
+  ClearOutputs();
+  VLocs[0].Vars.clear();
+  VLocs[1].Vars.clear();
+
+  // Test that we can eliminate PHIs. A PHI will be placed at the loop head
+  // because there's a def in in.
+  InLocs[1][0] = LiveInRsp;
+  OutLocs[1][0] = LiveInRsp;
+  VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  VLocs[1].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
+                    OutLocsPtr, InLocsPtr, 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[2][0].second.Kind, DbgValue::Def);
+  EXPECT_EQ(Output[2][0].second.ID, LiveInRsp);
+  ClearOutputs();
+  VLocs[0].Vars.clear();
+  VLocs[1].Vars.clear();
+}
+
+// test phi elimination with the nested situation
+TEST_F(InstrRefLDVTest, VLocNestedLoop) {
+  //    entry
+  //     |
+  //    loop1
+  //     ^\
+  //     | \    /-\
+  //     |  loop2  |
+  //     |  /   \-/
+  //     ^ /
+  //     join
+  //     |
+  //     ret
+  setupNestedLoops();
+
+  ASSERT_TRUE(MTracker->getNumLocs() == 1);
+  LocIdx RspLoc(0);
+  Register RAX = getRegByName("RAX");
+  LocIdx RaxLoc = MTracker->lookupOrTrackRegister(RAX);
+
+  unsigned EntryBlk = 0, Loop1Blk = 1, Loop2Blk = 2;
+
+  ValueIDNum LiveInRsp = ValueIDNum(EntryBlk, 0, RspLoc);
+  ValueIDNum LiveInRax = ValueIDNum(EntryBlk, 0, RaxLoc);
+  ValueIDNum RspPHIInBlk1 = ValueIDNum(Loop1Blk, 0, RspLoc);
+  ValueIDNum RspPHIInBlk2 = ValueIDNum(Loop2Blk, 0, RspLoc);
+  ValueIDNum RspDefInBlk2 = ValueIDNum(Loop2Blk, 1, RspLoc);
+
+  ValueIDNum InLocs[5][2], OutLocs[5][2];
+  ValueIDNum *InLocsPtr[5] = {InLocs[0], InLocs[1], InLocs[2], InLocs[3], InLocs[4]};
+  ValueIDNum *OutLocsPtr[5] = {OutLocs[0], OutLocs[1], OutLocs[2], OutLocs[3], OutLocs[4]};
+
+  initValueArray(InLocsPtr, 5, 2);
+  initValueArray(OutLocsPtr, 5, 2);
+
+  DebugVariable Var(FuncVariable, None, nullptr);
+  DbgValueProperties EmptyProps(EmptyExpr, false);
+
+  SmallSet<DebugVariable, 4> AllVars;
+  AllVars.insert(Var);
+
+  SmallPtrSet<MachineBasicBlock *, 5> AssignBlocks;
+  AssignBlocks.insert(MBB0);
+  AssignBlocks.insert(MBB1);
+  AssignBlocks.insert(MBB2);
+  AssignBlocks.insert(MBB3);
+  AssignBlocks.insert(MBB4);
+
+  SmallVector<VLocTracker, 5> VLocs;
+  VLocs.resize(5);
+
+  InstrRefBasedLDV::LiveInsT Output;
+
+  // Start off with LiveInRsp in every location.
+  for (unsigned int I = 0; I < 5; ++I) {
+    InLocs[I][0] = InLocs[I][1] = LiveInRsp;
+    OutLocs[I][0] = OutLocs[I][1] = LiveInRsp;
+  }
+
+  auto ClearOutputs = [&]() {
+    for (auto &Elem : Output)
+      Elem.clear();
+  };
+  Output.resize(5);
+
+  // A dominating assign should propagate to all blocks.
+  VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
+                    OutLocsPtr, InLocsPtr, VLocs);
+  EXPECT_EQ(Output[0].size(), 0ul);
+  ASSERT_EQ(Output[1].size(), 1ul);
+  ASSERT_EQ(Output[2].size(), 1ul);
+  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[2][0].second.Kind, DbgValue::Def);
+  EXPECT_EQ(Output[2][0].second.ID, LiveInRsp);
+  EXPECT_EQ(Output[3][0].second.Kind, DbgValue::Def);
+  EXPECT_EQ(Output[3][0].second.ID, LiveInRsp);
+  EXPECT_EQ(Output[4][0].second.Kind, DbgValue::Def);
+  EXPECT_EQ(Output[4][0].second.ID, LiveInRsp);
+  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)});
+  buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
+                    OutLocsPtr, InLocsPtr, VLocs);
+  EXPECT_EQ(Output[0].size(), 0ul);
+  EXPECT_EQ(Output[1].size(), 0ul);
+  EXPECT_EQ(Output[2].size(), 0ul);
+  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[4][0].second.Kind, DbgValue::Def);
+  EXPECT_EQ(Output[4][0].second.ID, LiveInRax);
+  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)});
+  buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
+                    OutLocsPtr, InLocsPtr, VLocs);
+  EXPECT_EQ(Output[0].size(), 0ul);
+  EXPECT_EQ(Output[1].size(), 0ul);
+  EXPECT_EQ(Output[2].size(), 0ul);
+  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[4][0].second.Kind, DbgValue::Def);
+  EXPECT_EQ(Output[4][0].second.ID, LiveInRax);
+  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)});
+  buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
+                    OutLocsPtr, InLocsPtr, VLocs);
+  EXPECT_EQ(Output[0].size(), 0ul);
+  EXPECT_EQ(Output[1].size(), 0ul);
+  EXPECT_EQ(Output[2].size(), 0ul);
+  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);
+  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)});
+  buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
+                    OutLocsPtr, InLocsPtr, VLocs);
+  EXPECT_EQ(Output[0].size(), 0ul);
+  EXPECT_EQ(Output[1].size(), 0ul);
+  ASSERT_EQ(Output[2].size(), 1ul);
+  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[3][0].second.Kind, DbgValue::Def);
+  EXPECT_EQ(Output[3][0].second.ID, LiveInRax);
+  EXPECT_EQ(Output[4][0].second.Kind, DbgValue::Def);
+  EXPECT_EQ(Output[4][0].second.ID, LiveInRax);
+  ClearOutputs();
+  VLocs[0].Vars.clear();
+  VLocs[1].Vars.clear();
+
+  // 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)});
+  buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
+                    OutLocsPtr, InLocsPtr, VLocs);
+  EXPECT_EQ(Output[0].size(), 0ul);
+  EXPECT_EQ(Output[1].size(), 1ul);
+  EXPECT_EQ(Output[2].size(), 1ul);
+  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[2][0].second.Kind, DbgValue::Def);
+  EXPECT_EQ(Output[2][0].second.ID, LiveInRsp);
+  EXPECT_EQ(Output[3][0].second.Kind, DbgValue::Def);
+  EXPECT_EQ(Output[3][0].second.ID, LiveInRsp);
+  EXPECT_EQ(Output[4][0].second.Kind, DbgValue::Def);
+  EXPECT_EQ(Output[4][0].second.ID, LiveInRsp);
+  ClearOutputs();
+  VLocs[0].Vars.clear();
+  VLocs[2].Vars.clear();
+
+  // If we have an assignment in the inner loop, and a PHI for it at the inner
+  // loop head, we could find a live-in location for the inner loop. But because
+  // the outer loop has no PHI, we can't find a variable value for outer loop
+  // head, so can't have a live-in value for the inner loop head.
+  InLocs[2][0] = RspPHIInBlk2;
+  OutLocs[2][0] = LiveInRax;
+  // NB: all other machine locations are LiveInRsp, disallowing a PHI in block
+  // 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)});
+  buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
+                    OutLocsPtr, InLocsPtr, VLocs);
+  EXPECT_EQ(Output[0].size(), 0ul);
+  EXPECT_EQ(Output[1].size(), 0ul);
+  EXPECT_EQ(Output[2].size(), 0ul);
+  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[4][0].second.Kind, DbgValue::Def);
+  EXPECT_EQ(Output[4][0].second.ID, LiveInRax);
+  ClearOutputs();
+  VLocs[0].Vars.clear();
+  VLocs[2].Vars.clear();
+
+  // Have an assignment in inner loop that can have a PHI resolved; and add a
+  // machine value PHI to the outer loop head, so that we can find a location
+  // all the way through the function.
+  InLocs[1][0] = RspPHIInBlk1;
+  OutLocs[1][0] = RspPHIInBlk1;
+  InLocs[2][0] = RspPHIInBlk2;
+  OutLocs[2][0] = RspDefInBlk2;
+  InLocs[3][0] = RspDefInBlk2;
+  OutLocs[3][0] = RspDefInBlk2;
+  VLocs[0].Vars.insert({Var, DbgValue(LiveInRsp, EmptyProps, DbgValue::Def)});
+  VLocs[2].Vars.insert({Var, DbgValue(RspDefInBlk2, EmptyProps, DbgValue::Def)});
+  buildVLocValueMap(OutermostLoc, AllVars, AssignBlocks, Output,
+                    OutLocsPtr, InLocsPtr, VLocs);
+  EXPECT_EQ(Output[0].size(), 0ul);
+  ASSERT_EQ(Output[1].size(), 1ul);
+  ASSERT_EQ(Output[2].size(), 1ul);
+  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[2][0].second.Kind, DbgValue::Def);
+  EXPECT_EQ(Output[2][0].second.ID, RspPHIInBlk2);
+  EXPECT_EQ(Output[3][0].second.Kind, DbgValue::Def);
+  EXPECT_EQ(Output[3][0].second.ID, RspDefInBlk2);
+  EXPECT_EQ(Output[4][0].second.Kind, DbgValue::Def);
+  EXPECT_EQ(Output[4][0].second.ID, RspDefInBlk2);
+  ClearOutputs();
+  VLocs[0].Vars.clear();
+  VLocs[2].Vars.clear();
+}
+


        


More information about the llvm-commits mailing list