[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