[llvm] 9e50194 - [WebAssembly] Undef invalid DBG_VALUEs after RegColoring

Heejin Ahn via llvm-commits llvm-commits at lists.llvm.org
Thu Jun 8 16:56:41 PDT 2023


Author: Heejin Ahn
Date: 2023-06-08T16:56:25-07:00
New Revision: 9e501945ca4ec3f8dbe6992f155426f7390caa3c

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

LOG: [WebAssembly] Undef invalid DBG_VALUEs after RegColoring

After register coalescing, some `DBG_VALUE`s can have incorrect info.
For example, if a `DBG_VALUE` has a register operand `%0`, but it
resides in a live range of `%1`, it has incorrect info after `%0` and
`%1` are coalesced. See the comments for more details.

This does not have meaningful changes on our variable debug info
coverage or compilation time, which is good news.

Reviewed By: dschuff

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

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

Modified: 
    llvm/lib/Target/WebAssembly/WebAssemblyRegColoring.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Target/WebAssembly/WebAssemblyRegColoring.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyRegColoring.cpp
index 5252db4858b97..4a6d37d7052e5 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyRegColoring.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyRegColoring.cpp
@@ -72,6 +72,152 @@ static float computeWeight(const MachineRegisterInfo *MRI,
   return Weight;
 }
 
