[llvm] 5a55c95 - [WebAssembly] Redesign DebugValueManager

Heejin Ahn via llvm-commits llvm-commits at lists.llvm.org
Wed Mar 29 12:50:05 PDT 2023


Author: Heejin Ahn
Date: 2023-03-29T12:49:57-07:00
New Revision: 5a55c9507b6961ba4fb6acbeb2ffa1dba0d13969

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

LOG: [WebAssembly] Redesign DebugValueManager

The current `DebugValueManager`, which is mostly used in `RegStackify`,
simply sinks `DBG_VALUE`s along when a def instruction sinks.
(`RegStackify` only does sinks; it doesn't do hoists.)

But this simple strategy can result in incorrect combinations of
variables' values which would have not been possible in the original
program. In this case, LLVM's policy is to make the value unavailable,
so they will be shown as 'optimized out', rather than showing inaccurate
debug info. Especially, when an instruction sinks, its original
`DBG_VALUE` should be set to undef. This is well illustrated in the
third example in
https://llvm.org/docs/SourceLevelDebugging.html#instruction-scheduling.

This CL rewrites `DebugValueManager` with this principle in mind. When
sinking an instruction, it sinks its eligible `DBG_VALUE`s with it, but
also leaves undef `DBG_VALUE`s in the original place to make those
variables' values undefined.

Also, unlike the current version, we sink only an eligible subset of
`DBG_VALUE`s with a def instruction. See comments in the code for
details.

In case of cloning, because the original def is still there, we don't
set its `DBG_VALUE`s to undef. But we clone only an eligible subset of
`DBG_VALUE`s here as well.

One consequence of this change is that now we do sinking and cloning of
the def instruction itself within the `DebugValueManager`'s `sink` and
`clone` methods. This is necessary because the `DebugValueManager` needs
to know the original def's location before sinking and cloning in order
to scan other interfering `DBG_VALUE`s between the original def and the
insertion point. If we want to separate these two, we need to call
`DebugValueManager`'s `sink` and `clone` methods //before//
sinking/cloning the def instruction, which I don't think is a good
design alternative either, because the user of this class needs to pay
extra attention when using it.

Because this change is fixing the existing inaccuracy of the current
debug info, this reduces the variable info coverage in debug info, but
not by a large margin. In Emscripten core benchmarks compiled with
`-O1`, the coverage goes from 56.6% down to 55.2%, which I doubt will be
a noticeable drop. The compilation time doesn't have any meaningful
difference either with this change.

Reviewed By: dschuff

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

Added: 
    llvm/test/DebugInfo/WebAssembly/dbg-value-reg-stackify.mir

Modified: 
    llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h
    llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.cpp
    llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.h
    llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp
    llvm/test/DebugInfo/WebAssembly/dbg-value-move-2.ll

Removed: 
    llvm/test/DebugInfo/WebAssembly/dbg-value-move-reg-stackify.mir


################################################################################
diff  --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h
index e46fb3d311ad6..773223b728740 100644
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h
@@ -279,6 +279,50 @@ inline unsigned GetDefaultP2Align(unsigned Opc) {
   return Align;
 }
 
+inline bool isConst(unsigned Opc) {
+  switch (Opc) {
+  case WebAssembly::CONST_I32:
+  case WebAssembly::CONST_I32_S:
+  case WebAssembly::CONST_I64:
+  case WebAssembly::CONST_I64_S:
+  case WebAssembly::CONST_F32:
+  case WebAssembly::CONST_F32_S:
+  case WebAssembly::CONST_F64:
+  case WebAssembly::CONST_F64_S:
+  case WebAssembly::CONST_V128_I8x16:
+  case WebAssembly::CONST_V128_I8x16_S:
+  case WebAssembly::CONST_V128_I16x8:
+  case WebAssembly::CONST_V128_I16x8_S:
+  case WebAssembly::CONST_V128_I32x4:
+  case WebAssembly::CONST_V128_I32x4_S:
+  case WebAssembly::CONST_V128_I64x2:
+  case WebAssembly::CONST_V128_I64x2_S:
+  case WebAssembly::CONST_V128_F32x4:
+  case WebAssembly::CONST_V128_F32x4_S:
+  case WebAssembly::CONST_V128_F64x2:
+  case WebAssembly::CONST_V128_F64x2_S:
+    return true;
+  default:
+    return false;
+  }
+}
+
+inline bool isScalarConst(unsigned Opc) {
+  switch (Opc) {
+  case WebAssembly::CONST_I32:
+  case WebAssembly::CONST_I32_S:
+  case WebAssembly::CONST_I64:
+  case WebAssembly::CONST_I64_S:
+  case WebAssembly::CONST_F32:
+  case WebAssembly::CONST_F32_S:
+  case WebAssembly::CONST_F64:
+  case WebAssembly::CONST_F64_S:
+    return true;
+  default:
+    return false;
+  }
+}
+
 inline bool isArgument(unsigned Opc) {
   switch (Opc) {
   case WebAssembly::ARGUMENT_i32:

diff  --git a/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.cpp
index 45502a577e4e2..c036ef02a6d8b 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.cpp
@@ -12,15 +12,18 @@
 //===----------------------------------------------------------------------===//
 
 #include "WebAssemblyDebugValueManager.h"
+#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
 #include "WebAssembly.h"
 #include "WebAssemblyMachineFunctionInfo.h"
 #include "llvm/CodeGen/MachineInstr.h"
 
 using namespace llvm;
 
-WebAssemblyDebugValueManager::WebAssemblyDebugValueManager(MachineInstr *Def) {
+WebAssemblyDebugValueManager::WebAssemblyDebugValueManager(MachineInstr *Def)
+    : Def(Def) {
   // This code 
diff ers from MachineInstr::collectDebugValues in that it scans
-  // the whole BB, not just contiguous DBG_VALUEs.
+  // the whole BB, not just contiguous DBG_VALUEs, until another definition to
+  // the same register is encountered.
   if (!Def->getOperand(0).isReg())
     return;
   CurrentReg = Def->getOperand(0).getReg();
@@ -28,34 +31,317 @@ WebAssemblyDebugValueManager::WebAssemblyDebugValueManager(MachineInstr *Def) {
   for (MachineBasicBlock::iterator MI = std::next(Def->getIterator()),
                                    ME = Def->getParent()->end();
        MI != ME; ++MI) {
+    // If another definition appears, stop
+    if (MI->definesRegister(CurrentReg))
+      break;
     if (MI->isDebugValue() && MI->hasDebugOperandForReg(CurrentReg))
       DbgValues.push_back(&*MI);
   }
 }
 
-void WebAssemblyDebugValueManager::move(MachineInstr *Insert) {
+// Returns true if both A and B are the same CONST_I32/I64/F32/F64 instructions.
+// Doesn't include CONST_V128.
+static bool isSameScalarConst(const MachineInstr *A, const MachineInstr *B) {
+  if (A->getOpcode() != B->getOpcode() ||
+      !WebAssembly::isScalarConst(A->getOpcode()) ||
+      !WebAssembly::isScalarConst(B->getOpcode()))
+    return false;
+  const MachineOperand &OpA = A->getOperand(1), &OpB = B->getOperand(1);
+  if ((OpA.isImm() && OpB.isImm() && OpA.getImm() == OpB.getImm()) ||
+      (OpA.isFPImm() && OpB.isFPImm() && OpA.getFPImm() == OpB.getFPImm()) ||
+      (OpA.isGlobal() && OpB.isGlobal() && OpA.getGlobal() == OpB.getGlobal()))
+    return true;
+  return false;
+}
+
+SmallVector<MachineInstr *>
+WebAssemblyDebugValueManager::getSinkableDebugValues(
+    MachineInstr *Insert) const {
+  if (DbgValues.empty())
+    return {};
+  // DBG_VALUEs between Def and Insert
+  SmallVector<MachineInstr *, 8> DbgValuesInBetween;
+
+  if (Def->getParent() == Insert->getParent()) {
+    // When Def and Insert are within the same BB, check if Insert comes after
+    // Def, because we only support sinking.
+    bool DefFirst = false;
+    for (MachineBasicBlock::iterator MI = std::next(Def->getIterator()),
+                                     ME = Def->getParent()->end();
+         MI != ME; ++MI) {
+      if (&*MI == Insert) {
+        DefFirst = true;
+        break;
+      }
+      if (MI->isDebugValue())
+        DbgValuesInBetween.push_back(&*MI);
+    }
+    if (!DefFirst) // Not a sink
+      return {};
+
+  } else { // Def and Insert are in 
diff erent BBs
+    // If Def and Insert are in 
diff erent BBs, we only handle a simple case in
+    // which Insert's BB is a successor of Def's BB.
+    if (!Def->getParent()->isSuccessor(Insert->getParent()))
+      return {};
+
+    // Gather DBG_VALUEs between 'Def~Def BB's end' and
+    // 'Insert BB's begin~Insert'
+    for (MachineBasicBlock::iterator MI = std::next(Def->getIterator()),
+                                     ME = Def->getParent()->end();
+         MI != ME; ++MI) {
+      if (MI->isDebugValue())
+        DbgValuesInBetween.push_back(&*MI);
+    }
+    for (MachineBasicBlock::iterator MI = Insert->getParent()->begin(),
+                                     ME = Insert->getIterator();
+         MI != ME; ++MI) {
+      if (MI->isDebugValue())
+        DbgValuesInBetween.push_back(&*MI);
+    }
+  }
+
+  // Gather DebugVariables that are seen between Def and Insert, excluding our
+  // own DBG_VALUEs in DbgValues.
+  SmallDenseMap<DebugVariable, SmallVector<MachineInstr *, 2>>
+      SeenDbgVarToDbgValues;
+  for (auto *DV : DbgValuesInBetween) {
+    if (std::find(DbgValues.begin(), DbgValues.end(), DV) == DbgValues.end()) {
+      DebugVariable Var(DV->getDebugVariable(), DV->getDebugExpression(),
+                        DV->getDebugLoc()->getInlinedAt());
+      SeenDbgVarToDbgValues[Var].push_back(DV);
+    }
+  }
+
+  // Gather sinkable DBG_VALUEs. We should not sink a DBG_VALUE if there is
+  // another DBG_VALUE between Def and Insert referring to the same
+  // DebugVariable. For example,
+  //   %0 = someinst
+  //   DBG_VALUE %0, !"a", !DIExpression() // Should not sink with %0
+  //   %1 = anotherinst
+  //   DBG_VALUE %1, !"a", !DIExpression()
+  // Where if %0 were to sink, the DBG_VAUE should not sink with it, as that
+  // would re-order assignments.
+  SmallVector<MachineInstr *, 1> SinkableDbgValues;
+  MachineRegisterInfo &MRI = Def->getParent()->getParent()->getRegInfo();
+  for (auto *DV : DbgValues) {
+    DebugVariable Var(DV->getDebugVariable(), DV->getDebugExpression(),
+                      DV->getDebugLoc()->getInlinedAt());
+    auto It = SeenDbgVarToDbgValues.find(Var);
+    if (It == SeenDbgVarToDbgValues.end()) {
+      SinkableDbgValues.push_back(DV);
+      continue;
+    }
+    if (!WebAssembly::isScalarConst(Def->getOpcode()))
+      continue;
+    auto &OverlappingDbgValues = It->second;
+    bool Sinkable = true;
+    for (auto *OverlappingDV : OverlappingDbgValues) {
+      MachineOperand &DbgOp = OverlappingDV->getDebugOperand(0);
+      if (!DbgOp.isReg()) {
+        Sinkable = false;
+        break;
+      }
+      Register OtherReg = DbgOp.getReg();
+      MachineInstr *OtherDef = MRI.getUniqueVRegDef(OtherReg);
+      // We have an exception to allow encoutering other DBG_VALUEs with the
+      // smae DebugVariables, only when they are referring to the same scalar
+      // CONST instruction. For example,
+      //   %0 = CONST_I32 1
+      //   DBG_VALUE %0, !"a", !DIExpression() // Can sink with %0
+      //   %1 = CONST_I32 1
+      //   DBG_VALUE %1, !"a", !DIExpression()
+      // When %0 were to be sunk/cloneed, the DBG_VALUE can be sunk/cloned with
+      // it because even though the second DBG_VALUE refers to the same
+      // DebugVariable, its value in effect is the same CONST instruction.
+      //
+      // This is to allow a case that can happen with RegStackify's
+      // "rematerializeCheapDef". For example, we have this program with two
+      // BBs:
+      // bb0:
+      //   %0 = CONST_I32 1
+      //   DBG_VALUE %0, !"a", ...
+      //   ...
+      //   INST0 ..., $0 ...
+      //  bb1:
+      //   INST1 ..., $0 ...
+      //   INST2 ..., $0 ...
+      //
+      // We process bb0 first. Because %0 is used multiple times, %0 is cloned
+      // before INST0:
+      // bb0:
+      //   %0 = CONST_I32 1
+      //   DBG_VALUE %0, !"a", ...
+      //   ...
+      //   %1 = CONST_I32 1
+      //   DBG_VALUE %1, !"a", ...
+      //   INST0 ..., $1 ...
+      //
+      // And when we process bb1, we clone %0 and its DBG_VALUE again:
+      // bb0:
+      //   %0 = CONST_I32 1
+      //   DBG_VALUE %0, !"a", ...
+      //   ...
+      //   %1 = CONST_I32 1
+      //   DBG_VALUE %1, !"a", ...
+      //   INST0 ..., $1 ...
+      //  bb1:
+      //   %2 = CONST_I32 1
+      //   DBG_VALUE %2, !"a", ... // !!!
+      //   INST1 ..., $2 ...
+      //   %3 = CONST_I32 1
+      //   DBG_VALUE %3, !"a", ... // !!!
+      //   INST2 ..., $3 ...
+      //
+      // But (without this exception) the cloned DBG_VALUEs marked with !!! are
+      // not possible to be cloned, because there is a previously cloned
+      // 'DBG_VALUE %1, !"a"' at the end of bb0 referring to the same
+      // DebugVariable "a". But in this case they are OK to be cloned, because
+      // the interfering DBG_VALUE is pointing to the same 'CONST_I32 1',
+      // because it was cloned from the same instruction.
+      if (!OtherDef || !isSameScalarConst(Def, OtherDef)) {
+        Sinkable = false;
+        break;
+      }
+    }
+    if (Sinkable)
+      SinkableDbgValues.push_back(DV);
+  }
+  return SinkableDbgValues;
+}
+
+// Sink 'Def', and also sink its eligible DBG_VALUEs to the place before
+// 'Insert'. Convert the original DBG_VALUEs into undefs.
+//
+// For DBG_VALUEs to sink properly, if 'Def' and 'Insert' are within the same
+// BB, 'Insert' should be below 'Def'; if they are in 
diff erent BBs, 'Insert'
+// should be in one of 'Def's BBs successors. Def will be sunk regardless of the
+// location.
+//
+// This DebugValueManager's new Def and DbgValues will be updated to the newly
+// sinked Def + DBG_VALUEs.
+void WebAssemblyDebugValueManager::sink(MachineInstr *Insert) {
   MachineBasicBlock *MBB = Insert->getParent();
-  for (MachineInstr *DBI : reverse(DbgValues))
-    MBB->splice(Insert, DBI->getParent(), DBI);
+  MachineFunction *MF = MBB->getParent();
+
+  // Get the list of sinkable DBG_VALUEs. This should be done before sinking
+  // Def, because we need to examine instructions between Def and Insert.
+  SmallVector<MachineInstr *, 1> SinkableDbgValues =
+      getSinkableDebugValues(Insert);
+
+  // Sink Def first.
+  MBB->splice(Insert, Def->getParent(), Def);
+
+  if (DbgValues.empty())
+    return;
+
+  // Clone sinkable DBG_VALUEs and insert them.
+  SmallVector<MachineInstr *, 1> NewDbgValues;
+  for (MachineInstr *DV : SinkableDbgValues) {
+    MachineInstr *Clone = MF->CloneMachineInstr(DV);
+    MBB->insert(Insert, Clone);
+    NewDbgValues.push_back(Clone);
+  }
+
+  // When sinking a Def and its DBG_VALUEs, we shouldn't just remove the
+  // original DBG_VALUE instructions; we should set them to undef not to create
+  // an impossible combination of variable assignments in the original program.
+  // For example, this is the original program in order:
+  //   %0 = CONST_I32 0
+  //   DBG_VALUE %0, !"a", !DIExpression()  // a = 0, b = ?
+  //   %1 = CONST_I32 1
+  //   DBG_VALUE %1, !"b", !DIExpression()  // a = 0, b = 1
+  //   %2 = CONST_I32 2
+  //   DBG_VALUE %2, !"a", !DIExpression()  // a = 2, b = 1
+  //   %3 = CONST_I32 3
+  //   DBG_VALUE %3, !"b", !DIExpression()  // a = 2, b = 3
+  //
+  // If %2 were to sink below %3, if we just sink DBG_VALUE %1 with it, the
+  // debug info will show the variable "b" is updated to 2, creating the
+  // variable assignment combination of (a = 0, b = 3), which is not possible in
+  // the original program:
+  //   %0 = CONST_I32 0
+  //   DBG_VALUE %0, !"a", !DIExpression()  // a = 0, b = ?
+  //   %1 = CONST_I32 1
+  //   DBG_VALUE %1, !"b", !DIExpression()  // a = 0, b = 1
+  //   %3 = CONST_I32 3
+  //   DBG_VALUE %3, !"b", !DIExpression()  // a = 0, b = 3 (Incorrect!)
+  //   %2 = CONST_I32 2
+  //   DBG_VALUE %2, !"a", !DIExpression()  // a = 2, b = 3
+  //
+  // To fix this,we leave an undef DBG_VALUE in its original place, so that the
+  // result will be
+  //   %0 = CONST_I32 0
+  //   DBG_VALUE %0, !"a", !DIExpression()      // a = 0, b = ?
+  //   %1 = CONST_I32 1
+  //   DBG_VALUE %1, !"b", !DIExpression()      // a = 0, b = 1
+  //   DBG_VALUE $noreg, !"a", !DIExpression()  // a = ?, b = 1
+  //   %3 = CONST_I32 3
+  //   DBG_VALUE %3, !"b", !DIExpression()      // a = ?, b = 3
+  //   %2 = CONST_I32 2
+  //   DBG_VALUE %2, !"a", !DIExpression()      // a = 2, b = 3
+  // Now in the middle "a" will be shown as "optimized out", but it wouldn't
+  // show the impossible combination of (a = 0, b = 3).
+  for (MachineInstr *DV : DbgValues)
+    DV->setDebugValueUndef();
+
+  DbgValues.swap(NewDbgValues);
+  return;
 }
 
-void WebAssemblyDebugValueManager::clone(MachineInstr *Insert,
-                                         Register NewReg) {
+// Clone 'Def', and also clone its eligible DBG_VALUEs to the place before
+// 'Insert'.
+//
+// For DBG_VALUEs to be cloned properly, if 'Def' and 'Insert' are within the
+// same BB, 'Insert' should be below 'Def'; if they are in 
diff erent BBs,
+// 'Insert' should be in one of 'Def's BBs successors. Def will be cloned
+// regardless of the location.
+//
+// If NewReg is not $noreg, the newly cloned DBG_VALUEs will have the new
+// register as its operand.
+void WebAssemblyDebugValueManager::cloneSink(MachineInstr *Insert,
+                                             Register NewReg,
+                                             bool CloneDef) const {
   MachineBasicBlock *MBB = Insert->getParent();
   MachineFunction *MF = MBB->getParent();
-  for (MachineInstr *DBI : reverse(DbgValues)) {
-    MachineInstr *Clone = MF->CloneMachineInstr(DBI);
-    for (auto &MO : Clone->getDebugOperandsForReg(CurrentReg))
-      MO.setReg(NewReg);
+
+  SmallVector<MachineInstr *> SinkableDbgValues =
+      getSinkableDebugValues(Insert);
+
+  // Clone Def first.
+  if (CloneDef) {
+    MachineInstr *Clone = MF->CloneMachineInstr(Def);
+    if (NewReg != CurrentReg && NewReg.isValid())
+      Clone->getOperand(0).setReg(NewReg);
     MBB->insert(Insert, Clone);
   }
+
+  if (DbgValues.empty())
+    return;
+
+  // Clone sinkable DBG_VALUEs and insert them.
+  SmallVector<MachineInstr *, 1> NewDbgValues;
+  for (MachineInstr *DV : SinkableDbgValues) {
+    MachineInstr *Clone = MF->CloneMachineInstr(DV);
+    MBB->insert(Insert, Clone);
+    NewDbgValues.push_back(Clone);
+  }
+
+  if (NewReg != CurrentReg && NewReg.isValid())
+    for (auto *DBI : NewDbgValues)
+      for (auto &MO : DBI->getDebugOperandsForReg(CurrentReg))
+        MO.setReg(NewReg);
 }
 
+// Update the register for Def and DBG_VALUEs.
 void WebAssemblyDebugValueManager::updateReg(Register Reg) {
-  for (auto *DBI : DbgValues)
-    for (auto &MO : DBI->getDebugOperandsForReg(CurrentReg))
-      MO.setReg(Reg);
-  CurrentReg = Reg;
+  if (Reg != CurrentReg && Reg.isValid()) {
+    for (auto *DBI : DbgValues)
+      for (auto &MO : DBI->getDebugOperandsForReg(CurrentReg))
+        MO.setReg(Reg);
+    CurrentReg = Reg;
+    Def->getOperand(0).setReg(Reg);
+  }
 }
 
 void WebAssemblyDebugValueManager::replaceWithLocal(unsigned LocalId) {
@@ -67,3 +353,10 @@ void WebAssemblyDebugValueManager::replaceWithLocal(unsigned LocalId) {
       MO.ChangeToTargetIndex(IndexType, LocalId);
   }
 }
+
+// Remove Def, and set its DBG_VALUEs to undef.
+void WebAssemblyDebugValueManager::removeDef() {
+  Def->removeFromParent();
+  for (MachineInstr *DV : DbgValues)
+    DV->setDebugValueUndef();
+}

diff  --git a/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.h b/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.h
index 4c63af21406e1..3ca9290ecd21d 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.h
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.h
@@ -26,19 +26,29 @@ namespace llvm {
 class MachineInstr;
 
 class WebAssemblyDebugValueManager {
+  MachineInstr *Def;
   SmallVector<MachineInstr *, 1> DbgValues;
   Register CurrentReg;
+  SmallVector<MachineInstr *>
+  getSinkableDebugValues(MachineInstr *Insert) const;
 
 public:
   WebAssemblyDebugValueManager(MachineInstr *Def);
 
-  void move(MachineInstr *Insert);
-  void clone(MachineInstr *Insert, Register NewReg);
+  // Sink 'Def', and also sink its eligible DBG_VALUEs to the place before
+  // 'Insert'. Convert the original DBG_VALUEs into undefs.
+  void sink(MachineInstr *Insert);
+  // Clone 'Def' (optionally), and also clone its eligible DBG_VALUEs to the
+  // place before 'Insert'.
+  void cloneSink(MachineInstr *Insert, Register NewReg = Register(),
+                 bool CloneDef = true) const;
   // Update the register for Def and DBG_VALUEs.
   void updateReg(Register Reg);
   // Replace the current register in DBG_VALUEs with the given LocalId target
   // index.
   void replaceWithLocal(unsigned LocalId);
+  // Remove Def, and set its DBG_VALUEs to undef.
+  void removeDef();
 };
 
 } // end namespace llvm

diff  --git a/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp
index 4b24f7fdb118c..2e0df3c478411 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp
@@ -278,12 +278,13 @@ static MachineInstr *getVRegDef(unsigned Reg, const MachineInstr *Insert,
 }
 
 // Test whether Reg, as defined at Def, has exactly one use. This is a
-// generalization of MachineRegisterInfo::hasOneUse that uses LiveIntervals
-// to handle complex cases.
-static bool hasOneUse(unsigned Reg, MachineInstr *Def, MachineRegisterInfo &MRI,
-                      MachineDominatorTree &MDT, LiveIntervals &LIS) {
+// generalization of MachineRegisterInfo::hasOneNonDBGUse that uses
+// LiveIntervals to handle complex cases.
+static bool hasOneNonDBGUse(unsigned Reg, MachineInstr *Def,
+                            MachineRegisterInfo &MRI, MachineDominatorTree &MDT,
+                            LiveIntervals &LIS) {
   // Most registers are in SSA form here so we try a quick MRI query first.
-  if (MRI.hasOneUse(Reg))
+  if (MRI.hasOneNonDBGUse(Reg))
     return true;
 
   bool HasOne = false;
@@ -525,11 +526,10 @@ static MachineInstr *moveForSingleUse(unsigned Reg, MachineOperand &Op,
   LLVM_DEBUG(dbgs() << "Move for single use: "; Def->dump());
 
   WebAssemblyDebugValueManager DefDIs(Def);
-  MBB.splice(Insert, &MBB, Def);
-  DefDIs.move(Insert);
+  DefDIs.sink(Insert);
   LIS.handleMove(*Def);
 
-  if (MRI.hasOneDef(Reg) && MRI.hasOneUse(Reg)) {
+  if (MRI.hasOneDef(Reg) && MRI.hasOneNonDBGUse(Reg)) {
     // No one else is using this register for anything so we can just stackify
     // it in place.
     MFI.stackifyVReg(MRI, Reg);
@@ -537,8 +537,8 @@ static MachineInstr *moveForSingleUse(unsigned Reg, MachineOperand &Op,
     // The register may have unrelated uses or defs; create a new register for
     // just our one def and use so that we can stackify it.
     Register NewReg = MRI.createVirtualRegister(MRI.getRegClass(Reg));
-    Def->getOperand(0).setReg(NewReg);
     Op.setReg(NewReg);
+    DefDIs.updateReg(NewReg);
 
     // Tell LiveIntervals about the new register.
     LIS.createAndComputeVirtRegInterval(NewReg);
@@ -551,8 +551,6 @@ static MachineInstr *moveForSingleUse(unsigned Reg, MachineOperand &Op,
 
     MFI.stackifyVReg(MRI, NewReg);
 
-    DefDIs.updateReg(NewReg);
-
     LLVM_DEBUG(dbgs() << " - Replaced register: "; Def->dump());
   }
 
@@ -560,6 +558,13 @@ static MachineInstr *moveForSingleUse(unsigned Reg, MachineOperand &Op,
   return Def;
 }
 
+static MachineInstr *getPrevNonDebugInst(MachineInstr *MI) {
+  for (auto *I = MI->getPrevNode(); I; I = I->getPrevNode())
+    if (!I->isDebugInstr())
+      return I;
+  return nullptr;
+}
+
 /// A trivially cloneable instruction; clone it and nest the new copy with the
 /// current instruction.
 static MachineInstr *rematerializeCheapDef(
@@ -573,9 +578,10 @@ static MachineInstr *rematerializeCheapDef(
   WebAssemblyDebugValueManager DefDIs(&Def);
 
   Register NewReg = MRI.createVirtualRegister(MRI.getRegClass(Reg));
-  TII->reMaterialize(MBB, Insert, NewReg, 0, Def, *TRI);
+  DefDIs.cloneSink(&*Insert, NewReg);
   Op.setReg(NewReg);
-  MachineInstr *Clone = &*std::prev(Insert);
+  MachineInstr *Clone = getPrevNonDebugInst(&*Insert);
+  assert(Clone);
   LIS.InsertMachineInstrInMaps(*Clone);
   LIS.createAndComputeVirtRegInterval(NewReg);
   MFI.stackifyVReg(MRI, NewReg);
@@ -592,19 +598,13 @@ static MachineInstr *rematerializeCheapDef(
   }
 
   // If that was the last use of the original, delete the original.
-  // Move or clone corresponding DBG_VALUEs to the 'Insert' location.
   if (IsDead) {
     LLVM_DEBUG(dbgs() << " - Deleting original\n");
     SlotIndex Idx = LIS.getInstructionIndex(Def).getRegSlot();
     LIS.removePhysRegDefAt(MCRegister::from(WebAssembly::ARGUMENTS), Idx);
     LIS.removeInterval(Reg);
     LIS.RemoveMachineInstrFromMaps(Def);
-    Def.eraseFromParent();
-
-    DefDIs.move(&*Insert);
-    DefDIs.updateReg(NewReg);
-  } else {
-    DefDIs.clone(&*Insert, NewReg);
+    DefDIs.removeDef();
   }
 
   return Clone;
@@ -636,28 +636,26 @@ static MachineInstr *moveAndTeeForMultiUse(
     MachineRegisterInfo &MRI, const WebAssemblyInstrInfo *TII) {
   LLVM_DEBUG(dbgs() << "Move and tee for multi-use:"; Def->dump());
 
-  WebAssemblyDebugValueManager DefDIs(Def);
+  const auto *RegClass = MRI.getRegClass(Reg);
+  Register TeeReg = MRI.createVirtualRegister(RegClass);
+  Register DefReg = MRI.createVirtualRegister(RegClass);
 
   // Move Def into place.
-  MBB.splice(Insert, &MBB, Def);
+  WebAssemblyDebugValueManager DefDIs(Def);
+  DefDIs.sink(Insert);
   LIS.handleMove(*Def);
 
   // Create the Tee and attach the registers.
-  const auto *RegClass = MRI.getRegClass(Reg);
-  Register TeeReg = MRI.createVirtualRegister(RegClass);
-  Register DefReg = MRI.createVirtualRegister(RegClass);
   MachineOperand &DefMO = Def->getOperand(0);
   MachineInstr *Tee = BuildMI(MBB, Insert, Insert->getDebugLoc(),
                               TII->get(getTeeOpcode(RegClass)), TeeReg)
                           .addReg(Reg, RegState::Define)
                           .addReg(DefReg, getUndefRegState(DefMO.isDead()));
   Op.setReg(TeeReg);
-  DefMO.setReg(DefReg);
+  DefDIs.updateReg(DefReg);
   SlotIndex TeeIdx = LIS.InsertMachineInstrInMaps(*Tee).getRegSlot();
   SlotIndex DefIdx = LIS.getInstructionIndex(*Def).getRegSlot();
 
-  DefDIs.move(Insert);
-
   // Tell LiveIntervals we moved the original vreg def from Def to Tee.
   LiveInterval &LI = LIS.getInterval(Reg);
   LiveInterval::iterator I = LI.FindSegmentContaining(DefIdx);
@@ -674,8 +672,11 @@ static MachineInstr *moveAndTeeForMultiUse(
   imposeStackOrdering(Def);
   imposeStackOrdering(Tee);
 
-  DefDIs.clone(Tee, DefReg);
-  DefDIs.clone(Insert, TeeReg);
+  // Even though 'TeeReg, Reg = TEE ...', has two defs, we don't need to clone
+  // DBG_VALUEs for both of them, given that the latter will cancel the former
+  // anyway. Here we only clone DBG_VALUEs for TeeReg, which will be converted
+  // to a local index in ExplicitLocals pass.
+  DefDIs.cloneSink(Insert, TeeReg, /* CloneDef */ false);
 
   LLVM_DEBUG(dbgs() << " - Replaced register: "; Def->dump());
   LLVM_DEBUG(dbgs() << " - Tee instruction: "; Tee->dump());
@@ -876,7 +877,7 @@ bool WebAssemblyRegStackify::runOnMachineFunction(MachineFunction &MF) {
         bool SameBlock = DefI->getParent() == &MBB;
         bool CanMove = SameBlock && isSafeToMove(Def, &Use, Insert, MFI, MRI) &&
                        !TreeWalker.isOnStack(Reg);
-        if (CanMove && hasOneUse(Reg, DefI, MRI, MDT, LIS)) {
+        if (CanMove && hasOneNonDBGUse(Reg, DefI, MRI, MDT, LIS)) {
           Insert = moveForSingleUse(Reg, Use, DefI, MBB, Insert, LIS, MFI, MRI);
 
           // If we are removing the frame base reg completely, remove the debug
@@ -913,7 +914,7 @@ bool WebAssemblyRegStackify::runOnMachineFunction(MachineFunction &MF) {
           Register DefReg = SubsequentDef->getReg();
           Register UseReg = SubsequentUse->getReg();
           // TODO: This single-use restriction could be relaxed by using tees
-          if (DefReg != UseReg || !MRI.hasOneUse(DefReg))
+          if (DefReg != UseReg || !MRI.hasOneNonDBGUse(DefReg))
             break;
           MFI.stackifyVReg(MRI, DefReg);
           ++SubsequentDef;

diff  --git a/llvm/test/DebugInfo/WebAssembly/dbg-value-move-2.ll b/llvm/test/DebugInfo/WebAssembly/dbg-value-move-2.ll
index a8d60b591d6d8..5eccb316778c6 100644
--- a/llvm/test/DebugInfo/WebAssembly/dbg-value-move-2.ll
+++ b/llvm/test/DebugInfo/WebAssembly/dbg-value-move-2.ll
@@ -3,8 +3,8 @@
 
 ; CHECK: {{.*}}After WebAssembly Register Stackify
 ; CHECK: bb.2.for.body:
-; CHECK: [[REG:%[0-9]+]]:i32 = TEE_I32 {{.*}} fib2.c:6:7
-; CHECK-NEXT: DBG_VALUE [[REG]]:i32, $noreg, !"a", {{.*}} fib2.c:2:13
+; CHECK: [[TEEREG:%[0-9]+]]:i32, %{{[0-9]+}}:i32 = TEE_I32 {{.*}} fib2.c:6:7
+; CHECK-NEXT: DBG_VALUE [[TEEREG]]:i32, $noreg, !"a", {{.*}} fib2.c:2:13
 
 ; ModuleID = 'fib2.bc'
 ; The test generated via: clang --target=wasm32-unknown-unknown-wasm fib2.c -g -O2

diff  --git a/llvm/test/DebugInfo/WebAssembly/dbg-value-move-reg-stackify.mir b/llvm/test/DebugInfo/WebAssembly/dbg-value-move-reg-stackify.mir
deleted file mode 100644
index 041707ef8632e..0000000000000
--- a/llvm/test/DebugInfo/WebAssembly/dbg-value-move-reg-stackify.mir
+++ /dev/null
@@ -1,60 +0,0 @@
-# RUN: llc < %s -run-pass=wasm-reg-stackify -x=mir 2>&1 | FileCheck %s
-
-# CHECK: body:
-# CHECK: dead %3:i32 = I32_WRAP_I64 %0,
-# CHECK-NEXT: DBG_VALUE %1:i32
-# CHECK-NEXT: dead %2:i32 = CALL @bar,
-# CHECK-NEXT: DBG_VALUE %1:i32,
-# CHECK-NEXT: %[[NEWREG:.*]]:i32 = CALL @bar,
-# CHECK-NEXT: DBG_VALUE %[[NEWREG]],
-# CHECK-NEXT: CALL @foo, %[[NEWREG]],
-
---- |
-  target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
-  target triple = "wasm32-unknown-unknown"
-
-  declare void @foo(i32)
-  declare i32 @bar()
-
-  define void @test(i64 %arg) {
-    unreachable
-  }
-
-  !llvm.dbg.cu = !{!0}
-  !llvm.module.flags = !{!4}
-  !0 = distinct !DICompileUnit(language: DW_LANG_Rust, file: !2, producer: "clang LLVM (rustc version 1.30.0-dev)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !1, globals: !1)
-  !1 = !{}
-  !2 = !DIFile(filename: "<unknown>", directory: "")
-  !3 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "&str", file: !2, size: 64, align: 32, elements: !{}, identifier: "111094d970b097647de579f9c509ef08")
-  !4 = !{i32 2, !"Debug Info Version", i32 3}
-  !5 = distinct !DILexicalBlock(scope: !6, file: !2, line: 357, column: 8)
-  !6 = distinct !DISubprogram(name: "testfoo", linkageName: "_testba", scope: !7, file: !2, line: 353, type: !8, isLocal: true, isDefinition: true, scopeLine: 353, flags: DIFlagPrototyped, isOptimized: true, unit: !0, templateParams: !1, retainedNodes: !9)
-  !7 = !DINamespace(name: "ptr", scope: null)
-  !8 = !DISubroutineType(types: !1)
-  !9 = !{!10}
-  !10 = !DILocalVariable(name: "val0", scope: !5, file: !2, line: 357, type: !3, align: 4)
-  !11 = !DILocalVariable(name: "val1", scope: !5, file: !2, line: 358, type: !3, align: 4)
-  !12 = !DILocalVariable(name: "val2", scope: !5, file: !2, line: 359, type: !3, align: 4)
-  !13 = !DILocation(line: 357, column: 12, scope: !5)
-  !14 = !DILocation(line: 358, column: 12, scope: !5)
-  !15 = !DILocation(line: 359, column: 12, scope: !5)
-
----
-name: test
-liveins:
-  - { reg: '$arguments' }
-tracksRegLiveness: true
-body: |
-  bb.0:
-    liveins: $arguments
-    %0:i64 = ARGUMENT_i64 0, implicit $arguments
-    %1:i32 = I32_WRAP_I64 %0:i64, implicit-def dead $arguments
-    DBG_VALUE %1:i32, $noreg, !10, !DIExpression(), debug-location !13; <unknown>:357:12 line no:357
-    %1:i32 = CALL @bar, implicit-def dead $arguments, implicit $sp32, implicit $sp64
-    DBG_VALUE %1:i32, $noreg, !11, !DIExpression(), debug-location !14; <unknown>:357:12 line no:357
-    %1:i32 = CALL @bar, implicit-def dead $arguments, implicit $sp32, implicit $sp64
-    DBG_VALUE %1:i32, $noreg, !12, !DIExpression(), debug-location !15; <unknown>:357:12 line no:357
-    CALL @foo, %1:i32, implicit-def dead $arguments, implicit $sp32, implicit $sp64
-    RETURN implicit-def dead $arguments
-
-...

diff  --git a/llvm/test/DebugInfo/WebAssembly/dbg-value-reg-stackify.mir b/llvm/test/DebugInfo/WebAssembly/dbg-value-reg-stackify.mir
new file mode 100644
index 0000000000000..cb78abd54cbd8
--- /dev/null
+++ b/llvm/test/DebugInfo/WebAssembly/dbg-value-reg-stackify.mir
@@ -0,0 +1,514 @@
+# RUN: llc -run-pass wasm-reg-stackify %s -o - | FileCheck %s
+
+# Tests for DBG_VALUE hanlding in RegStackify + DebugValueManager
+
+--- |
+  target triple = "wasm32-unknown-unknown"
+
+  declare void @use(i32)
+  declare void @use_2(i32, i32)
+
+  define void @sink_simple() {
+    call void @llvm.dbg.value(metadata i32 0, metadata !5, metadata !DIExpression()), !dbg !10
+    call void @llvm.dbg.value(metadata i32 0, metadata !11, metadata !DIExpression()), !dbg !10
+    call void @llvm.dbg.value(metadata i32 0, metadata !12, metadata !DIExpression()), !dbg !10
+    call void @llvm.dbg.value(metadata i32 0, metadata !13, metadata !DIExpression()), !dbg !10
+    ret void
+  }
+  define void @sink_non_consecutive() {
+    unreachable
+  }
+  define void @dont_sink_above_def() {
+    unreachable
+  }
+  define void @sink_to_same_place() {
+    unreachable
+  }
+  define void @cannot_sink_across_same_variable() {
+    unreachable
+  }
+  define void @cannot_sink_across_same_variable2() {
+    unreachable
+  }
+  define void @can_sink_across_same_variable_with_same_const() {
+    unreachable
+  }
+  define void @sink_multiple_defs() {
+    unreachable
+  }
+  define void @clone_same_bb() {
+    unreachable
+  }
+  define void @clone_
diff erent_bb() {
+    unreachable
+  }
+  define void @tee_with_two_use_insts() {
+    unreachable
+  }
+  define void @tee_with_one_inst_with_two_uses() {
+    unreachable
+  }
+  declare void @llvm.dbg.value(metadata, metadata, metadata)
+
+  !llvm.dbg.cu = !{!0}
+  !llvm.module.flags = !{!2, !3, !4}
+
+  ; Note the current mapping variable metadata and their names, which we will
+  ; use in all functions in ths file:
+  ; - "var_a" / VAR_A: !5
+  ; - "var_b" / VAR_B: !11
+  ; - "var_c" / VAR_C: !12
+  ; - "var_d" / VAR_D: !13
+  ; We will use VAR_? in the CHECK lines for robustness in case of metadata
+  ; renumbering, but currently in mir tests we cannot use variable names like
+  ; "var_a" directly in the input, which can be confusing to read.
+
+  !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, emissionKind: FullDebug)
+  !1 = !DIFile(filename: "test.c", directory: "")
+  !2 = !{i32 7, !"Dwarf Version", i32 5}
+  !3 = !{i32 2, !"Debug Info Version", i32 3}
+  !4 = !{i32 1, !"wchar_size", i32 4}
+  !5 = !DILocalVariable(name: "var_a", scope: !6, file: !1, line: 2, type: !9)
+  ; CHECK: ![[VAR_A:[0-9]+]] = !DILocalVariable(name: "var_a"
+  !6 = distinct !DISubprogram(name: "sink_simple", scope: !1, file: !1, line: 1, type: !7, scopeLine: 1, unit: !0)
+  !7 = !DISubroutineType(types: !8)
+  !8 = !{null}
+  !9 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+  !10 = !DILocation(line: 0, scope: !6)
+  !11 = !DILocalVariable(name: "var_b", scope: !6, file: !1, line: 2, type: !9)
+  ; CHECK: ![[VAR_B:[0-9]+]] = !DILocalVariable(name: "var_b"
+  !12 = !DILocalVariable(name: "var_c", scope: !6, file: !1, line: 2, type: !9)
+  ; CHECK: ![[VAR_C:[0-9]+]] = !DILocalVariable(name: "var_c"
+  !13 = !DILocalVariable(name: "var_d", scope: !6, file: !1, line: 2, type: !9)
+  ; CHECK: ![[VAR_D:[0-9]+]] = !DILocalVariable(name: "var_d"
+...
+
+---
+# A simple sinking example.
+# '%0 = CONST_I32 1' will sink to the place before 'CALL %use', and the two
+# DBG_VALUEs will sink with it, leaving the original DBG_VALUEs to be set to
+# undef (= DBG_VALUE $noreg).
+# CHECK-LABEL: name: sink_simple
+name: sink_simple
+liveins:
+  - { reg: '$arguments' }
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $arguments
+    %0:i32 = CONST_I32 1, implicit-def $arguments
+    DBG_VALUE %0:i32, $noreg, !5, !DIExpression(), debug-location !10
+    DBG_VALUE %0:i32, $noreg, !11, !DIExpression(), debug-location !10
+    NOP implicit-def $arguments
+    CALL @use, %0:i32, implicit-def $arguments
+    RETURN implicit-def $arguments
+
+  ; CHECK:      DBG_VALUE $noreg, $noreg, ![[VAR_A]], !DIExpression()
+  ; CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[VAR_B]], !DIExpression()
+  ; CHECK-NEXT: NOP implicit-def $arguments
+  ; CHECK-NEXT: %0:i32 = CONST_I32 1, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
+  ; CHECK-NEXT: DBG_VALUE %0, $noreg, ![[VAR_A]], !DIExpression()
+  ; CHECK-NEXT: DBG_VALUE %0, $noreg, ![[VAR_B]], !DIExpression()
+  ; CHECK-NEXT: CALL @use, %0, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
+  ; CHECK-NEXT: RETURN implicit-def $arguments
+...
+
+---
+# Sinking when DBG_VALUEs are non-consecutive.
+# '%0 = CONST_I32 1' will sink to the place before 'CALL %use', and the two
+# DBG_VALUEs will sink with it, even though they are not consecutive. The
+# original DBG_VALUEs will be set to undef.
+# CHECK-LABEL: name: sink_non_consecutive
+name: sink_non_consecutive
+liveins:
+  - { reg: '$arguments' }
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $arguments
+    %0:i32 = CONST_I32 1, implicit-def $arguments
+    DBG_VALUE %0:i32, $noreg, !5, !DIExpression(), debug-location !10
+    NOP implicit-def $arguments
+    DBG_VALUE %0:i32, $noreg, !11, !DIExpression(), debug-location !10
+    NOP implicit-def $arguments
+    CALL @use, %0:i32, implicit-def $arguments
+    RETURN implicit-def $arguments
+
+  ; CHECK:      DBG_VALUE $noreg, $noreg, ![[VAR_A]], !DIExpression()
+  ; CHECK-NEXT: NOP implicit-def $arguments
+  ; CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[VAR_B]], !DIExpression()
+  ; CHECK-NEXT: NOP implicit-def $arguments
+  ; CHECK-NEXT: %0:i32 = CONST_I32 1, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
+  ; CHECK-NEXT: DBG_VALUE %0, $noreg, ![[VAR_A]], !DIExpression()
+  ; CHECK-NEXT: DBG_VALUE %0, $noreg, ![[VAR_B]], !DIExpression()
+  ; CHECK-NEXT: CALL @use, %0, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
+  ; CHECK-NEXT: RETURN implicit-def $arguments
+...
+
+---
+# Only DBG_VALUEs following a def should be sunk together.
+# '%0 = CONST_I32 1' will sink to the place before 'CALL %use', but the
+# DBG_VALUE above it should be untouched.
+# CHECK-LABEL: name: dont_sink_above_def
+name: dont_sink_above_def
+liveins:
+  - { reg: '$arguments' }
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $arguments
+    DBG_VALUE %0:i32, $noreg, !5, !DIExpression(), debug-location !10
+    %0:i32 = CONST_I32 1, implicit-def $arguments
+    NOP implicit-def $arguments
+    CALL @use, %0:i32, implicit-def $arguments
+    RETURN implicit-def $arguments
+
+  ; CHECK:      DBG_VALUE %0, $noreg, ![[VAR_A]], !DIExpression()
+  ; CHECK-NEXT: NOP implicit-def $arguments
+  ; CHECK-NEXT: %0:i32 = CONST_I32 1, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
+  ; CHECK-NEXT: CALL @use, %0, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
+  ; CHECK-NEXT: RETURN implicit-def $arguments
+...
+
+---
+# A sink no-op case.
+# '%0 = CONST_I32 1' will sink to the place before 'CALL %use', but it's already
+# right before the CALL so it should be effectively a no-op. But currently
+# sinking happens anyway so this will create unnecessary two undef DBG_VALUEs.
+# This increases the number of DBG_VALUEs but doesn't hurt the coverage or
+# generate incorrect debug info. TODO Improve this?
+# CHECK-LABEL: name: sink_to_same_place
+name: sink_to_same_place
+liveins:
+  - { reg: '$arguments' }
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $arguments
+    %0:i32 = CONST_I32 1, implicit-def $arguments
+    DBG_VALUE %0:i32, $noreg, !5, !DIExpression(), debug-location !10
+    DBG_VALUE %0:i32, $noreg, !11, !DIExpression(), debug-location !10
+    CALL @use, %0:i32, implicit-def $arguments
+    RETURN implicit-def $arguments
+
+  ; CHECK:      DBG_VALUE $noreg, $noreg, ![[VAR_A]], !DIExpression()
+  ; CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[VAR_B]], !DIExpression()
+  ; CHECK-NEXT: %0:i32 = CONST_I32 1, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
+  ; CHECK-NEXT: DBG_VALUE %0, $noreg, ![[VAR_A]], !DIExpression()
+  ; CHECK-NEXT: DBG_VALUE %0, $noreg, ![[VAR_B]], !DIExpression()
+  ; CHECK-NEXT: CALL @use, %0, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
+  ; CHECK-NEXT: RETURN implicit-def $arguments
+...
+
+---
+# A DBG_VALUE cannot be sunk across another DBG_VALUE that has the same
+# DebugVariable, because it will reorder assignments.
+# '%0 = CONST_I32 1' will sink to the place before 'CALL %use'. But from the two
+# DBG_VALUEs following it, only the DBG_VALUE for "var_b" can sink with it,
+# because there is another 'DBG_VALUE 10' for "var_a" in the middle.
+# CHECK-LABEL: name: cannot_sink_across_same_variable
+name: cannot_sink_across_same_variable
+liveins:
+  - { reg: '$arguments' }
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $arguments
+    %0:i32 = CONST_I32 1, implicit-def $arguments
+    DBG_VALUE %0:i32, $noreg, !5, !DIExpression(), debug-location !10
+    DBG_VALUE %0:i32, $noreg, !11, !DIExpression(), debug-location !10
+    DBG_VALUE 10, $noreg, !5, !DIExpression(), debug-location !10
+    NOP implicit-def $arguments
+    CALL @use, %0:i32, implicit-def $arguments
+    RETURN implicit-def $arguments
+
+  ; CHECK:      DBG_VALUE $noreg, $noreg, ![[VAR_A]], !DIExpression()
+  ; CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[VAR_B]], !DIExpression()
+  ; CHECK-NEXT: DBG_VALUE 10, $noreg, ![[VAR_A]], !DIExpression()
+  ; CHECK-NEXT: NOP implicit-def $arguments
+  ; CHECK-NEXT: %0:i32 = CONST_I32 1, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
+  ; CHECK-NEXT: DBG_VALUE %0, $noreg, ![[VAR_B]], !DIExpression()
+  ; CHECK-NEXT: CALL @use, %0, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
+  ; CHECK-NEXT: RETURN implicit-def $arguments
+...
+
+---
+# Another case in which a DBG_VALUE cannot be sunk across another DBG_VALUE with
+# the same DebugVariable, because it will reorder assignments.
+# '%0 = CONST_I32 1' will sink to the place before 'CALL %use'. But from the two
+# DBG_VALUEs following it, only the DBG_VALUE for "var_b" can sink with it,
+# because there is another 'DBG_VALUE %1, "var_a"' in the middle.
+# CHECK-LABEL: name: cannot_sink_across_same_variable2
+name: cannot_sink_across_same_variable2
+liveins:
+  - { reg: '$arguments' }
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $arguments
+    %0:i32 = CONST_I32 1, implicit-def $arguments
+    %1:i32 = CONST_I32 2, implicit-def $arguments
+    DBG_VALUE %0:i32, $noreg, !5, !DIExpression(), debug-location !10
+    DBG_VALUE %0:i32, $noreg, !11, !DIExpression(), debug-location !10
+    DBG_VALUE %1:i32, $noreg, !5, !DIExpression(), debug-location !10
+    NOP implicit-def $arguments
+    CALL @use, %0:i32, implicit-def $arguments
+    RETURN implicit-def $arguments
+
+  ; CHECK:      %1:i32 = CONST_I32 2, implicit-def $arguments
+  ; CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[VAR_A]], !DIExpression()
+  ; CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[VAR_B]], !DIExpression()
+  ; CHECK-NEXT: DBG_VALUE %1, $noreg, ![[VAR_A]], !DIExpression()
+  ; CHECK-NEXT: NOP implicit-def $arguments
+  ; CHECK-NEXT: %0:i32 = CONST_I32 1, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
+  ; CHECK-NEXT: DBG_VALUE %0, $noreg, ![[VAR_B]], !DIExpression()
+  ; CHECK-NEXT: CALL @use, %0, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
+  ; CHECK-NEXT: RETURN implicit-def $arguments
+...
+
+---
+# There is a exception in which a DBG_VALUE can be sunk across another DBG_VALUE
+# with the same DebugVariable: when the interfering DBG_VALUE refers to the same
+# CONST_[I32/I64/F32/F64] instruction, in which case we don't reorder
+# assignments.
+#
+# This is the same test with the previous one with one 
diff erence: %1 has the
+# same CONST instruction with %0 'CONST_I32 1'. We can sink the DBG_VALUE for
+# "var_a" here as well.
+# CHECK-LABEL: name: can_sink_across_same_variable_with_same_const
+name: can_sink_across_same_variable_with_same_const
+liveins:
+  - { reg: '$arguments' }
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $arguments
+    %0:i32 = CONST_I32 1, implicit-def $arguments
+    %1:i32 = CONST_I32 1, implicit-def $arguments ; Same CONST_I32
+    DBG_VALUE %0:i32, $noreg, !5, !DIExpression(), debug-location !10
+    DBG_VALUE %0:i32, $noreg, !11, !DIExpression(), debug-location !10
+    DBG_VALUE %1:i32, $noreg, !5, !DIExpression(), debug-location !10
+    NOP implicit-def $arguments
+    CALL @use, %0:i32, implicit-def $arguments
+    RETURN implicit-def $arguments
+
+  ; CHECK:      %1:i32 = CONST_I32 1, implicit-def $arguments
+  ; CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[VAR_A]], !DIExpression()
+  ; CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[VAR_B]], !DIExpression()
+  ; CHECK-NEXT: DBG_VALUE %1, $noreg, ![[VAR_A]], !DIExpression()
+  ; CHECK-NEXT: NOP implicit-def $arguments
+  ; CHECK-NEXT: %0:i32 = CONST_I32 1, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
+  ; CHECK-NEXT: DBG_VALUE %0, $noreg, ![[VAR_A]], !DIExpression()
+  ; CHECK-NEXT: DBG_VALUE %0, $noreg, ![[VAR_B]], !DIExpression()
+  ; CHECK-NEXT: CALL @use, %0, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
+  ; CHECK-NEXT: RETURN implicit-def $arguments
+...
+
+---
+# Both %0 and %1 will be sunk to the place before ADD_I32. DBG_VALUEs associated
+# with those two defs will be sunk as well, leaving the original DBG_VALUEs set
+# to undef.
+# CHECK-LABEL: name: sink_multiple_defs
+name: sink_multiple_defs
+liveins:
+  - { reg: '$arguments' }
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $arguments
+    %0:i32 = CONST_I32 1, implicit-def $arguments
+    DBG_VALUE %0:i32, $noreg, !5, !DIExpression(), debug-location !10
+    DBG_VALUE %0:i32, $noreg, !11, !DIExpression(), debug-location !10
+    NOP implicit-def $arguments
+    %1:i32 = CONST_I32 2, implicit-def $arguments
+    DBG_VALUE %1:i32, $noreg, !12, !DIExpression(), debug-location !10
+    DBG_VALUE %1:i32, $noreg, !13, !DIExpression(), debug-location !10
+    NOP implicit-def $arguments
+    %2:i32 = ADD_I32 %0:i32, %1:i32, implicit-def $arguments
+    RETURN implicit-def $arguments
+
+  ; CHECK:      DBG_VALUE $noreg, $noreg, ![[VAR_A]], !DIExpression()
+  ; CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[VAR_B]], !DIExpression()
+  ; CHECK-NEXT: NOP implicit-def $arguments
+  ; CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[VAR_C]], !DIExpression()
+  ; CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[VAR_D]], !DIExpression()
+  ; CHECK-NEXT: NOP implicit-def $arguments
+  ; CHECK-NEXT: %0:i32 = CONST_I32 1, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
+  ; CHECK-NEXT: DBG_VALUE %0, $noreg, ![[VAR_A]], !DIExpression()
+  ; CHECK-NEXT: DBG_VALUE %0, $noreg, ![[VAR_B]], !DIExpression()
+  ; CHECK-NEXT: %1:i32 = CONST_I32 2, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
+  ; CHECK-NEXT: DBG_VALUE %1, $noreg, ![[VAR_C]], !DIExpression()
+  ; CHECK-NEXT: DBG_VALUE %1, $noreg, ![[VAR_D]], !DIExpression()
+  ; CHECK-NEXT: dead %2:i32 = ADD_I32 %0, %1, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
+  ; CHECK-NEXT: RETURN implicit-def $arguments
+...
+
+---
+# A simple cloning example.
+# When processing the second 'CALL @use', because %0 has multiple uses, the def
+# '%0 = CONST_I32 1' is cloned before the CALL, along with its DBG_VALUEs. And
+# then when processing the first 'CALL @use', by that time %0 has only one use
+# remaining, so it is just sink with the DBG_VALUEs, leaving the original
+# DBG_VALUEs undef.
+# CHECK-LABEL: name: clone_same_bb
+name: clone_same_bb
+liveins:
+  - { reg: '$arguments' }
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $arguments
+    %0:i32 = CONST_I32 1, implicit-def $arguments
+    DBG_VALUE %0:i32, $noreg, !5, !DIExpression(), debug-location !10
+    DBG_VALUE %0:i32, $noreg, !11, !DIExpression(), debug-location !10
+    NOP implicit-def $arguments
+    CALL @use, %0:i32, implicit-def $arguments
+    CALL @use, %0:i32, implicit-def $arguments
+    RETURN implicit-def $arguments
+
+  ; CHECK:      DBG_VALUE $noreg, $noreg, ![[VAR_A]], !DIExpression()
+  ; CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[VAR_B]], !DIExpression()
+  ; CHECK-NEXT: NOP implicit-def $arguments
+  ; CHECK-NEXT: %0:i32 = CONST_I32 1, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
+  ; CHECK-NEXT: DBG_VALUE %0, $noreg, ![[VAR_A]], !DIExpression()
+  ; CHECK-NEXT: DBG_VALUE %0, $noreg, ![[VAR_B]], !DIExpression()
+  ; CHECK-NEXT: CALL @use, %0, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
+  ; CHECK-NEXT: %1:i32 = CONST_I32 1, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
+  ; CHECK-NEXT: DBG_VALUE %1, $noreg, ![[VAR_A]], !DIExpression()
+  ; CHECK-NEXT: DBG_VALUE %1, $noreg, ![[VAR_B]], !DIExpression()
+  ; CHECK-NEXT: CALL @use, %1, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
+  ; CHECK-NEXT: RETURN implicit-def $arguments
+...
+
+---
+# Cloning across 
diff erent BBs.
+# First, when bb.0's 'CALL @use' is procssed, '%0 = CONST_I32 1' and its
+# DBG_VALUEs are cloned before the CALL. And when bb.1's 'CALL @use' is
+# processed, '%0 = CONST_I32 1' and its DBG_VALUEs are cloned to bb.1 this time.
+# Even though there are (previously cloned) DBG_VALUEs for "var_a" and "var_b"
+# in the middle, it's fine because they point to the same 'CONST_I32 1'
+# instruction.
+# After the second cloning, the original '%0 = CONST_I32 1' is removed because
+# it doesn't have any users anymore, leaving its original DBG_VALUEs as undef.
+# CHECK-LABEL: name: clone_
diff erent_bb
+name: clone_
diff erent_bb
+liveins:
+  - { reg: '$arguments' }
+tracksRegLiveness: true
+body: |
+  bb.0:
+    successors: %bb.1
+    liveins: $arguments
+    %0:i32 = CONST_I32 1, implicit-def $arguments
+    DBG_VALUE %0:i32, $noreg, !5, !DIExpression(), debug-location !10
+    DBG_VALUE %0:i32, $noreg, !11, !DIExpression(), debug-location !10
+    NOP implicit-def $arguments
+    CALL @use, %0:i32, implicit-def $arguments
+    BR %bb.1, implicit-def $arguments
+
+  ; CHECK:    bb.0:
+  ; CHECK:      DBG_VALUE $noreg, $noreg, ![[VAR_A]], !DIExpression()
+  ; CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[VAR_B]], !DIExpression()
+  ; CHECK-NEXT: NOP implicit-def $arguments
+  ; CHECK-NEXT: %1:i32 = CONST_I32 1, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
+  ; CHECK-NEXT: DBG_VALUE %1, $noreg, ![[VAR_A]], !DIExpression()
+  ; CHECK-NEXT: DBG_VALUE %1, $noreg, ![[VAR_B]], !DIExpression()
+  ; CHECK-NEXT: CALL @use, %1, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
+  ; CHECK-NEXT: BR %bb.1, implicit-def $arguments
+
+  bb.1:
+  ; predecessors: %bb.0
+    CALL @use, %0:i32, implicit-def $arguments
+    RETURN implicit-def $arguments
+
+  ; CHECK:    bb.1:
+  ; CHECK:      %2:i32 = CONST_I32 1, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
+  ; CHECK-NEXT: DBG_VALUE %2, $noreg, ![[VAR_A]], !DIExpression()
+  ; CHECK-NEXT: DBG_VALUE %2, $noreg, ![[VAR_B]], !DIExpression()
+  ; CHECK-NEXT: CALL @use, %2, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
+  ; CHECK-NEXT: RETURN implicit-def $arguments
+...
+
+---
+# TEE conversion example.
+# Convert this form:
+#   Reg = INST ...        // Def
+#   DBG_VALUE Reg, ...
+#   INST ..., Reg, ...    // Insert
+#   INST ..., Reg, ...
+# to
+#   DefReg = INST ...     // Def (to become the new Insert)
+#   DBG_VALUE DefReg, ...
+#   TeeReg, Reg = TEE_... DefReg
+#   DBG_VALUE TeeReg, ...
+#   INST ..., TeeReg, ... // Insert
+#   INST ..., Reg, ...
+# CHECK-LABEL: name: tee_with_two_use_insts
+name: tee_with_two_use_insts
+liveins:
+  - { reg: '$arguments' }
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $arguments
+    %0:i32 = ARGUMENT_i32 0, implicit $arguments
+    %1:i32 = ARGUMENT_i32 1, implicit $arguments
+    %2:i32 = MUL_I32 %1:i32, %0:i32, implicit-def $arguments
+    DBG_VALUE %2:i32, $noreg, !5, !DIExpression(), debug-location !10
+    DBG_VALUE %2:i32, $noreg, !11, !DIExpression(), debug-location !10
+    NOP implicit-def $arguments
+    CALL @use, %2:i32, implicit-def $arguments
+    CALL @use, %2:i32, implicit-def $arguments
+    RETURN implicit-def $arguments
+
+  ; CHECK:      %0:i32 = ARGUMENT_i32 0, implicit $arguments
+  ; CHECK-NEXT: %1:i32 = ARGUMENT_i32 1, implicit $arguments
+  ; CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[VAR_A]], !DIExpression()
+  ; CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[VAR_B]], !DIExpression()
+  ; CHECK-NEXT: NOP implicit-def $arguments
+  ; CHECK-NEXT: %4:i32 = MUL_I32 %1, %0, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
+  ; CHECK-NEXT: DBG_VALUE %4, $noreg, ![[VAR_A]], !DIExpression()
+  ; CHECK-NEXT: DBG_VALUE %4, $noreg, ![[VAR_B]], !DIExpression()
+  ; CHECK-NEXT: %3:i32, %2:i32 = TEE_I32 %4, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
+  ; CHECK-NEXT: DBG_VALUE %3, $noreg, ![[VAR_A]], !DIExpression()
+  ; CHECK-NEXT: DBG_VALUE %3, $noreg, ![[VAR_B]], !DIExpression()
+  ; CHECK-NEXT: CALL @use, %3, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
+  ; CHECK-NEXT: CALL @use, %2, implicit-def $arguments
+  ; CHECK-NEXT: RETURN implicit-def $arguments
+...
+
+---
+# Another TEE conversion example. The previous example had two instructions
+# that use a single register, whereas this has one instructions that has two
+# same use operands. The resulting transformation is the same.
+# CHECK-LABEL: name: tee_with_one_inst_with_two_uses
+name: tee_with_one_inst_with_two_uses
+liveins:
+  - { reg: '$arguments' }
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $arguments
+    %0:i32 = ARGUMENT_i32 0, implicit $arguments
+    %1:i32 = ARGUMENT_i32 1, implicit $arguments
+    %2:i32 = MUL_I32 %1:i32, %0:i32, implicit-def $arguments
+    DBG_VALUE %2:i32, $noreg, !5, !DIExpression(), debug-location !10
+    DBG_VALUE %2:i32, $noreg, !11, !DIExpression(), debug-location !10
+    NOP implicit-def $arguments
+    CALL @use_2, %2:i32, %2:i32, implicit-def $arguments
+    RETURN implicit-def $arguments
+
+  ; CHECK:      %0:i32 = ARGUMENT_i32 0, implicit $arguments
+  ; CHECK-NEXT: %1:i32 = ARGUMENT_i32 1, implicit $arguments
+  ; CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[VAR_A]], !DIExpression()
+  ; CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[VAR_B]], !DIExpression()
+  ; CHECK-NEXT: NOP implicit-def $arguments
+  ; CHECK-NEXT: %4:i32 = MUL_I32 %1, %0, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
+  ; CHECK-NEXT: DBG_VALUE %4, $noreg, ![[VAR_A]], !DIExpression()
+  ; CHECK-NEXT: DBG_VALUE %4, $noreg, ![[VAR_B]], !DIExpression()
+  ; CHECK-NEXT: %3:i32, %2:i32 = TEE_I32 %4, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
+  ; CHECK-NEXT: DBG_VALUE %3, $noreg, ![[VAR_A]], !DIExpression()
+  ; CHECK-NEXT: DBG_VALUE %3, $noreg, ![[VAR_B]], !DIExpression()
+  ; CHECK-NEXT: CALL @use_2, %3, %2, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
+  ; CHECK-NEXT: RETURN implicit-def $arguments
+...


        


More information about the llvm-commits mailing list