[llvm] a3936a6 - [DebugInfo][InstrRef] Use PHI placement utilities for machine locations

Jeremy Morse via llvm-commits llvm-commits at lists.llvm.org
Wed Oct 13 04:51:26 PDT 2021


Author: Jeremy Morse
Date: 2021-10-13T12:49:04+01:00
New Revision: a3936a6c19c7914b86ee7a30ce95439ffb72b9c9

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

LOG: [DebugInfo][InstrRef] Use PHI placement utilities for machine locations

InstrRefBasedLDV used to try and determine which values are in which
registers using a lattice approach; however this is hard to understand, and
broken in various ways. This patch replaces that approach with a standard
SSA approach using existing LLVM utilities. PHIs are placed at dominance
frontiers; value propagation then eliminates un-necessary PHIs.

This patch also adds a bunch of unit tests that should cover many of the
weirder forms of control flow.

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

Added: 
    

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

Removed: 
    


################################################################################
diff  --git a/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp b/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp
index a790b91394697..904b7fd9ba871 100644
--- a/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp
+++ b/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp
@@ -11,114 +11,48 @@
 /// LiveDebugValues.cpp and VarLocBasedImpl.cpp for more information.
 ///
 /// This pass propagates variable locations between basic blocks, resolving
-/// control flow conflicts between them. The problem is much like SSA
-/// construction, where each DBG_VALUE instruction assigns the *value* that
-/// a variable has, and every instruction where the variable is in scope uses
-/// that variable. The resulting map of instruction-to-value is then translated
-/// into a register (or spill) location for each variable over each instruction.
+/// control flow conflicts between them. The problem is SSA construction, where
+/// each debug instruction assigns the *value* that a variable has, and every
+/// instruction where the variable is in scope uses that variable. The resulting
+/// map of instruction-to-value is then translated into a register (or spill)
+/// location for each variable over each instruction.
 ///
-/// This pass determines which DBG_VALUE dominates which instructions, or if
-/// none do, where values must be merged (like PHI nodes). The added
-/// complication is that because codegen has already finished, a PHI node may
-/// be needed for a variable location to be correct, but no register or spill
-/// slot merges the necessary values. In these circumstances, the variable
-/// location is dropped.
+/// The primary 
diff erence from normal SSA construction is that we cannot
+/// _create_ PHI values that contain variable values. CodeGen has already
+/// completed, and we can't alter it just to make debug-info complete. Thus:
+/// we can identify function positions where we would like a PHI value for a
+/// variable, but must search the MachineFunction to see whether such a PHI is
+/// available. If no such PHI exists, the variable location must be dropped.
 ///
-/// What makes this analysis non-trivial is loops: we cannot tell in advance
-/// whether a variable location is live throughout a loop, or whether its
-/// location is clobbered (or redefined by another DBG_VALUE), without
-/// exploring all the way through.
-///
-/// To make this simpler we perform two kinds of analysis. First, we identify
+/// To achieve this, we perform two kinds of analysis. First, we identify
 /// every value defined by every instruction (ignoring those that only move
-/// another value), then compute a map of which values are available for each
-/// instruction. This is stronger than a reaching-def analysis, as we create
-/// PHI values where other values merge.
-///
-/// Secondly, for each variable, we effectively re-construct SSA using each
-/// DBG_VALUE as a def. The DBG_VALUEs read a value-number computed by the
-/// first analysis from the location they refer to. We can then compute the
-/// dominance frontiers of where a variable has a value, and create PHI nodes
-/// where they merge.
-/// This isn't precisely SSA-construction though, because the function shape
-/// is pre-defined. If a variable location requires a PHI node, but no
-/// PHI for the relevant values is present in the function (as computed by the
-/// first analysis), the location must be dropped.
-///
-/// Once both are complete, we can pass back over all instructions knowing:
-///  * What _value_ each variable should contain, either defined by an
-///    instruction or where control flow merges
-///  * What the location of that value is (if any).
-/// Allowing us to create appropriate live-in DBG_VALUEs, and DBG_VALUEs when
-/// a value moves location. After this pass runs, all variable locations within
-/// a block should be specified by DBG_VALUEs within that block, allowing
-/// DbgEntityHistoryCalculator to focus on individual blocks.
-///
-/// This pass is able to go fast because the size of the first
-/// reaching-definition analysis is proportional to the working-set size of
-/// the function, which the compiler tries to keep small. (It's also
-/// proportional to the number of blocks). Additionally, we repeatedly perform
-/// the second reaching-definition analysis with only the variables and blocks
-/// in a single lexical scope, exploiting their locality.
-///
-/// Determining where PHIs happen is trickier with this approach, and it comes
-/// to a head in the major problem for LiveDebugValues: is a value live-through
-/// a loop, or not? Your garden-variety dataflow analysis aims to build a set of
-/// facts about a function, however this analysis needs to generate new value
-/// numbers at joins.
-///
-/// To do this, consider a lattice of all definition values, from instructions
-/// and from PHIs. Each PHI is characterised by the RPO number of the block it
-/// occurs in. Each value pair A, B can be ordered by RPO(A) < RPO(B):
-/// with non-PHI values at the top, and any PHI value in the last block (by RPO
-/// order) at the bottom.
-///
-/// (Awkwardly: lower-down-the _lattice_ means a greater RPO _number_. Below,
-/// "rank" always refers to the former).
-///
-/// At any join, for each register, we consider:
-///  * All incoming values, and
-///  * The PREVIOUS live-in value at this join.
-/// If all incoming values agree: that's the live-in value. If they do not, the
-/// incoming values are ranked according to the partial order, and the NEXT
-/// LOWEST rank after the PREVIOUS live-in value is picked (multiple values of
-/// the same rank are ignored as conflicting). If there are no candidate values,
-/// or if the rank of the live-in would be lower than the rank of the current
-/// blocks PHIs, create a new PHI value.
+/// another value), then re-compute an SSA-form representation of the
+/// MachineFunction, using value propagation to eliminate any un-necessary
+/// PHI values. This gives us a map of every value computed in the function,
+/// and its location within the register file / stack.
 ///
-/// Intuitively: if it's not immediately obvious what value a join should result
-/// in, we iteratively descend from instruction-definitions down through PHI
-/// values, getting closer to the current block each time. If the current block
-/// is a loop head, this ordering is effectively searching outer levels of
-/// loops, to find a value that's live-through the current loop.
+/// Secondly, for each variable we perform the same analysis, where each debug
+/// instruction is considered a def, and every instruction where the variable
+/// is in lexical scope as a use. Value propagation is used again to eliminate
+/// any un-necessary PHIs. This gives us a map of each variable to the value
+/// it should have in a block.
 ///
-/// If there is no value that's live-through this loop, a PHI is created for
-/// this location instead. We can't use a lower-ranked PHI because by definition
-/// it doesn't dominate the current block. We can't create a PHI value any
-/// earlier, because we risk creating a PHI value at a location where values do
-/// not in fact merge, thus misrepresenting the truth, and not making the true
-/// live-through value for variable locations.
+/// Once both are complete, we have two maps for each block:
+///  * Variables to the values they should have,
+///  * Values to the register / spill slot they are located in.
+/// After which we can marry-up variable values with a location, and emit
+/// DBG_VALUE instructions specifying those locations. Variable locations may
+/// be dropped in this process due to the desired variable value not being
+/// resident in any machine location, or because there is no PHI value in any
+/// location that accurately represents the desired value.  The building of
+/// location lists for each block is left to DbgEntityHistoryCalculator.
 ///
-/// This algorithm applies to both calculating the availability of values in
-/// the first analysis, and the location of variables in the second. However
-/// for the second we add an extra dimension of pain: creating a variable
-/// location PHI is only valid if, for each incoming edge,
-///  * There is a value for the variable on the incoming edge, and
-///  * All the edges have that value in the same register.
-/// Or put another way: we can only create a variable-location PHI if there is
-/// a matching machine-location PHI, each input to which is the variables value
-/// in the predecessor block.
-///
-/// To accommodate this 
diff erence, each point on the lattice is split in
-/// two: a "proposed" PHI and "definite" PHI. Any PHI that can immediately
-/// have a location determined are "definite" PHIs, and no further work is
-/// needed. Otherwise, a location that all non-backedge predecessors agree
-/// on is picked and propagated as a "proposed" PHI value. If that PHI value
-/// is truly live-through, it'll appear on the loop backedges on the next
-/// dataflow iteration, after which the block live-in moves to be a "definite"
-/// PHI. If it's not truly live-through, the variable value will be downgraded
-/// further as we explore the lattice, or remains "proposed" and is considered
-/// invalid once dataflow completes.
+/// This pass is kept efficient because the size of the first SSA problem
+/// is proportional to the working-set size of the function, which the compiler
+/// tries to keep small. (It's also proportional to the number of blocks).
+/// Additionally, we repeatedly perform the second SSA problem analysis with
+/// only the variables and blocks in a single lexical scope, exploiting their
+/// locality.
 ///
 /// ### Terminology
 ///
@@ -128,15 +62,13 @@
 /// contain the appropriate variable value. A value that is a PHI node is
 /// occasionally called an mphi.
 ///
-/// The first dataflow problem is the "machine value location" problem,
+/// The first SSA problem is the "machine value location" problem,
 /// because we're determining which machine locations contain which values.
 /// The "locations" are constant: what's unknown is what value they contain.
 ///
-/// The second dataflow problem (the one for variables) is the "variable value
+/// The second SSA problem (the one for variables) is the "variable value
 /// problem", because it's determining what values a variable has, rather than
-/// what location those values are placed in. Unfortunately, it's not that
-/// simple, because producing a PHI value always involves picking a location.
-/// This is an imperfection that we just have to accept, at least for now.
+/// what location those values are placed in.
 ///
 /// TODO:
 ///   Overlapping fragments
@@ -153,8 +85,10 @@
 #include "llvm/ADT/SmallSet.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/Statistic.h"
+#include "llvm/Analysis/IteratedDominanceFrontier.h"
 #include "llvm/CodeGen/LexicalScopes.h"
 #include "llvm/CodeGen/MachineBasicBlock.h"