+// Create a map of "Register -> vector of <SlotIndex, DBG_VALUE>".
+// The SlotIndex is the slot index of the next non-debug instruction or the end
+// of a BB, because DBG_VALUE's don't have slot index themselves.
+// Adapted from RegisterCoalescer::buildVRegToDbgValueMap.
+static DenseMap<Register, std::vector<std::pair<SlotIndex, MachineInstr *>>>
+buildVRegToDbgValueMap(MachineFunction &MF, const LiveIntervals *Liveness) {
+  DenseMap<Register, std::vector<std::pair<SlotIndex, MachineInstr *>>>
+      DbgVRegToValues;
+  const SlotIndexes *Slots = Liveness->getSlotIndexes();
+  SmallVector<MachineInstr *, 8> ToInsert;
+
+  // After collecting a block of DBG_VALUEs into ToInsert, enter them into the
+  // map.
+  auto CloseNewDVRange = [&DbgVRegToValues, &ToInsert](SlotIndex Slot) {
+    for (auto *X : ToInsert) {
+      for (const auto &Op : X->debug_operands()) {
+        if (Op.isReg() && Op.getReg().isVirtual())
+          DbgVRegToValues[Op.getReg()].push_back({Slot, X});
+      }
+    }
+
+    ToInsert.clear();
+  };
+
+  // Iterate over all instructions, collecting them into the ToInsert vector.
+  // Once a non-debug instruction is found, record the slot index of the
+  // collected DBG_VALUEs.
+  for (auto &MBB : MF) {
+    SlotIndex CurrentSlot = Slots->getMBBStartIdx(&MBB);
+
+    for (auto &MI : MBB) {
+      if (MI.isDebugValue()) {
+        if (any_of(MI.debug_operands(), [](const MachineOperand &MO) {
+              return MO.isReg() && MO.getReg().isVirtual();
+            }))
+          ToInsert.push_back(&MI);
+      } else if (!MI.isDebugOrPseudoInstr()) {
+        CurrentSlot = Slots->getInstructionIndex(MI);
+        CloseNewDVRange(CurrentSlot);
+      }
+    }
+
+    // Close range of DBG_VALUEs at the end of blocks.
+    CloseNewDVRange(Slots->getMBBEndIdx(&MBB));
+  }
+
+  // Sort all DBG_VALUEs we've seen by slot number.
+  for (auto &Pair : DbgVRegToValues)
+    llvm::sort(Pair.second);
+  return DbgVRegToValues;
+}
+
+// After register coalescing, some DBG_VALUEs will be invalid. Set them undef.
+// This function has to run before the actual coalescing, i.e., the register
+// changes.
+static void undefInvalidDbgValues(
+    const LiveIntervals *Liveness,
+    const ArrayRef<SmallVector<LiveInterval *, 4>> &Assignments,
+    DenseMap<Register, std::vector<std::pair<SlotIndex, MachineInstr *>>>
+        &DbgVRegToValues) {
+#ifndef NDEBUG
+  DenseSet<Register> SeenRegs;
+#endif
+  for (size_t I = 0, E = Assignments.size(); I < E; ++I) {
+    const auto &CoalescedIntervals = Assignments[I];
+    if (CoalescedIntervals.empty())
+      continue;
+    for (LiveInterval *LI : CoalescedIntervals) {
+      Register Reg = LI->reg();
+#ifndef NDEBUG
+      // Ensure we don't process the same register twice
+      assert(SeenRegs.insert(Reg).second);
+#endif
+      auto RegMapIt = DbgVRegToValues.find(Reg);
+      if (RegMapIt == DbgVRegToValues.end())
+        continue;
+      SlotIndex LastSlot;
+      bool LastUndefResult = false;
+      for (auto [Slot, DbgValue] : RegMapIt->second) {
+        // All consecutive DBG_VALUEs have the same slot because the slot
+        // indices they have is the one for the first non-debug instruction
+        // after it, because DBG_VALUEs don't have slot index themselves. Before
+        // doing live range queries, quickly check if the current DBG_VALUE has
+        // the same slot index as the previous one, in which case we should do
+        // the same. Note that RegMapIt->second, the vector of {SlotIndex,
+        // DBG_VALUE}, is sorted by SlotIndex, which is necessary for this
+        // check.
+        if (Slot == LastSlot) {
+          if (LastUndefResult) {
+            LLVM_DEBUG(dbgs() << "Undefed: " << *DbgValue << "\n");
+            DbgValue->setDebugValueUndef();
+          }
+          continue;
+        }
+        LastSlot = Slot;
+        LastUndefResult = false;
+        for (LiveInterval *OtherLI : CoalescedIntervals) {
+          if (LI == OtherLI)
+            continue;
+
+          // This DBG_VALUE has 'Reg' (the current LiveInterval's register) as
+          // its operand. If this DBG_VALUE's slot index is within other
+          // registers' live ranges, this DBG_VALUE should be undefed. For
+          // example, suppose %0 and %1 are to be coalesced into %0.
+          //   ; %0's live range starts
+          //   %0 = value_0
+          //   DBG_VALUE %0, !"a", ...      (a)
+          //   DBG_VALUE %1, !"b", ...      (b)
+          //   use %0
+          //   ; %0's live range ends
+          //   ...
+          //   ; %1's live range starts
+          //   %1 = value_1
+          //   DBG_VALUE %0, !"c", ...      (c)
+          //   DBG_VALUE %1, !"d", ...      (d)
+          //   use %1
+          //   ; %1's live range ends
+          //
+          // In this code, (b) and (c) should be set to undef. After the two
+          // registers are coalesced, (b) will incorrectly say the variable
+          // "b"'s value is 'value_0', and (c) will also incorrectly say the
+          // variable "c"'s value is value_1. Note it doesn't actually matter
+          // which register they are coalesced into (%0 or %1); (b) and (c)
+          // should be set to undef as well if they are coalesced into %1.
+          //
+          // This happens DBG_VALUEs are not included when computing live
+          // ranges.
+          //
+          // Note that it is not possible for this DBG_VALUE to be
+          // simultaneously within 'Reg''s live range and one of other coalesced
+          // registers' live ranges because if their live ranges overlapped they
+          // would have not been selected as a coalescing candidate in the first
+          // place.
+          auto *SegmentIt = OtherLI->find(Slot);
+          if (SegmentIt != OtherLI->end() && SegmentIt->contains(Slot)) {
+            LLVM_DEBUG(dbgs() << "Undefed: " << *DbgValue << "\n");
+            DbgValue->setDebugValueUndef();
+            LastUndefResult = true;
+            break;
+          }
+        }
+      }
+    }
+  }
+}
+
 bool WebAssemblyRegColoring::runOnMachineFunction(MachineFunction &MF) {
   LLVM_DEBUG({
     dbgs() << "********** Register Coloring **********\n"
@@ -91,11 +237,17 @@ bool WebAssemblyRegColoring::runOnMachineFunction(MachineFunction &MF) {
       &getAnalysis<MachineBlockFrequencyInfo>();
   WebAssemblyFunctionInfo &MFI = *MF.getInfo<WebAssemblyFunctionInfo>();
 
+  // We don't preserve SSA form.
+  MRI->leaveSSA();
+
   // Gather all register intervals into a list and sort them.
   unsigned NumVRegs = MRI->getNumVirtRegs();
   SmallVector<LiveInterval *, 0> SortedIntervals;
   SortedIntervals.reserve(NumVRegs);
 
+  // Record DBG_VALUEs and their SlotIndexes.
+  auto DbgVRegToValues = buildVRegToDbgValueMap(MF, Liveness);
+
   LLVM_DEBUG(dbgs() << "Interesting register intervals:\n");
   for (unsigned I = 0; I < NumVRegs; ++I) {
     Register VReg = Register::index2VirtReg(I);
@@ -166,6 +318,9 @@ bool WebAssemblyRegColoring::runOnMachineFunction(MachineFunction &MF) {
   if (!Changed)
     return false;
 
+  // Set DBG_VALUEs that will be invalid after coalescing to undef.
+  undefInvalidDbgValues(Liveness, Assignments, DbgVRegToValues);
+
   // Rewrite register operands.
   for (size_t I = 0, E = SortedIntervals.size(); I < E; ++I) {
     Register Old = SortedIntervals[I]->reg();

diff  --git a/llvm/test/DebugInfo/WebAssembly/dbg-value-reg-coloring.mir b/llvm/test/DebugInfo/WebAssembly/dbg-value-reg-coloring.mir
new file mode 100644
index 0000000000000..6c0c0e2861ef7
--- /dev/null
+++ b/llvm/test/DebugInfo/WebAssembly/dbg-value-reg-coloring.mir
@@ -0,0 +1,210 @@
+# RUN: llc -run-pass wasm-reg-coloring %s -o - | FileCheck %s
+
+# Tests for invalid DBG_VALUE set to undef after in RegColoring
+
+--- |
+  target triple = "wasm32-unknown-unknown"
+
+  declare void @use(i32)
+
+  define void @coalesce_test_0() {
+    call void @llvm.dbg.value(metadata i32 0, metadata !5, metadata !DIExpression()), !dbg !10
+    ret void
+  }
+  define void @coalesce_test_1() {
+    unreachable
+  }
+  define void @coalesce_test_2() {
+    unreachable
+  }
+  define void @coalesce_test_3() {
+    unreachable
+  }
+  declare void @llvm.dbg.value(metadata, metadata, metadata)
+
+  !llvm.dbg.cu = !{!0}
+  !llvm.module.flags = !{!2, !3, !4}
+
+  !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)
+  !6 = distinct !DISubprogram(name: "coalesce_test_0", 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)
+  !12 = !DILocalVariable(name: "var_c", scope: !6, file: !1, line: 2, type: !9)
+...
+
+---
+# %0 and %1 are coalesced in this test
+# CHECK-LABEL: name: coalesce_test_0
+name: coalesce_test_0
+liveins:
+  - { reg: '$arguments' }
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $arguments
+    %0:i32 = CONST_I32 0, implicit-def $arguments
+    ; This should remain the same
+    DBG_VALUE %0, $noreg, !5, !DIExpression(), debug-location !10
+    ; This should be set to undef, because this is within %0's live range
+    DBG_VALUE %1, $noreg, !10, !DIExpression(), debug-location !10
+    CALL @use, %0, implicit-def $arguments
+    NOP implicit-def $arguments
+    %1:i32 = CONST_I32 1, implicit-def $arguments
+    ; This should be set to undef, because this is within %1's live range
+    DBG_VALUE %0, $noreg, !5, !DIExpression(), debug-location !10
+    ; This should remain the same
+    DBG_VALUE %1, $noreg, !10, !DIExpression(), debug-location !10
+    CALL @use, %1, implicit-def $arguments
+    RETURN implicit-def $arguments
+
+  ; CHECK:      %0:i32 = CONST_I32 0
+  ; CHECK-NEXT: DBG_VALUE %0, $noreg
+  ; CHECK-NEXT: DBG_VALUE $noreg, $noreg
+  ; CHECK-NEXT: CALL @use, %0
+  ; CHECK-NEXT: NOP
+  ; CHECK-NEXT: %0:i32 = CONST_I32 1
+  ; CHECK-NEXT: DBG_VALUE $noreg, $noreg
+  ; CHECK-NEXT: DBG_VALUE %0, $noreg
+  ; CHECK-NEXT: CALL @use, %0
+  ; CHECK-NEXT: RETURN implicit-def $arguments
+...
+
+---
+# A similar test with above, only with more consecutive DBG_VALUEs. These
+# consecutive DBG_VALUEs will be handled with a quick last result check.
+# CHECK-LABEL: name: coalesce_test_1
+name: coalesce_test_1
+liveins:
+  - { reg: '$arguments' }
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $arguments
+    %0:i32 = CONST_I32 0, implicit-def $arguments
+    ; All DBG_VALUE %1s in %0's live range will be set to undef
+    DBG_VALUE %1, $noreg, !10, !DIExpression(), debug-location !10
+    DBG_VALUE %1, $noreg, !10, !DIExpression(), debug-location !10
+    DBG_VALUE %0, $noreg, !5, !DIExpression(), debug-location !10
+    DBG_VALUE %1, $noreg, !10, !DIExpression(), debug-location !10
+    CALL @use, %0, implicit-def $arguments
+    NOP implicit-def $arguments
+    %1:i32 = CONST_I32 1, implicit-def $arguments
+    ; All DBG_VALUE %0s in %1's live range will be set to undef
+    DBG_VALUE %0, $noreg, !5, !DIExpression(), debug-location !10
+    DBG_VALUE %0, $noreg, !5, !DIExpression(), debug-location !10
+    DBG_VALUE %1, $noreg, !10, !DIExpression(), debug-location !10
+    DBG_VALUE %0, $noreg, !5, !DIExpression(), debug-location !10
+    CALL @use, %1, implicit-def $arguments
+    RETURN implicit-def $arguments
+
+  ; CHECK:      %0:i32 = CONST_I32 0
+  ; CHECK-NEXT: DBG_VALUE $noreg, $noreg
+  ; CHECK-NEXT: DBG_VALUE $noreg, $noreg
+  ; CHECK-NEXT: DBG_VALUE %0, $noreg
+  ; CHECK-NEXT: DBG_VALUE $noreg, $noreg
+  ; CHECK-NEXT: CALL @use, %0
+  ; CHECK-NEXT: NOP
+  ; CHECK-NEXT: %0:i32 = CONST_I32 1
+  ; CHECK-NEXT: DBG_VALUE $noreg, $noreg
+  ; CHECK-NEXT: DBG_VALUE $noreg, $noreg
+  ; CHECK-NEXT: DBG_VALUE %0, $noreg
+  ; CHECK-NEXT: DBG_VALUE $noreg, $noreg
+  ; CHECK-NEXT: CALL @use, %0
+  ; CHECK-NEXT: RETURN implicit-def $arguments
+...
+
+---
+# The same with coalesce_test_0, but the two registers' live ranges are in
+# 
diff erent BBs.
+# CHECK-LABEL: name: coalesce_test_2
+name: coalesce_test_2
+liveins:
+  - { reg: '$arguments' }
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $arguments
+    %0:i32 = CONST_I32 0, implicit-def $arguments
+    DBG_VALUE %0, $noreg, !5, !DIExpression(), debug-location !10
+    DBG_VALUE %1, $noreg, !10, !DIExpression(), debug-location !10
+    CALL @use, %0, implicit-def $arguments
+    BR %bb.1, implicit-def $arguments
+
+  ; CHECK: bb.0:
+  ; CHECK:      %0:i32 = CONST_I32 0
+  ; CHECK-NEXT: DBG_VALUE %0, $noreg
+  ; CHECK-NEXT: DBG_VALUE $noreg, $noreg
+  ; CHECK-NEXT: CALL @use, %0
+  ; CHECK-NEXT: BR %bb.1
+
+  bb.1:
+    %1:i32 = CONST_I32 1, implicit-def $arguments
+    DBG_VALUE %0, $noreg, !5, !DIExpression(), debug-location !10
+    DBG_VALUE %1, $noreg, !10, !DIExpression(), debug-location !10
+    CALL @use, %1, implicit-def $arguments
+    RETURN implicit-def $arguments
+
+  ; CHECK: bb.1:
+  ; CHECK-NEXT: %0:i32 = CONST_I32 1
+  ; CHECK-NEXT: DBG_VALUE $noreg, $noreg
+  ; CHECK-NEXT: DBG_VALUE %0, $noreg
+  ; CHECK-NEXT: CALL @use, %0
+  ; CHECK-NEXT: RETURN implicit-def $arguments
+...
+
+---
+# Same test with three registers.
+# CHECK-LABEL: name: coalesce_test_3
+name: coalesce_test_3
+liveins:
+  - { reg: '$arguments' }
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $arguments
+    %0:i32 = CONST_I32 0, implicit-def $arguments
+    DBG_VALUE %0, $noreg, !5, !DIExpression(), debug-location !10
+    DBG_VALUE %1, $noreg, !10, !DIExpression(), debug-location !10
+    DBG_VALUE %2, $noreg, !11, !DIExpression(), debug-location !10
+    CALL @use, %0, implicit-def $arguments
+    NOP implicit-def $arguments
+    %1:i32 = CONST_I32 1, implicit-def $arguments
+    DBG_VALUE %0, $noreg, !5, !DIExpression(), debug-location !10
+    DBG_VALUE %1, $noreg, !10, !DIExpression(), debug-location !10
+    DBG_VALUE %2, $noreg, !11, !DIExpression(), debug-location !10
+    CALL @use, %1, implicit-def $arguments
+    NOP implicit-def $arguments
+    %2:i32 = CONST_I32 2, implicit-def $arguments
+    DBG_VALUE %0, $noreg, !5, !DIExpression(), debug-location !10
+    DBG_VALUE %1, $noreg, !10, !DIExpression(), debug-location !10
+    DBG_VALUE %2, $noreg, !11, !DIExpression(), debug-location !10
+    CALL @use, %2, implicit-def $arguments
+    RETURN implicit-def $arguments
+
+  ; CHECK:      %0:i32 = CONST_I32 0
+  ; CHECK-NEXT: DBG_VALUE %0, $noreg
+  ; CHECK-NEXT: DBG_VALUE $noreg, $noreg
+  ; CHECK-NEXT: DBG_VALUE $noreg, $noreg
+  ; CHECK-NEXT: CALL @use, %0
+  ; CHECK-NEXT: NOP
+  ; CHECK-NEXT: %0:i32 = CONST_I32 1
+  ; CHECK-NEXT: DBG_VALUE $noreg, $noreg
+  ; CHECK-NEXT: DBG_VALUE %0, $noreg
+  ; CHECK-NEXT: DBG_VALUE $noreg, $noreg
+  ; CHECK-NEXT: CALL @use, %0
+  ; CHECK-NEXT: NOP
+  ; CHECK-NEXT: %0:i32 = CONST_I32 2
+  ; CHECK-NEXT: DBG_VALUE $noreg, $noreg
+  ; CHECK-NEXT: DBG_VALUE $noreg, $noreg
+  ; CHECK-NEXT: DBG_VALUE %0, $noreg
+  ; CHECK-NEXT: CALL @use, %0
+  ; CHECK-NEXT: RETURN implicit-def $arguments
+---


        


More information about the llvm-commits mailing list