+#include "llvm/CodeGen/MachineDominators.h"
 #include "llvm/CodeGen/MachineFrameInfo.h"
 #include "llvm/CodeGen/MachineFunction.h"
 #include "llvm/CodeGen/MachineFunctionPass.h"
@@ -1786,23 +1720,20 @@ void InstrRefBasedLDV::produceMLocTransferFunction(
   }
 }
 
-std::tuple<bool, bool>
-InstrRefBasedLDV::mlocJoin(MachineBasicBlock &MBB,
-                           SmallPtrSet<const MachineBasicBlock *, 16> &Visited,
-                           ValueIDNum **OutLocs, ValueIDNum *InLocs) {
+bool InstrRefBasedLDV::mlocJoin(
+    MachineBasicBlock &MBB, SmallPtrSet<const MachineBasicBlock *, 16> &Visited,
+    ValueIDNum **OutLocs, ValueIDNum *InLocs) {
   LLVM_DEBUG(dbgs() << "join MBB: " << MBB.getNumber() << "\n");
   bool Changed = false;
-  bool DowngradeOccurred = false;
 
-  // Collect predecessors that have been visited. Anything that hasn't been
-  // visited yet is a backedge on the first iteration, and the meet of it's
-  // lattice value for all locations will be unaffected.
+  // Handle value-propagation when control flow merges on entry to a block. For
+  // any location without a PHI already placed, the location has the same value
+  // as its predecessors. If a PHI is placed, test to see whether it's now a
+  // redundant PHI that we can eliminate.
+
   SmallVector<const MachineBasicBlock *, 8> BlockOrders;
-  for (auto Pred : MBB.predecessors()) {
-    if (Visited.count(Pred)) {
-      BlockOrders.push_back(Pred);
-    }
-  }
+  for (auto Pred : MBB.predecessors())
+    BlockOrders.push_back(Pred);
 
   // Visit predecessors in RPOT order.
   auto Cmp = [&](const MachineBasicBlock *A, const MachineBasicBlock *B) {
@@ -1812,83 +1743,60 @@ InstrRefBasedLDV::mlocJoin(MachineBasicBlock &MBB,
 
   // Skip entry block.
   if (BlockOrders.size() == 0)
-    return std::tuple<bool, bool>(false, false);
+    return false;
 
-  // Step through all machine locations, then look at each predecessor and
-  // detect disagreements.
-  unsigned ThisBlockRPO = BBToOrder.find(&MBB)->second;
+  // Step through all machine locations, look at each predecessor and test
+  // whether we can eliminate redundant PHIs.
   for (auto Location : MTracker->locations()) {
     LocIdx Idx = Location.Idx;
+
     // Pick out the first predecessors live-out value for this location. It's
-    // guaranteed to be not a backedge, as we order by RPO.
-    ValueIDNum BaseVal = OutLocs[BlockOrders[0]->getNumber()][Idx.asU64()];
+    // guaranteed to not be a backedge, as we order by RPO.
+    ValueIDNum FirstVal = OutLocs[BlockOrders[0]->getNumber()][Idx.asU64()];
+
+    // If we've already eliminated a PHI here, do no further checking, just
+    // propagate the first live-in value into this block.
+    if (InLocs[Idx.asU64()] != ValueIDNum(MBB.getNumber(), 0, Idx)) {
+      if (InLocs[Idx.asU64()] != FirstVal) {
+        InLocs[Idx.asU64()] = FirstVal;
+        Changed |= true;
+      }
+      continue;
+    }
 
-    // Some flags for whether there's a disagreement, and whether it's a
-    // disagreement with a backedge or not.
+    // We're now examining a PHI to see whether it's un-necessary. Loop around
+    // the other live-in values and test whether they're all the same.
     bool Disagree = false;
-    bool NonBackEdgeDisagree = false;
-
-    // Loop around everything that wasn't 'base'.
     for (unsigned int I = 1; I < BlockOrders.size(); ++I) {
-      auto *MBB = BlockOrders[I];
-      if (BaseVal != OutLocs[MBB->getNumber()][Idx.asU64()]) {
-        // Live-out of a predecessor disagrees with the first predecessor.
-        Disagree = true;
+      const MachineBasicBlock *PredMBB = BlockOrders[I];
+      const ValueIDNum &PredLiveOut =
+          OutLocs[PredMBB->getNumber()][Idx.asU64()];
 
-        // Test whether it's a disagreemnt in the backedges or not.
-        if (BBToOrder.find(MBB)->second < ThisBlockRPO) // might be self b/e
-          NonBackEdgeDisagree = true;
-      }
-    }
+      // Incoming values agree, continue trying to eliminate this PHI.
+      if (FirstVal == PredLiveOut)
+        continue;
 
-    bool OverRide = false;
-    if (Disagree && !NonBackEdgeDisagree) {
-      // Only the backedges disagree. Consider demoting the livein
-      // lattice value, as per the file level comment. The value we consider
-      // demoting to is the value that the non-backedge predecessors agree on.
-      // The order of values is that non-PHIs are \top, a PHI at this block
-      // \bot, and phis between the two are ordered by their RPO number.
-      // If there's no agreement, or we've already demoted to this PHI value
-      // before, replace with a PHI value at this block.
-
-      // Calculate order numbers: zero means normal def, nonzero means RPO
-      // number.
-      unsigned BaseBlockRPONum = BBNumToRPO[BaseVal.getBlock()] + 1;
-      if (!BaseVal.isPHI())
-        BaseBlockRPONum = 0;
-
-      ValueIDNum &InLocID = InLocs[Idx.asU64()];
-      unsigned InLocRPONum = BBNumToRPO[InLocID.getBlock()] + 1;
-      if (!InLocID.isPHI())
-        InLocRPONum = 0;
-
-      // Should we ignore the disagreeing backedges, and override with the
-      // value the other predecessors agree on (in "base")?
-      unsigned ThisBlockRPONum = BBNumToRPO[MBB.getNumber()] + 1;
-      if (BaseBlockRPONum > InLocRPONum && BaseBlockRPONum < ThisBlockRPONum) {
-        // Override.
-        OverRide = true;
-        DowngradeOccurred = true;
-      }
+      // We can also accept a PHI value that feeds back into itself.
+      if (PredLiveOut == ValueIDNum(MBB.getNumber(), 0, Idx))
+        continue;
+
+      // Live-out of a predecessor disagrees with the first predecessor.
+      Disagree = true;
     }
-    // else: if we disagree in the non-backedges, then this is definitely
-    // a control flow merge where 
diff erent values merge. Make it a PHI.
 
-    // Generate a phi...
-    ValueIDNum PHI = {(uint64_t)MBB.getNumber(), 0, Idx};
-    ValueIDNum NewVal = (Disagree && !OverRide) ? PHI : BaseVal;
-    if (InLocs[Idx.asU64()] != NewVal) {
+    // No disagreement? No PHI. Otherwise, leave the PHI in live-ins.
+    if (!Disagree) {
+      InLocs[Idx.asU64()] = FirstVal;
       Changed |= true;
-      InLocs[Idx.asU64()] = NewVal;
     }
   }
 
   // TODO: Reimplement NumInserted and NumRemoved.
-  return std::tuple<bool, bool>(Changed, DowngradeOccurred);
+  return Changed;
 }
 
-void InstrRefBasedLDV::mlocDataflow(
-    ValueIDNum **MInLocs, ValueIDNum **MOutLocs,
+void InstrRefBasedLDV::buildMLocValueMap(
+    MachineFunction &MF, ValueIDNum **MInLocs, ValueIDNum **MOutLocs,
     SmallVectorImpl<MLocTransferMap> &MLocTransfer) {
   std::priority_queue<unsigned int, std::vector<unsigned int>,
                       std::greater<unsigned int>>
@@ -1899,20 +1807,59 @@ void InstrRefBasedLDV::mlocDataflow(
   // but this is probably not worth it.
   SmallPtrSet<MachineBasicBlock *, 16> OnPending, OnWorklist;
 
-  // Initialize worklist with every block to be visited.
+  // Initialize worklist with every block to be visited. Also produce list of
+  // all blocks.
+  SmallPtrSet<MachineBasicBlock *, 32> AllBlocks;
   for (unsigned int I = 0; I < BBToOrder.size(); ++I) {
     Worklist.push(I);
     OnWorklist.insert(OrderToBB[I]);
+    AllBlocks.insert(OrderToBB[I]);
   }
 
+  // Initialize entry block to PHIs. These represent arguments.
+  for (auto Location : MTracker->locations())
+    MInLocs[0][Location.Idx.asU64()] = ValueIDNum(0, 0, Location.Idx);
+
   MTracker->reset();
 
-  // Set inlocs for entry block -- each as a PHI at the entry block. Represents
-  // the incoming value to the function.
-  MTracker->setMPhis(0);
-  for (auto Location : MTracker->locations())
-    MInLocs[0][Location.Idx.asU64()] = Location.Value;
+  // Start by placing PHIs, using the usual SSA constructor algorithm. Consider
+  // any machine-location that isn't live-through a block to be def'd in that
+  // block.
+  for (auto Location : MTracker->locations()) {
+    // Collect the set of defs.
+    SmallPtrSet<MachineBasicBlock *, 32> DefBlocks;
+    for (unsigned int I = 0; I < OrderToBB.size(); ++I) {
+      MachineBasicBlock *MBB = OrderToBB[I];
+      const auto &TransferFunc = MLocTransfer[MBB->getNumber()];
+      if (TransferFunc.find(Location.Idx) != TransferFunc.end())
+        DefBlocks.insert(MBB);
+    }
+
+    // The entry block defs the location too: it's the live-in / argument value.
+    // Only insert if there are other defs though; everything is trivially live
+    // through otherwise.
+    if (!DefBlocks.empty())
+      DefBlocks.insert(&*MF.begin());
 
+    // Ask the SSA construction algorithm where we should put PHIs.
+    SmallVector<MachineBasicBlock *, 32> PHIBlocks;
+    BlockPHIPlacement(AllBlocks, DefBlocks, PHIBlocks);
+
+    // Install those PHI values into the live-in value array.
+    for (const MachineBasicBlock *MBB : PHIBlocks) {
+      MInLocs[MBB->getNumber()][Location.Idx.asU64()] =
+          ValueIDNum(MBB->getNumber(), 0, Location.Idx);
+    }
+  }
+
+  // Propagate values to eliminate redundant PHIs. At the same time, this
+  // produces the table of Block x Location => Value for the entry to each
+  // block.
+  // The kind of PHIs we can eliminate are, for example, where one path in a
+  // conditional spills and restores a register, and the register still has
+  // the same value once control flow joins, unbeknowns to the PHI placement
+  // code. Propagating values allows us to identify such un-necessary PHIs and
+  // remove them.
   SmallPtrSet<const MachineBasicBlock *, 16> Visited;
   while (!Worklist.empty() || !Pending.empty()) {
     // Vector for storing the evaluated block transfer function.
@@ -1924,16 +1871,10 @@ void InstrRefBasedLDV::mlocDataflow(
       Worklist.pop();
 
       // Join the values in all predecessor blocks.
-      bool InLocsChanged, DowngradeOccurred;
-      std::tie(InLocsChanged, DowngradeOccurred) =
-          mlocJoin(*MBB, Visited, MOutLocs, MInLocs[CurBB]);
+      bool InLocsChanged;
+      InLocsChanged = mlocJoin(*MBB, Visited, MOutLocs, MInLocs[CurBB]);
       InLocsChanged |= Visited.insert(MBB).second;
 
-      // If a downgrade occurred, book us in for re-examination on the next
-      // iteration.
-      if (DowngradeOccurred && OnPending.insert(MBB).second)
-        Pending.push(BBToOrder[MBB]);
-
       // Don't examine transfer function if we've visited this loc at least
       // once, and inlocs haven't changed.
       if (!InLocsChanged)
@@ -1978,8 +1919,8 @@ void InstrRefBasedLDV::mlocDataflow(
         continue;
 
       // All successors should be visited: put any back-edges on the pending
-      // list for the next dataflow iteration, and any other successors to be
-      // visited this iteration, if they're not going to be already.
+      // list for the next pass-through, and any other successors to be
+      // visited this pass, if they're not going to be already.
       for (auto s : MBB->successors()) {
         // Does branching to this successor represent a back-edge?
         if (BBToOrder[s] > BBToOrder[MBB]) {
@@ -2002,8 +1943,55 @@ void InstrRefBasedLDV::mlocDataflow(
     assert(Pending.empty() && "Pending should be empty");
   }
 
-  // Once all the live-ins don't change on mlocJoin(), we've reached a
-  // fixedpoint.
+  // Once all the live-ins don't change on mlocJoin(), we've eliminated all
+  // redundant PHIs.
+}
+
+// Boilerplate for feeding MachineBasicBlocks into IDF calculator. Provide
+// template specialisations for graph traits and a successor enumerator.
+namespace llvm {
+template <> struct GraphTraits<MachineBasicBlock> {
+  using NodeRef = MachineBasicBlock *;
+  using ChildIteratorType = MachineBasicBlock::succ_iterator;
+
+  static NodeRef getEntryNode(MachineBasicBlock *BB) { return BB; }
+  static ChildIteratorType child_begin(NodeRef N) { return N->succ_begin(); }
+  static ChildIteratorType child_end(NodeRef N) { return N->succ_end(); }
+};
+
+template <> struct GraphTraits<const MachineBasicBlock> {
+  using NodeRef = const MachineBasicBlock *;
+  using ChildIteratorType = MachineBasicBlock::const_succ_iterator;
+
+  static NodeRef getEntryNode(const MachineBasicBlock *BB) { return BB; }
+  static ChildIteratorType child_begin(NodeRef N) { return N->succ_begin(); }
+  static ChildIteratorType child_end(NodeRef N) { return N->succ_end(); }
+};
+
+using MachineDomTreeBase = DomTreeBase<MachineBasicBlock>::NodeType;
+using MachineDomTreeChildGetter =
+    typename IDFCalculatorDetail::ChildrenGetterTy<MachineDomTreeBase, false>;
+
+template <>
+typename MachineDomTreeChildGetter::ChildrenTy
+MachineDomTreeChildGetter::get(const NodeRef &N) {
+  return {N->succ_begin(), N->succ_end()};
+}
+} // namespace llvm
+
+void InstrRefBasedLDV::BlockPHIPlacement(
+    const SmallPtrSetImpl<MachineBasicBlock *> &AllBlocks,
+    const SmallPtrSetImpl<MachineBasicBlock *> &DefBlocks,
+    SmallVectorImpl<MachineBasicBlock *> &PHIBlocks) {
+  // Apply IDF calculator to the designated set of location defs, storing
+  // required PHIs into PHIBlocks. Uses the dominator tree stored in the
+  // InstrRefBasedLDV object.
+  IDFCalculatorDetail::ChildrenGetterTy<MachineDomTreeBase, false> foo;
+  IDFCalculatorBase<MachineDomTreeBase, false> IDF(DomTree->getBase(), foo);
+
+  IDF.setLiveInBlocks(AllBlocks);
+  IDF.setDefiningBlocks(DefBlocks);
+  IDF.calculate(PHIBlocks);
 }
 
 bool InstrRefBasedLDV::vlocDowngradeLattice(
@@ -2756,7 +2744,9 @@ void InstrRefBasedLDV::initialSetup(MachineFunction &MF) {
 
 /// Calculate the liveness information for the given machine function and
 /// extend ranges across basic blocks.
-bool InstrRefBasedLDV::ExtendRanges(MachineFunction &MF, TargetPassConfig *TPC,
+bool InstrRefBasedLDV::ExtendRanges(MachineFunction &MF,
+                                    MachineDominatorTree *DomTree,
+                                    TargetPassConfig *TPC,
                                     unsigned InputBBLimit,
                                     unsigned InputDbgValLimit) {
   // No subprogram means this function contains no debuginfo.
@@ -2766,6 +2756,7 @@ bool InstrRefBasedLDV::ExtendRanges(MachineFunction &MF, TargetPassConfig *TPC,
   LLVM_DEBUG(dbgs() << "\nDebug Range Extension\n");
   this->TPC = TPC;
 
+  this->DomTree = DomTree;
   TRI = MF.getSubtarget().getRegisterInfo();
   TII = MF.getSubtarget().getInstrInfo();
   TFI = MF.getSubtarget().getFrameLowering();
@@ -2803,6 +2794,7 @@ bool InstrRefBasedLDV::ExtendRanges(MachineFunction &MF, TargetPassConfig *TPC,
   ValueIDNum **MInLocs = new ValueIDNum *[MaxNumBlocks];
   unsigned NumLocs = MTracker->getNumLocs();
   for (int i = 0; i < MaxNumBlocks; ++i) {
+    // These all auto-initialize to ValueIDNum::EmptyValue
     MOutLocs[i] = new ValueIDNum[NumLocs];
     MInLocs[i] = new ValueIDNum[NumLocs];
   }
@@ -2811,7 +2803,7 @@ bool InstrRefBasedLDV::ExtendRanges(MachineFunction &MF, TargetPassConfig *TPC,
   // storing the computed live-ins / live-outs into the array-of-arrays. We use
   // both live-ins and live-outs for decision making in the variable value
   // dataflow problem.
-  mlocDataflow(MInLocs, MOutLocs, MLocTransfer);
+  buildMLocValueMap(MF, MInLocs, MOutLocs, MLocTransfer);
 
   // Patch up debug phi numbers, turning unknown block-live-in values into
   // either live-through machine values, or PHIs.

diff  --git a/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h b/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h
index 665cd58167bb6..722806026ab1a 100644
--- a/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h
+++ b/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h
@@ -581,6 +581,7 @@ class InstrRefBasedLDV : public LDVImpl {
   /// Used as the result type for the variable value dataflow problem.
   using LiveInsT = SmallVector<SmallVector<VarAndLoc, 8>, 8>;
 
+  MachineDominatorTree *DomTree;
   const TargetRegisterInfo *TRI;
   const TargetInstrInfo *TII;
   const TargetFrameLowering *TFI;
@@ -728,8 +729,19 @@ class InstrRefBasedLDV : public LDVImpl {
   /// live-out arrays to the (initialized to zero) multidimensional arrays in
   /// \p MInLocs and \p MOutLocs. The outer dimension is indexed by block
   /// number, the inner by LocIdx.
-  void mlocDataflow(ValueIDNum **MInLocs, ValueIDNum **MOutLocs,
-                    SmallVectorImpl<MLocTransferMap> &MLocTransfer);
+  void buildMLocValueMap(MachineFunction &MF, ValueIDNum **MInLocs,
+                         ValueIDNum **MOutLocs,
+                         SmallVectorImpl<MLocTransferMap> &MLocTransfer);
+
+  /// Calculate the iterated-dominance-frontier for a set of defs, using the
+  /// existing LLVM facilities for this. Works for a single "value" or
+  /// machine/variable location.
+  /// \p AllBlocks Set of blocks where we might consume the value.
+  /// \p DefBlocks Set of blocks where the value/location is defined.
+  /// \p PHIBlocks Output set of blocks where PHIs must be placed.
+  void BlockPHIPlacement(const SmallPtrSetImpl<MachineBasicBlock *> &AllBlocks,
+                         const SmallPtrSetImpl<MachineBasicBlock *> &DefBlocks,
+                         SmallVectorImpl<MachineBasicBlock *> &PHIBlocks);
 
   /// Perform a control flow join (lattice value meet) of the values in machine
   /// locations at \p MBB. Follows the algorithm described in the file-comment,
@@ -738,16 +750,15 @@ class InstrRefBasedLDV : public LDVImpl {
   /// \p InLocs. \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>
-  mlocJoin(MachineBasicBlock &MBB,
-           SmallPtrSet<const MachineBasicBlock *, 16> &Visited,
-           ValueIDNum **OutLocs, ValueIDNum *InLocs);
+  bool mlocJoin(MachineBasicBlock &MBB,
+                SmallPtrSet<const MachineBasicBlock *, 16> &Visited,
+                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
-  /// mlocDataflow) and reads the variable values transfer function from
+  /// 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.
@@ -824,8 +835,9 @@ class InstrRefBasedLDV : public LDVImpl {
   /// RPOT block ordering.
   void initialSetup(MachineFunction &MF);
 
-  bool ExtendRanges(MachineFunction &MF, TargetPassConfig *TPC,
-                    unsigned InputBBLimit, unsigned InputDbgValLimit) override;
+  bool ExtendRanges(MachineFunction &MF, MachineDominatorTree *DomTree,
+                    TargetPassConfig *TPC, unsigned InputBBLimit,
+                    unsigned InputDbgValLimit) override;
 
 public:
   /// Default construct and initialize the pass.

diff  --git a/llvm/lib/CodeGen/LiveDebugValues/LiveDebugValues.cpp b/llvm/lib/CodeGen/LiveDebugValues/LiveDebugValues.cpp
index 07dba303e67b6..a6b2f19a0444a 100644
--- a/llvm/lib/CodeGen/LiveDebugValues/LiveDebugValues.cpp
+++ b/llvm/lib/CodeGen/LiveDebugValues/LiveDebugValues.cpp
@@ -81,6 +81,7 @@ class LiveDebugValues : public MachineFunctionPass {
 private:
   LDVImpl *TheImpl;
   TargetPassConfig *TPC;
+  MachineDominatorTree MDT;
 };
 
 char LiveDebugValues::ID = 0;
@@ -97,12 +98,12 @@ LiveDebugValues::LiveDebugValues() : MachineFunctionPass(ID) {
 }
 
 bool LiveDebugValues::runOnMachineFunction(MachineFunction &MF) {
+  bool InstrRefBased = MF.useDebugInstrRef();
+  // Allow the user to force selection of InstrRef LDV.
+  InstrRefBased |= ForceInstrRefLDV;
+
   if (!TheImpl) {
     TPC = getAnalysisIfAvailable<TargetPassConfig>();
-    bool InstrRefBased = MF.useDebugInstrRef();
-
-    // Allow the user to force selection of InstrRef LDV.
-    InstrRefBased |= ForceInstrRefLDV;
 
     if (InstrRefBased)
       TheImpl = llvm::makeInstrRefBasedLiveDebugValues();
@@ -110,5 +111,12 @@ bool LiveDebugValues::runOnMachineFunction(MachineFunction &MF) {
       TheImpl = llvm::makeVarLocBasedLiveDebugValues();
   }
 
-  return TheImpl->ExtendRanges(MF, TPC, InputBBLimit, InputDbgValueLimit);
+  MachineDominatorTree *DomTree = nullptr;
+  if (InstrRefBased) {
+    DomTree = &MDT;
+    MDT.calculate(MF);
+  }
+
+  return TheImpl->ExtendRanges(MF, DomTree, TPC, InputBBLimit,
+                               InputDbgValueLimit);
 }

diff  --git a/llvm/lib/CodeGen/LiveDebugValues/LiveDebugValues.h b/llvm/lib/CodeGen/LiveDebugValues/LiveDebugValues.h
index e38360b08bafa..a5936c8a96f01 100644
--- a/llvm/lib/CodeGen/LiveDebugValues/LiveDebugValues.h
+++ b/llvm/lib/CodeGen/LiveDebugValues/LiveDebugValues.h
@@ -9,6 +9,7 @@
 #ifndef LLVM_LIB_CODEGEN_LIVEDEBUGVALUES_LIVEDEBUGVALUES_H
 #define LLVM_LIB_CODEGEN_LIVEDEBUGVALUES_LIVEDEBUGVALUES_H
 
+#include "llvm/CodeGen/MachineDominators.h"
 #include "llvm/CodeGen/MachineFunction.h"
 #include "llvm/CodeGen/TargetPassConfig.h"
 
@@ -23,8 +24,8 @@ inline namespace SharedLiveDebugValues {
 // implementation.
 class LDVImpl {
 public:
-  virtual bool ExtendRanges(MachineFunction &MF, TargetPassConfig *TPC,
-                            unsigned InputBBLimit,
+  virtual bool ExtendRanges(MachineFunction &MF, MachineDominatorTree *DomTree,
+                            TargetPassConfig *TPC, unsigned InputBBLimit,
                             unsigned InputDbgValLimit) = 0;
   virtual ~LDVImpl() {}
 };

diff  --git a/llvm/lib/CodeGen/LiveDebugValues/VarLocBasedImpl.cpp b/llvm/lib/CodeGen/LiveDebugValues/VarLocBasedImpl.cpp
index 43fc17ac10c89..214f9040471f8 100644
--- a/llvm/lib/CodeGen/LiveDebugValues/VarLocBasedImpl.cpp
+++ b/llvm/lib/CodeGen/LiveDebugValues/VarLocBasedImpl.cpp
@@ -1014,8 +1014,9 @@ class VarLocBasedLDV : public LDVImpl {
   /// had their instruction creation deferred.
   void flushPendingLocs(VarLocInMBB &PendingInLocs, VarLocMap &VarLocIDs);
 
-  bool ExtendRanges(MachineFunction &MF, TargetPassConfig *TPC,
-                    unsigned InputBBLimit, unsigned InputDbgValLimit) override;
+  bool ExtendRanges(MachineFunction &MF, MachineDominatorTree *DomTree,
+                    TargetPassConfig *TPC, unsigned InputBBLimit,
+                    unsigned InputDbgValLimit) override;
 
 public:
   /// Default construct and initialize the pass.
@@ -2107,9 +2108,11 @@ void VarLocBasedLDV::recordEntryValue(const MachineInstr &MI,
 
 /// Calculate the liveness information for the given machine function and
 /// extend ranges across basic blocks.
-bool VarLocBasedLDV::ExtendRanges(MachineFunction &MF, TargetPassConfig *TPC,
-                                  unsigned InputBBLimit,
+bool VarLocBasedLDV::ExtendRanges(MachineFunction &MF,
+                                  MachineDominatorTree *DomTree,
+                                  TargetPassConfig *TPC, unsigned InputBBLimit,
                                   unsigned InputDbgValLimit) {
+  (void)DomTree;
   LLVM_DEBUG(dbgs() << "\nDebug Range Extension\n");
 
   if (!MF.getFunction().getSubprogram())

diff  --git a/llvm/unittests/CodeGen/InstrRefLDVTest.cpp b/llvm/unittests/CodeGen/InstrRefLDVTest.cpp
index a0dec21ee2c08..8a4405e458e59 100644
--- a/llvm/unittests/CodeGen/InstrRefLDVTest.cpp
+++ b/llvm/unittests/CodeGen/InstrRefLDVTest.cpp
@@ -29,9 +29,14 @@ using namespace LiveDebugValues;
 
 class InstrRefLDVTest : public testing::Test {
 public:
+  friend class InstrRefBasedLDV;
+  using MLocTransferMap = InstrRefBasedLDV::MLocTransferMap;
+
   LLVMContext Ctx;
   Module Mod;
+  std::unique_ptr<TargetMachine> Machine;
   std::unique_ptr<MachineFunction> MF;
+  std::unique_ptr<MachineDominatorTree> DomTree;
   DICompileUnit *OurCU;
   DIFile *OurFile;
   DISubprogram *OurFunc;
@@ -41,33 +46,36 @@ class InstrRefLDVTest : public testing::Test {
 
   DebugLoc OutermostLoc, InBlockLoc, NotNestedBlockLoc, InlinedLoc;
 
-  MachineBasicBlock *MBB1, *MBB2, *MBB3, *MBB4;
+  MachineBasicBlock *MBB1, *MBB2, *MBB3, *MBB4, *MBB5;
+
+  std::unique_ptr<InstrRefBasedLDV> LDV;
+  std::unique_ptr<MLocTracker> MTracker;
 
   InstrRefLDVTest() : Ctx(), Mod("beehives", Ctx) {
+  }
+
+  void SetUp() {
     // Boilerplate that creates a MachineFunction and associated blocks.
-    MF = createMachineFunction(Ctx, Mod);
-    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);
+
+    Mod.setDataLayout("e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128");
+    Triple TargetTriple("x86_64--");
+    std::string Error;
+    const Target *T = TargetRegistry::lookupTarget("", TargetTriple, Error);
+    if (!T)
+      GTEST_SKIP();
+
+    TargetOptions Options;
+    Machine = std::unique_ptr<TargetMachine>(T->createTargetMachine(
+        "X86", "", "", Options, None, None, CodeGenOpt::Aggressive));
+
+    auto Type = FunctionType::get(Type::getVoidTy(Ctx), false);
+    auto F = Function::Create(Type, GlobalValue::ExternalLinkage, "Test", &Mod);
+
+    unsigned FunctionNum = 42;
+    MachineModuleInfo MMI((LLVMTargetMachine*)&*Machine);
+    const TargetSubtargetInfo &STI = *Machine->getSubtargetImpl(*F);
+
+    MF = std::make_unique<MachineFunction>(*F, (LLVMTargetMachine&)*Machine, STI, FunctionNum, MMI);
 
     // Create metadata: CU, subprogram, some blocks and an inline function
     // scope.
@@ -79,7 +87,7 @@ class InstrRefLDVTest : public testing::Test {
     OurFunc =
         DIB.createFunction(OurCU, "bees", "", OurFile, 1, OurSubT, 1,
                            DINode::FlagZero, DISubprogram::SPFlagDefinition);
-    F.setSubprogram(OurFunc);
+    F->setSubprogram(OurFunc);
     OurBlock = DIB.createLexicalBlock(OurFunc, OurFile, 2, 3);
     AnotherBlock = DIB.createLexicalBlock(OurFunc, OurFile, 2, 6);
     ToInlineFunc =
@@ -96,4 +104,994 @@ class InstrRefLDVTest : public testing::Test {
 
     DIB.finalize();
   }
+
+  Register getRegByName(const char *WantedName) {
+    auto *TRI = MF->getRegInfo().getTargetRegisterInfo();
+    // Slow, but works.
+    for (unsigned int I = 1; I < TRI->getNumRegs(); ++I) {
+      const char *Name = TRI->getName(I);
+      if (strcmp(WantedName, Name) == 0)
+        return I;
+    }
+
+    // If this ever fails, something is very wrong with this unit test.
+    llvm_unreachable("Can't find register by name");
+  }
+
+  InstrRefBasedLDV *setupLDVObj() {
+    // Create a new LDV object, and plug some relevant object ptrs into it.
+    LDV = std::make_unique<InstrRefBasedLDV>();
+    const TargetSubtargetInfo &STI = MF->getSubtarget();
+    LDV->TII = STI.getInstrInfo();
+    LDV->TRI = STI.getRegisterInfo();
+    LDV->TFI = STI.getFrameLowering();
+    LDV->MFI = &MF->getFrameInfo();
+
+    DomTree = std::make_unique<MachineDominatorTree>(*MF);
+    LDV->DomTree = &*DomTree;
+
+    // Future work: unit tests for mtracker / vtracker / ttracker.
+
+    // Setup things like the artifical block map, and BlockNo <=> RPO Order
+    // mappings.
+    LDV->initialSetup(*MF);
+    addMTracker();
+    return &*LDV;
+  }
+
+  void addMTracker() {
+    ASSERT_TRUE(LDV);
+    // Add a machine-location-tracking object to LDV. Don't initialize any
+    // register locations within it though.
+    const TargetSubtargetInfo &STI = MF->getSubtarget();
+    MTracker = std::make_unique<MLocTracker>(
+          *MF, *LDV->TII, *LDV->TRI, *STI.getTargetLowering());
+    LDV->MTracker = &*MTracker;
+  }
+
+  // Some routines for bouncing into LDV,
+  void buildMLocValueMap(ValueIDNum **MInLocs, ValueIDNum **MOutLocs,
+                         SmallVectorImpl<MLocTransferMap> &MLocTransfer) {
+    LDV->buildMLocValueMap(*MF, MInLocs, MOutLocs, MLocTransfer);
+  }
+
+  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;
+  }
 };
+
+TEST_F(InstrRefLDVTest, MLocSingleBlock) {
+  // Test some very simple properties about interpreting the transfer function.
+
+  // 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);
+
+  // Set up live-in and live-out tables for this function: two locations (we
+  // add one later) in a single block.
+  ValueIDNum InLocs[2], OutLocs[2];
+  ValueIDNum *InLocsPtr[1] = {&InLocs[0]};
+  ValueIDNum *OutLocsPtr[1] = {&OutLocs[0]};
+
+  // Transfer function: nothing.
+  SmallVector<MLocTransferMap, 1> TransferFunc = {{}};
+
+  // Try and build value maps...
+  buildMLocValueMap(InLocsPtr, OutLocsPtr, TransferFunc);
+
+  // The result should be that RSP is marked as a live-in-PHI -- this represents
+  // an argument. And as there's no transfer function, the block live-out should
+  // be the same.
+  EXPECT_EQ(InLocs[0], ValueIDNum(0, 0, RspLoc));
+  EXPECT_EQ(OutLocs[0], ValueIDNum(0, 0, RspLoc));
+
+  // Try again, this time initialising the in-locs to be defined by an
+  // instruction. The entry block should always be re-assigned to be the
+  // arguments.
+  initValueArray(InLocsPtr, 1, 2);
+  initValueArray(OutLocsPtr, 1, 2);
+  InLocs[0] = ValueIDNum(0, 1, RspLoc);
+  buildMLocValueMap(InLocsPtr, OutLocsPtr, TransferFunc);
+  EXPECT_EQ(InLocs[0], ValueIDNum(0, 0, RspLoc));
+  EXPECT_EQ(OutLocs[0], ValueIDNum(0, 0, RspLoc));
+
+  // Now insert something into the transfer function to assign to the single
+  // machine location.
+  TransferFunc[0].insert({RspLoc, ValueIDNum(0, 1, RspLoc)});
+  initValueArray(InLocsPtr, 1, 2);
+  initValueArray(OutLocsPtr, 1, 2);
+  buildMLocValueMap(InLocsPtr, OutLocsPtr, TransferFunc);
+  EXPECT_EQ(InLocs[0], ValueIDNum(0, 0, RspLoc));
+  EXPECT_EQ(OutLocs[0], ValueIDNum(0, 1, RspLoc));
+  TransferFunc[0].clear();
+
+  // Add a new register to be tracked, and insert it into the transfer function
+  // as a copy. The output of $rax should be the live-in value of $rsp.
+  Register RAX = getRegByName("RAX");
+  LocIdx RaxLoc = MTracker->lookupOrTrackRegister(RAX);
+  TransferFunc[0].insert({RspLoc, ValueIDNum(0, 1, RspLoc)});
+  TransferFunc[0].insert({RaxLoc, ValueIDNum(0, 0, RspLoc)});
+  initValueArray(InLocsPtr, 1, 2);
+  initValueArray(OutLocsPtr, 1, 2);
+  buildMLocValueMap(InLocsPtr, OutLocsPtr, TransferFunc);
+  EXPECT_EQ(InLocs[0], ValueIDNum(0, 0, RspLoc));
+  EXPECT_EQ(InLocs[1], ValueIDNum(0, 0, RaxLoc));
+  EXPECT_EQ(OutLocs[0], ValueIDNum(0, 1, RspLoc));
+  EXPECT_EQ(OutLocs[1], ValueIDNum(0, 0, RspLoc)); // Rax contains RspLoc.
+  TransferFunc[0].clear();
+}
+
+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();
+
+  ASSERT_TRUE(MTracker->getNumLocs() == 1);
+  LocIdx RspLoc(0);
+  Register RAX = getRegByName("RAX");
+  LocIdx RaxLoc = MTracker->lookupOrTrackRegister(RAX);
+
+  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]};
+
+  // Transfer function: start with nothing.
+  SmallVector<MLocTransferMap, 1> TransferFunc;
+  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);
+
+  // 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.
+  // IN ADDITION: this checks that the exit block doesn't get a PHI put in it.
+  initValueArray(InLocsPtr, 4, 2);
+  initValueArray(OutLocsPtr, 4, 2);
+  buildMLocValueMap(InLocsPtr, OutLocsPtr, TransferFunc);
+  EXPECT_EQ(InLocs[0][0], LiveInRsp);
+  EXPECT_EQ(InLocs[1][0], LiveInRsp);
+  EXPECT_EQ(InLocs[2][0], LiveInRsp);
+  EXPECT_EQ(InLocs[3][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[0][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[1][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[2][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[3][0], LiveInRsp);
+  // (Skipped writing out locations for $rax).
+
+  // Check that a def of $rsp in the entry block will likewise reach all the
+  // successors.
+  TransferFunc[0].insert({RspLoc, RspDefInBlk0});
+  initValueArray(InLocsPtr, 4, 2);
+  initValueArray(OutLocsPtr, 4, 2);
+  buildMLocValueMap(InLocsPtr, OutLocsPtr, TransferFunc);
+  EXPECT_EQ(InLocs[0][0], LiveInRsp);
+  EXPECT_EQ(InLocs[1][0], RspDefInBlk0);
+  EXPECT_EQ(InLocs[2][0], RspDefInBlk0);
+  EXPECT_EQ(InLocs[3][0], RspDefInBlk0);
+  EXPECT_EQ(OutLocs[0][0], RspDefInBlk0);
+  EXPECT_EQ(OutLocs[1][0], RspDefInBlk0);
+  EXPECT_EQ(OutLocs[2][0], RspDefInBlk0);
+  EXPECT_EQ(OutLocs[3][0], RspDefInBlk0);
+  TransferFunc[0].clear();
+
+  // Def in one branch of the diamond means that we need a PHI in the ret block
+  TransferFunc[0].insert({RspLoc, RspDefInBlk0});
+  TransferFunc[1].insert({RspLoc, RspDefInBlk1});
+  initValueArray(InLocsPtr, 4, 2);
+  initValueArray(OutLocsPtr, 4, 2);
+  buildMLocValueMap(InLocsPtr, OutLocsPtr, TransferFunc);
+  // This value map: like above, where RspDefInBlk0 is propagated through one
+  // branch of the diamond, but is def'ed in the live-outs of the other. The
+  // ret / merging block should have a PHI in its live-ins.
+  EXPECT_EQ(InLocs[0][0], LiveInRsp);
+  EXPECT_EQ(InLocs[1][0], RspDefInBlk0);
+  EXPECT_EQ(InLocs[2][0], RspDefInBlk0);
+  EXPECT_EQ(InLocs[3][0], RspPHIInBlk3);
+  EXPECT_EQ(OutLocs[0][0], RspDefInBlk0);
+  EXPECT_EQ(OutLocs[1][0], RspDefInBlk1);
+  EXPECT_EQ(OutLocs[2][0], RspDefInBlk0);
+  EXPECT_EQ(OutLocs[3][0], RspPHIInBlk3);
+  TransferFunc[0].clear();
+  TransferFunc[1].clear();
+
+  // If we have 
diff ereing defs in either side of the diamond, we should
+  // continue to produce a PHI,
+  TransferFunc[0].insert({RspLoc, RspDefInBlk0});
+  TransferFunc[1].insert({RspLoc, RspDefInBlk1});
+  TransferFunc[2].insert({RspLoc, RspDefInBlk2});
+  initValueArray(InLocsPtr, 4, 2);
+  initValueArray(OutLocsPtr, 4, 2);
+  buildMLocValueMap(InLocsPtr, OutLocsPtr, TransferFunc);
+  EXPECT_EQ(InLocs[0][0], LiveInRsp);
+  EXPECT_EQ(InLocs[1][0], RspDefInBlk0);
+  EXPECT_EQ(InLocs[2][0], RspDefInBlk0);
+  EXPECT_EQ(InLocs[3][0], RspPHIInBlk3);
+  EXPECT_EQ(OutLocs[0][0], RspDefInBlk0);
+  EXPECT_EQ(OutLocs[1][0], RspDefInBlk1);
+  EXPECT_EQ(OutLocs[2][0], RspDefInBlk2);
+  EXPECT_EQ(OutLocs[3][0], RspPHIInBlk3);
+  TransferFunc[0].clear();
+  TransferFunc[1].clear();
+  TransferFunc[2].clear();
+
+  // If we have defs of the same value on either side of the branch, a PHI will
+  // initially be created, however value propagation should then eliminate it.
+  // Encode this by copying the live-in value to $rax, and copying it to $rsp
+  // from $rax in each branch of the diamond. We don't allow the definition of
+  // arbitary values in transfer functions.
+  TransferFunc[0].insert({RspLoc, RspDefInBlk0});
+  TransferFunc[0].insert({RaxLoc, LiveInRsp});
+  TransferFunc[1].insert({RspLoc, RaxLiveInBlk1});
+  TransferFunc[2].insert({RspLoc, RaxLiveInBlk2});
+  initValueArray(InLocsPtr, 4, 2);
+  initValueArray(OutLocsPtr, 4, 2);
+  buildMLocValueMap(InLocsPtr, OutLocsPtr, TransferFunc);
+  EXPECT_EQ(InLocs[0][0], LiveInRsp);
+  EXPECT_EQ(InLocs[1][0], RspDefInBlk0);
+  EXPECT_EQ(InLocs[2][0], RspDefInBlk0);
+  EXPECT_EQ(InLocs[3][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[0][0], RspDefInBlk0);
+  EXPECT_EQ(OutLocs[1][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[2][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[3][0], LiveInRsp);
+  TransferFunc[0].clear();
+  TransferFunc[1].clear();
+  TransferFunc[2].clear();
+}
+
+TEST_F(InstrRefLDVTest, MLocSimpleLoop) {
+  //    entry
+  //     |
+  //     |/-----\
+  //    loopblk |
+  //     |\-----/
+  //     |
+  //     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();
+
+  ASSERT_TRUE(MTracker->getNumLocs() == 1);
+  LocIdx RspLoc(0);
+  Register RAX = getRegByName("RAX");
+  LocIdx RaxLoc = MTracker->lookupOrTrackRegister(RAX);
+
+  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]};
+
+  SmallVector<MLocTransferMap, 1> TransferFunc;
+  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);
+
+  // Begin test with all locations being live-through.
+  initValueArray(InLocsPtr, 3, 2);
+  initValueArray(OutLocsPtr, 3, 2);
+  buildMLocValueMap(InLocsPtr, OutLocsPtr, TransferFunc);
+  EXPECT_EQ(InLocs[0][0], LiveInRsp);
+  EXPECT_EQ(InLocs[1][0], LiveInRsp);
+  EXPECT_EQ(InLocs[2][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[0][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[1][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[2][0], LiveInRsp);
+
+  // Add a def of $rsp to the loop block: it should be in the live-outs, but
+  // should cause a PHI to be placed in the live-ins. Test the transfer function
+  // by copying that PHI into $rax in the loop, then back to $rsp in the ret
+  // block.
+  TransferFunc[1].insert({RspLoc, RspDefInBlk1});
+  TransferFunc[1].insert({RaxLoc, RspPHIInBlk1});
+  TransferFunc[2].insert({RspLoc, RaxPHIInBlk2});
+  initValueArray(InLocsPtr, 3, 2);
+  initValueArray(OutLocsPtr, 3, 2);
+  buildMLocValueMap(InLocsPtr, OutLocsPtr, TransferFunc);
+  EXPECT_EQ(InLocs[0][0], LiveInRsp);
+  EXPECT_EQ(InLocs[1][0], RspPHIInBlk1);
+  EXPECT_EQ(InLocs[2][0], RspDefInBlk1);
+  EXPECT_EQ(OutLocs[0][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[1][0], RspDefInBlk1);
+  EXPECT_EQ(OutLocs[2][0], RspPHIInBlk1);
+  // Check rax as well,
+  EXPECT_EQ(InLocs[0][1], LiveInRax);
+  EXPECT_EQ(InLocs[1][1], RaxPHIInBlk1);
+  EXPECT_EQ(InLocs[2][1], RspPHIInBlk1);
+  EXPECT_EQ(OutLocs[0][1], LiveInRax);
+  EXPECT_EQ(OutLocs[1][1], RspPHIInBlk1);
+  EXPECT_EQ(OutLocs[2][1], RspPHIInBlk1);
+  TransferFunc[1].clear();
+  TransferFunc[2].clear();
+
+  // As with the diamond case, a PHI will be created if there's a (implicit)
+  // def in the entry block and loop block; but should be value propagated away
+  // if it copies in the same value. Copy live-in $rsp to $rax, then copy it
+  // into $rsp in the loop. Encoded as copying the live-in $rax value in block 1
+  // to $rsp.
+  TransferFunc[0].insert({RaxLoc, LiveInRsp});
+  TransferFunc[1].insert({RspLoc, RaxPHIInBlk1});
+  initValueArray(InLocsPtr, 3, 2);
+  initValueArray(OutLocsPtr, 3, 2);
+  buildMLocValueMap(InLocsPtr, OutLocsPtr, TransferFunc);
+  EXPECT_EQ(InLocs[0][0], LiveInRsp);
+  EXPECT_EQ(InLocs[1][0], LiveInRsp);
+  EXPECT_EQ(InLocs[2][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[0][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[1][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[2][0], LiveInRsp);
+  // Check $rax's values.
+  EXPECT_EQ(InLocs[0][1], LiveInRax);
+  EXPECT_EQ(InLocs[1][1], LiveInRsp);
+  EXPECT_EQ(InLocs[2][1], LiveInRsp);
+  EXPECT_EQ(OutLocs[0][1], LiveInRsp);
+  EXPECT_EQ(OutLocs[1][1], LiveInRsp);
+  EXPECT_EQ(OutLocs[2][1], LiveInRsp);
+  TransferFunc[0].clear();
+  TransferFunc[1].clear();
+}
+
+TEST_F(InstrRefLDVTest, MLocNestedLoop) {
+  //    entry
+  //     |
+  //    loop1
+  //     ^\
+  //     | \    /-\
+  //     |  loop2  |
+  //     |  /   \-/
+  //     ^ /
+  //     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();
+
+  ASSERT_TRUE(MTracker->getNumLocs() == 1);
+  LocIdx RspLoc(0);
+  Register RAX = getRegByName("RAX");
+  LocIdx RaxLoc = MTracker->lookupOrTrackRegister(RAX);
+
+  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]};
+
+  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);
+
+  // Like the other tests: first ensure that if there's nothing in the transfer
+  // function, then everything is live-through (check $rsp).
+  initValueArray(InLocsPtr, 5, 2);
+  initValueArray(OutLocsPtr, 5, 2);
+  buildMLocValueMap(InLocsPtr, OutLocsPtr, TransferFunc);
+  EXPECT_EQ(InLocs[0][0], LiveInRsp);
+  EXPECT_EQ(InLocs[1][0], LiveInRsp);
+  EXPECT_EQ(InLocs[2][0], LiveInRsp);
+  EXPECT_EQ(InLocs[3][0], LiveInRsp);
+  EXPECT_EQ(InLocs[4][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[0][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[1][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[2][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[3][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[4][0], LiveInRsp);
+
+  // A def in the inner loop means we should get PHIs at the heads of both
+  // loops. Live-outs of the last three blocks will be the def, as it dominates
+  // those.
+  TransferFunc[2].insert({RspLoc, RspDefInBlk2});
+  initValueArray(InLocsPtr, 5, 2);
+  initValueArray(OutLocsPtr, 5, 2);
+  buildMLocValueMap(InLocsPtr, OutLocsPtr, TransferFunc);
+  EXPECT_EQ(InLocs[0][0], LiveInRsp);
+  EXPECT_EQ(InLocs[1][0], RspPHIInBlk1);
+  EXPECT_EQ(InLocs[2][0], RspPHIInBlk2);
+  EXPECT_EQ(InLocs[3][0], RspDefInBlk2);
+  EXPECT_EQ(InLocs[4][0], RspDefInBlk2);
+  EXPECT_EQ(OutLocs[0][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[1][0], RspPHIInBlk1);
+  EXPECT_EQ(OutLocs[2][0], RspDefInBlk2);
+  EXPECT_EQ(OutLocs[3][0], RspDefInBlk2);
+  EXPECT_EQ(OutLocs[4][0], RspDefInBlk2);
+  TransferFunc[2].clear();
+
+  // Adding a def to the outer loop header shouldn't affect this much -- the
+  // live-out of block 1 changes.
+  TransferFunc[1].insert({RspLoc, RspDefInBlk1});
+  TransferFunc[2].insert({RspLoc, RspDefInBlk2});
+  initValueArray(InLocsPtr, 5, 2);
+  initValueArray(OutLocsPtr, 5, 2);
+  buildMLocValueMap(InLocsPtr, OutLocsPtr, TransferFunc);
+  EXPECT_EQ(InLocs[0][0], LiveInRsp);
+  EXPECT_EQ(InLocs[1][0], RspPHIInBlk1);
+  EXPECT_EQ(InLocs[2][0], RspPHIInBlk2);
+  EXPECT_EQ(InLocs[3][0], RspDefInBlk2);
+  EXPECT_EQ(InLocs[4][0], RspDefInBlk2);
+  EXPECT_EQ(OutLocs[0][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[1][0], RspDefInBlk1);
+  EXPECT_EQ(OutLocs[2][0], RspDefInBlk2);
+  EXPECT_EQ(OutLocs[3][0], RspDefInBlk2);
+  EXPECT_EQ(OutLocs[4][0], RspDefInBlk2);
+  TransferFunc[1].clear();
+  TransferFunc[2].clear();
+
+  // Likewise, putting a def in the outer loop tail shouldn't affect where
+  // the PHIs go, and should propagate into the ret block.
+
+  TransferFunc[1].insert({RspLoc, RspDefInBlk1});
+  TransferFunc[2].insert({RspLoc, RspDefInBlk2});
+  TransferFunc[3].insert({RspLoc, RspDefInBlk3});
+  initValueArray(InLocsPtr, 5, 2);
+  initValueArray(OutLocsPtr, 5, 2);
+  buildMLocValueMap(InLocsPtr, OutLocsPtr, TransferFunc);
+  EXPECT_EQ(InLocs[0][0], LiveInRsp);
+  EXPECT_EQ(InLocs[1][0], RspPHIInBlk1);
+  EXPECT_EQ(InLocs[2][0], RspPHIInBlk2);
+  EXPECT_EQ(InLocs[3][0], RspDefInBlk2);
+  EXPECT_EQ(InLocs[4][0], RspDefInBlk3);
+  EXPECT_EQ(OutLocs[0][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[1][0], RspDefInBlk1);
+  EXPECT_EQ(OutLocs[2][0], RspDefInBlk2);
+  EXPECT_EQ(OutLocs[3][0], RspDefInBlk3);
+  EXPECT_EQ(OutLocs[4][0], RspDefInBlk3);
+  TransferFunc[1].clear();
+  TransferFunc[2].clear();
+  TransferFunc[3].clear();
+
+  // However: if we don't def in the inner-loop, then we just have defs in the
+  // head and tail of the outer loop. The inner loop should be live-through.
+  TransferFunc[1].insert({RspLoc, RspDefInBlk1});
+  TransferFunc[3].insert({RspLoc, RspDefInBlk3});
+  initValueArray(InLocsPtr, 5, 2);
+  initValueArray(OutLocsPtr, 5, 2);
+  buildMLocValueMap(InLocsPtr, OutLocsPtr, TransferFunc);
+  EXPECT_EQ(InLocs[0][0], LiveInRsp);
+  EXPECT_EQ(InLocs[1][0], RspPHIInBlk1);
+  EXPECT_EQ(InLocs[2][0], RspDefInBlk1);
+  EXPECT_EQ(InLocs[3][0], RspDefInBlk1);
+  EXPECT_EQ(InLocs[4][0], RspDefInBlk3);
+  EXPECT_EQ(OutLocs[0][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[1][0], RspDefInBlk1);
+  EXPECT_EQ(OutLocs[2][0], RspDefInBlk1);
+  EXPECT_EQ(OutLocs[3][0], RspDefInBlk3);
+  EXPECT_EQ(OutLocs[4][0], RspDefInBlk3);
+  TransferFunc[1].clear();
+  TransferFunc[3].clear();
+
+  // Check that this still works if we copy RspDefInBlk1 to $rax and then
+  // copy it back into $rsp in the inner loop.
+  TransferFunc[1].insert({RspLoc, RspDefInBlk1});
+  TransferFunc[1].insert({RaxLoc, RspDefInBlk1});
+  TransferFunc[2].insert({RspLoc, RaxPHIInBlk2});
+  TransferFunc[3].insert({RspLoc, RspDefInBlk3});
+  initValueArray(InLocsPtr, 5, 2);
+  initValueArray(OutLocsPtr, 5, 2);
+  buildMLocValueMap(InLocsPtr, OutLocsPtr, TransferFunc);
+  EXPECT_EQ(InLocs[0][0], LiveInRsp);
+  EXPECT_EQ(InLocs[1][0], RspPHIInBlk1);
+  EXPECT_EQ(InLocs[2][0], RspDefInBlk1);
+  EXPECT_EQ(InLocs[3][0], RspDefInBlk1);
+  EXPECT_EQ(InLocs[4][0], RspDefInBlk3);
+  EXPECT_EQ(OutLocs[0][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[1][0], RspDefInBlk1);
+  EXPECT_EQ(OutLocs[2][0], RspDefInBlk1);
+  EXPECT_EQ(OutLocs[3][0], RspDefInBlk3);
+  EXPECT_EQ(OutLocs[4][0], RspDefInBlk3);
+  // Look at raxes value in the relevant blocks,
+  EXPECT_EQ(InLocs[2][1], RspDefInBlk1);
+  EXPECT_EQ(OutLocs[1][1], RspDefInBlk1);
+  TransferFunc[1].clear();
+  TransferFunc[2].clear();
+  TransferFunc[3].clear();
+
+  // If we have a single def in the tail of the outer loop, that should produce
+  // a PHI at the loop head, and be live-through the inner loop.
+  TransferFunc[3].insert({RspLoc, RspDefInBlk3});
+  initValueArray(InLocsPtr, 5, 2);
+  initValueArray(OutLocsPtr, 5, 2);
+  buildMLocValueMap(InLocsPtr, OutLocsPtr, TransferFunc);
+  EXPECT_EQ(InLocs[0][0], LiveInRsp);
+  EXPECT_EQ(InLocs[1][0], RspPHIInBlk1);
+  EXPECT_EQ(InLocs[2][0], RspPHIInBlk1);
+  EXPECT_EQ(InLocs[3][0], RspPHIInBlk1);
+  EXPECT_EQ(InLocs[4][0], RspDefInBlk3);
+  EXPECT_EQ(OutLocs[0][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[1][0], RspPHIInBlk1);
+  EXPECT_EQ(OutLocs[2][0], RspPHIInBlk1);
+  EXPECT_EQ(OutLocs[3][0], RspDefInBlk3);
+  EXPECT_EQ(OutLocs[4][0], RspDefInBlk3);
+  TransferFunc[3].clear();
+
+  // And if we copy from $rsp to $rax in block 2, it should resolve to the PHI
+  // in block 1, and we should keep that value in rax until the ret block.
+  // There'll be a PHI in block 1 and 2, because we're putting a def in the
+  // inner loop.
+  TransferFunc[2].insert({RaxLoc, RspPHIInBlk2});
+  TransferFunc[3].insert({RspLoc, RspDefInBlk3});
+  initValueArray(InLocsPtr, 5, 2);
+  initValueArray(OutLocsPtr, 5, 2);
+  buildMLocValueMap(InLocsPtr, OutLocsPtr, TransferFunc);
+  // Examining the values of rax,
+  EXPECT_EQ(InLocs[0][1], LiveInRax);
+  EXPECT_EQ(InLocs[1][1], RaxPHIInBlk1);
+  EXPECT_EQ(InLocs[2][1], RaxPHIInBlk2);
+  EXPECT_EQ(InLocs[3][1], RspPHIInBlk1);
+  EXPECT_EQ(InLocs[4][1], RspPHIInBlk1);
+  EXPECT_EQ(OutLocs[0][1], LiveInRax);
+  EXPECT_EQ(OutLocs[1][1], RaxPHIInBlk1);
+  EXPECT_EQ(OutLocs[2][1], RspPHIInBlk1);
+  EXPECT_EQ(OutLocs[3][1], RspPHIInBlk1);
+  EXPECT_EQ(OutLocs[4][1], RspPHIInBlk1);
+  TransferFunc[2].clear();
+  TransferFunc[3].clear();
+}
+
+TEST_F(InstrRefLDVTest, MLocNoDominatingLoop) {
+  //           entry
+  //            / \
+  //           /   \
+  //          /     \
+  //        head1   head2
+  //        ^  \   /   ^
+  //        ^   \ /    ^
+  //        \-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();
+
+  ASSERT_TRUE(MTracker->getNumLocs() == 1);
+  LocIdx RspLoc(0);
+  Register RAX = getRegByName("RAX");
+  LocIdx RaxLoc = MTracker->lookupOrTrackRegister(RAX);
+
+  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]};
+
+  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);
+
+  // As ever, test that everything is live-through if there are no defs.
+  initValueArray(InLocsPtr, 5, 2);
+  initValueArray(OutLocsPtr, 5, 2);
+  buildMLocValueMap(InLocsPtr, OutLocsPtr, TransferFunc);
+  EXPECT_EQ(InLocs[0][0], LiveInRsp);
+  EXPECT_EQ(InLocs[1][0], LiveInRsp);
+  EXPECT_EQ(InLocs[2][0], LiveInRsp);
+  EXPECT_EQ(InLocs[3][0], LiveInRsp);
+  EXPECT_EQ(InLocs[4][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[0][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[1][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[2][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[3][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[4][0], LiveInRsp);
+
+  // Putting a def in the 'join' block will cause us to have two distinct
+  // PHIs in each loop head, then on entry to the join block.
+  TransferFunc[3].insert({RspLoc, RspDefInBlk3});
+  initValueArray(InLocsPtr, 5, 2);
+  initValueArray(OutLocsPtr, 5, 2);
+  buildMLocValueMap(InLocsPtr, OutLocsPtr, TransferFunc);
+  EXPECT_EQ(InLocs[0][0], LiveInRsp);
+  EXPECT_EQ(InLocs[1][0], RspPHIInBlk1);
+  EXPECT_EQ(InLocs[2][0], RspPHIInBlk2);
+  EXPECT_EQ(InLocs[3][0], RspPHIInBlk3);
+  EXPECT_EQ(InLocs[4][0], RspDefInBlk3);
+  EXPECT_EQ(OutLocs[0][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[1][0], RspPHIInBlk1);
+  EXPECT_EQ(OutLocs[2][0], RspPHIInBlk2);
+  EXPECT_EQ(OutLocs[3][0], RspDefInBlk3);
+  EXPECT_EQ(OutLocs[4][0], RspDefInBlk3);
+  TransferFunc[3].clear();
+
+  // We should get the same behaviour if we put the def in either of the
+  // loop heads -- it should force the other head to be a PHI.
+  TransferFunc[1].insert({RspLoc, RspDefInBlk1});
+  initValueArray(InLocsPtr, 5, 2);
+  initValueArray(OutLocsPtr, 5, 2);
+  buildMLocValueMap(InLocsPtr, OutLocsPtr, TransferFunc);
+  EXPECT_EQ(InLocs[0][0], LiveInRsp);
+  EXPECT_EQ(InLocs[1][0], RspPHIInBlk1);
+  EXPECT_EQ(InLocs[2][0], RspPHIInBlk2);
+  EXPECT_EQ(InLocs[3][0], RspPHIInBlk3);
+  EXPECT_EQ(InLocs[4][0], RspPHIInBlk3);
+  EXPECT_EQ(OutLocs[0][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[1][0], RspDefInBlk1);
+  EXPECT_EQ(OutLocs[2][0], RspPHIInBlk2);
+  EXPECT_EQ(OutLocs[3][0], RspPHIInBlk3);
+  EXPECT_EQ(OutLocs[4][0], RspPHIInBlk3);
+  TransferFunc[1].clear();
+
+  // Check symmetry,
+  TransferFunc[2].insert({RspLoc, RspDefInBlk2});
+  initValueArray(InLocsPtr, 5, 2);
+  initValueArray(OutLocsPtr, 5, 2);
+  buildMLocValueMap(InLocsPtr, OutLocsPtr, TransferFunc);
+  EXPECT_EQ(InLocs[0][0], LiveInRsp);
+  EXPECT_EQ(InLocs[1][0], RspPHIInBlk1);
+  EXPECT_EQ(InLocs[2][0], RspPHIInBlk2);
+  EXPECT_EQ(InLocs[3][0], RspPHIInBlk3);
+  EXPECT_EQ(InLocs[4][0], RspPHIInBlk3);
+  EXPECT_EQ(OutLocs[0][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[1][0], RspPHIInBlk1);
+  EXPECT_EQ(OutLocs[2][0], RspDefInBlk2);
+  EXPECT_EQ(OutLocs[3][0], RspPHIInBlk3);
+  EXPECT_EQ(OutLocs[4][0], RspPHIInBlk3);
+  TransferFunc[2].clear();
+
+  // Test some scenarios where there _shouldn't_ be any PHIs created at heads.
+  // These are those PHIs are created, but value propagation eliminates them.
+  // For example, lets copy rsp-livein to $rsp inside each loop head, so that
+  // there's no need for a PHI in the join block. Put a def of $rsp in block 3
+  // to force PHIs elsewhere.
+  TransferFunc[0].insert({RaxLoc, LiveInRsp});
+  TransferFunc[1].insert({RspLoc, RaxPHIInBlk1});
+  TransferFunc[2].insert({RspLoc, RaxPHIInBlk2});
+  TransferFunc[3].insert({RspLoc, RspDefInBlk3});
+  initValueArray(InLocsPtr, 5, 2);
+  initValueArray(OutLocsPtr, 5, 2);
+  buildMLocValueMap(InLocsPtr, OutLocsPtr, TransferFunc);
+  EXPECT_EQ(InLocs[0][0], LiveInRsp);
+  EXPECT_EQ(InLocs[1][0], RspPHIInBlk1);
+  EXPECT_EQ(InLocs[2][0], RspPHIInBlk2);
+  EXPECT_EQ(InLocs[3][0], LiveInRsp);
+  EXPECT_EQ(InLocs[4][0], RspDefInBlk3);
+  EXPECT_EQ(OutLocs[0][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[1][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[2][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[3][0], RspDefInBlk3);
+  EXPECT_EQ(OutLocs[4][0], RspDefInBlk3);
+  TransferFunc[0].clear();
+  TransferFunc[1].clear();
+  TransferFunc[2].clear();
+  TransferFunc[3].clear();
+
+  // In fact, if we eliminate the def in block 3, none of those PHIs are
+  // necessary, as we're just repeatedly copying LiveInRsp into $rsp. They
+  // should all be value propagated out.
+  TransferFunc[0].insert({RaxLoc, LiveInRsp});
+  TransferFunc[1].insert({RspLoc, RaxPHIInBlk1});
+  TransferFunc[2].insert({RspLoc, RaxPHIInBlk2});
+  initValueArray(InLocsPtr, 5, 2);
+  initValueArray(OutLocsPtr, 5, 2);
+  buildMLocValueMap(InLocsPtr, OutLocsPtr, TransferFunc);
+  EXPECT_EQ(InLocs[0][0], LiveInRsp);
+  EXPECT_EQ(InLocs[1][0], LiveInRsp);
+  EXPECT_EQ(InLocs[2][0], LiveInRsp);
+  EXPECT_EQ(InLocs[3][0], LiveInRsp);
+  EXPECT_EQ(InLocs[4][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[0][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[1][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[2][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[3][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[4][0], LiveInRsp);
+  TransferFunc[0].clear();
+  TransferFunc[1].clear();
+  TransferFunc[2].clear();
+}
+
+TEST_F(InstrRefLDVTest, MLocBadlyNestedLoops) {
+  //           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 *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();
+
+  ASSERT_TRUE(MTracker->getNumLocs() == 1);
+  LocIdx RspLoc(0);
+  Register RAX = getRegByName("RAX");
+  LocIdx RaxLoc = MTracker->lookupOrTrackRegister(RAX);
+
+  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]};
+
+  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);
+
+  // As ever, test that everything is live-through if there are no defs.
+  initValueArray(InLocsPtr, 5, 2);
+  initValueArray(OutLocsPtr, 5, 2);
+  buildMLocValueMap(InLocsPtr, OutLocsPtr, TransferFunc);
+  EXPECT_EQ(InLocs[0][0], LiveInRsp);
+  EXPECT_EQ(InLocs[1][0], LiveInRsp);
+  EXPECT_EQ(InLocs[2][0], LiveInRsp);
+  EXPECT_EQ(InLocs[3][0], LiveInRsp);
+  EXPECT_EQ(InLocs[4][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[0][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[1][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[2][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[3][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[4][0], LiveInRsp);
+
+  // A def in loop3 should cause PHIs in every loop block: they're all
+  // reachable from each other.
+  TransferFunc[3].insert({RspLoc, RspDefInBlk3});
+  initValueArray(InLocsPtr, 5, 2);
+  initValueArray(OutLocsPtr, 5, 2);
+  buildMLocValueMap(InLocsPtr, OutLocsPtr, TransferFunc);
+  EXPECT_EQ(InLocs[0][0], LiveInRsp);
+  EXPECT_EQ(InLocs[1][0], RspPHIInBlk1);
+  EXPECT_EQ(InLocs[2][0], RspPHIInBlk2);
+  EXPECT_EQ(InLocs[3][0], RspPHIInBlk3);
+  EXPECT_EQ(InLocs[4][0], RspDefInBlk3);
+  EXPECT_EQ(OutLocs[0][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[1][0], RspPHIInBlk1);
+  EXPECT_EQ(OutLocs[2][0], RspPHIInBlk2);
+  EXPECT_EQ(OutLocs[3][0], RspDefInBlk3);
+  EXPECT_EQ(OutLocs[4][0], RspDefInBlk3);
+  TransferFunc[3].clear();
+
+  // A def in loop1 should cause a PHI in loop1, but not the other blocks.
+  // loop2 and loop3 are dominated by the def in loop1, so they should have
+  // that value live-through.
+  TransferFunc[1].insert({RspLoc, RspDefInBlk1});
+  initValueArray(InLocsPtr, 5, 2);
+  initValueArray(OutLocsPtr, 5, 2);
+  buildMLocValueMap(InLocsPtr, OutLocsPtr, TransferFunc);
+  EXPECT_EQ(InLocs[0][0], LiveInRsp);
+  EXPECT_EQ(InLocs[1][0], RspPHIInBlk1);
+  EXPECT_EQ(InLocs[2][0], RspDefInBlk1);
+  EXPECT_EQ(InLocs[3][0], RspDefInBlk1);
+  EXPECT_EQ(InLocs[4][0], RspDefInBlk1);
+  EXPECT_EQ(OutLocs[0][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[1][0], RspDefInBlk1);
+  EXPECT_EQ(OutLocs[2][0], RspDefInBlk1);
+  EXPECT_EQ(OutLocs[3][0], RspDefInBlk1);
+  EXPECT_EQ(OutLocs[4][0], RspDefInBlk1);
+  TransferFunc[1].clear();
+
+  // As with earlier tricks: copy $rsp to $rax in the entry block, then $rax
+  // to $rsp in block 3. The only def of $rsp is simply copying the same value
+  // back into itself, and the value of $rsp is LiveInRsp all the way through.
+  // PHIs should be created, then value-propagated away...  however this
+  // doesn't work in practice.
+  // Consider the entry to loop3: we can determine that there's an incoming
+  // PHI value from loop2, and LiveInRsp from the self-loop. This would still
+  // justify having a PHI on entry to loop3. The only way to completely
+  // value-propagate these PHIs away would be to speculatively explore what
+  // PHIs could be eliminated and what that would lead to; which is
+  // combinatorially complex.
+  // Happily:
+  //  a) In this scenario, we always have a tracked location for LiveInRsp
+  //     anyway, so there's no loss in availability,
+  //  b) Only DBG_PHIs of a register would be vunlerable to this scenario, and
+  //     even then only if a true PHI became a DBG_PHI and was then optimised
+  //     through branch folding to no longer be at a CFG join,
+  //  c) The register allocator can spot this kind of redundant COPY easily,
+  //     and eliminate it.
+  //
+  // This unit test left in as a reference for the limitations of this
+  // approach. PHIs will be left in $rsp on entry to each block.
+  TransferFunc[0].insert({RaxLoc, LiveInRsp});
+  TransferFunc[3].insert({RspLoc, RaxPHIInBlk3});
+  initValueArray(InLocsPtr, 5, 2);
+  initValueArray(OutLocsPtr, 5, 2);
+  buildMLocValueMap(InLocsPtr, OutLocsPtr, TransferFunc);
+  EXPECT_EQ(InLocs[0][0], LiveInRsp);
+  EXPECT_EQ(InLocs[1][0], RspPHIInBlk1);
+  EXPECT_EQ(InLocs[2][0], RspPHIInBlk2);
+  EXPECT_EQ(InLocs[3][0], RspPHIInBlk3);
+  EXPECT_EQ(InLocs[4][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[0][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[1][0], RspPHIInBlk1);
+  EXPECT_EQ(OutLocs[2][0], RspPHIInBlk2);
+  EXPECT_EQ(OutLocs[3][0], LiveInRsp);
+  EXPECT_EQ(OutLocs[4][0], LiveInRsp);
+  // Check $rax's value. It should have $rsps value from the entry block
+  // onwards.
+  EXPECT_EQ(InLocs[0][1], LiveInRax);
+  EXPECT_EQ(InLocs[1][1], LiveInRsp);
+  EXPECT_EQ(InLocs[2][1], LiveInRsp);
+  EXPECT_EQ(InLocs[3][1], LiveInRsp);
+  EXPECT_EQ(InLocs[4][1], LiveInRsp);
+  EXPECT_EQ(OutLocs[0][1], LiveInRsp);
+  EXPECT_EQ(OutLocs[1][1], LiveInRsp);
+  EXPECT_EQ(OutLocs[2][1], LiveInRsp);
+  EXPECT_EQ(OutLocs[3][1], LiveInRsp);
+  EXPECT_EQ(OutLocs[4][1], LiveInRsp);
+}


        


More information about the llvm-commits mailing list