[llvm] [AMDGPU][LiveDebugValues] Do not join debug values whose operand counts disagree (PR #221929)

via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 8 02:08:55 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-debuginfo

Author: Teja Alaghari (TejaX-Alaghari)

<details>
<summary>Changes</summary>

Currently, `DbgValueProperties::isJoinable()` decides whether two values could be joined by comparing their `DIExpressions` only. That was safe when location-operand count was taken from the expression. In instruction-referencing `LiveDebugValues`, `NumLocOps` is taken from the `MachineInstr` (`getNumDebugOperands()`), so two values can share an expression and still differ in operand count.

Joining pairs location operands by index. When the counts disagreed, `pickVPHILoc` indexed past the shorter list and hit `assert(Index < OpCount)` in `DbgValue::getDbgOpID`.

This change makes `isJoinable()` return `false` unless `NumLocOps` **also agrees**. The merge block then gets no location for that variable, which is correct.

---
Full diff: https://github.com/llvm/llvm-project/pull/221929.diff


2 Files Affected:

- (modified) llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h (+18-7) 
- (added) llvm/test/DebugInfo/MIR/InstrRef/expr-operand-count-mismatch.mir (+91) 


``````````diff
diff --git a/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h b/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h
index 184f07cb9fdf8..d73697fe5b4fc 100644
--- a/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h
+++ b/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h
@@ -299,8 +299,12 @@ class SpillLocationNo {
 /// the value, and Boolean of whether or not it's indirect.
 class DbgValueProperties {
 public:
-  DbgValueProperties(const DIExpression *DIExpr, bool Indirect, bool IsVariadic)
-      : DIExpr(DIExpr), Indirect(Indirect), IsVariadic(IsVariadic) {}
+  DbgValueProperties(const DIExpression *DIExpr, bool Indirect, bool IsVariadic,
+                     std::optional<unsigned> NumLocOps = std::nullopt)
+      : DIExpr(DIExpr), Indirect(Indirect), IsVariadic(IsVariadic),
+        NumLocOps(NumLocOps
+                      ? *NumLocOps
+                      : (IsVariadic ? DIExpr->getNumLocationOperands() : 1)) {}
 
   /// Extract properties from an existing DBG_VALUE instruction.
   DbgValueProperties(const MachineInstr &MI) {
@@ -310,29 +314,36 @@ class DbgValueProperties {
     IsVariadic = MI.isDebugValueList();
     DIExpr = MI.getDebugExpression();
     Indirect = MI.isDebugOffsetImm();
+    NumLocOps = MI.getNumDebugOperands();
   }
 
   bool isJoinable(const DbgValueProperties &Other) const {
+    // Joining pairs location operands by index, so the operand counts must
+    // agree. Equal expressions do not imply equal counts, because the same
+    // DIExpression can appear on MachineInstrs with different numbers of
+    // debug operands.
+    if (NumLocOps != Other.NumLocOps)
+      return false;
     return DIExpression::isEqualExpression(DIExpr, Indirect, Other.DIExpr,
                                            Other.Indirect);
   }
 
   bool operator==(const DbgValueProperties &Other) const {
-    return std::tie(DIExpr, Indirect, IsVariadic) ==
-           std::tie(Other.DIExpr, Other.Indirect, Other.IsVariadic);
+    return std::tie(DIExpr, Indirect, IsVariadic, NumLocOps) ==
+           std::tie(Other.DIExpr, Other.Indirect, Other.IsVariadic,
+                    Other.NumLocOps);
   }
 
   bool operator!=(const DbgValueProperties &Other) const {
     return !(*this == Other);
   }
 
-  unsigned getLocationOpCount() const {
-    return IsVariadic ? DIExpr->getNumLocationOperands() : 1;
-  }
+  unsigned getLocationOpCount() const { return NumLocOps; }
 
   const DIExpression *DIExpr;
   bool Indirect;
   bool IsVariadic;
+  unsigned NumLocOps;
 };
 
 /// TODO: Might pack better if we changed this to a Struct of Arrays, since
diff --git a/llvm/test/DebugInfo/MIR/InstrRef/expr-operand-count-mismatch.mir b/llvm/test/DebugInfo/MIR/InstrRef/expr-operand-count-mismatch.mir
new file mode 100644
index 0000000000000..13f436084d4d4
--- /dev/null
+++ b/llvm/test/DebugInfo/MIR/InstrRef/expr-operand-count-mismatch.mir
@@ -0,0 +1,91 @@
+# RUN: llc %s -o - -experimental-debug-variable-locations=true \
+# RUN:     -run-pass=livedebugvalues | FileCheck %s
+#
+## Two debug values for the same variable can carry identical expressions
+## while having different numbers of location operands.
+##
+## LiveDebugValues joins values by pairing their location operands up by index,
+## and used to decide two values were joinable by comparing expressions alone.
+## When the counts disagreed it read past the end of the shorter value's
+## operand list. Check that we no longer do so, and that no location is
+## propagated into the join block, where the two counts disagree.
+#
+# CHECK: ![[VAR:[0-9]+]] = !DILocalVariable(name: "x"
+#
+# CHECK-LABEL: bb.0.entry:
+# CHECK:       DBG_VALUE_LIST ![[VAR]], !DIExpression(), $noreg, $noreg, debug-location
+#
+# CHECK-LABEL: bb.1.then:
+# CHECK:       DBG_VALUE_LIST ![[VAR]], !DIExpression(), $edi, $edi, debug-location
+# CHECK:       DBG_VALUE_LIST ![[VAR]], !DIExpression(), $edi, debug-location
+#
+# CHECK-LABEL: bb.2.exit:
+# CHECK-NOT:   DBG_VALUE
+
+--- |
+  target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
+  target triple = "x86_64-unknown-unknown"
+
+  define i32 @f(i32 %a, i1 %c) !dbg !4 {
+  entry:
+    %v = add i32 %a, 1, !dbg !7
+      #dbg_value(!DIArgList(i32 %v, i32 %v), !8, !DIExpression(), !7)
+    br i1 %c, label %exit, label %then
+
+  then:                                             ; preds = %entry
+      #dbg_value(i32 %v, !8, !DIExpression(), !7)
+    %t = mul i32 %v, 3, !dbg !7
+    br label %exit
+
+  exit:                                             ; preds = %then, %entry
+    %p = phi i32 [ %t, %then ], [ 0, %entry ], !dbg !7
+    ret i32 %p, !dbg !7
+  }
+
+  !llvm.dbg.cu = !{!0}
+  !llvm.module.flags = !{!3}
+
+  !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "hand-written", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
+  !1 = !DIFile(filename: "min.c", directory: "/")
+  !2 = !{}
+  !3 = !{i32 2, !"Debug Info Version", i32 3}
+  !4 = distinct !DISubprogram(name: "f", scope: !1, file: !1, line: 1, type: !5, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2)
+  !5 = distinct !DISubroutineType(types: !6)
+  !6 = !{null}
+  !7 = !DILocation(line: 1, column: 1, scope: !4)
+  !8 = !DILocalVariable(name: "x", scope: !4, file: !1, line: 1, type: !9)
+  !9 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+...
+---
+name:            f
+alignment:       1
+tracksRegLiveness: true
+debugInstrRef:   true
+tracksDebugUserValues: true
+liveins:
+  - { reg: '$edi', virtual-reg: '' }
+  - { reg: '$esi', virtual-reg: '' }
+body:             |
+  bb.0.entry:
+    successors: %bb.2(0x40000000), %bb.1(0x40000000)
+    liveins: $edi, $esi
+
+    renamable $edi = KILL $edi, implicit-def $rdi
+    DBG_INSTR_REF !8, !DIExpression(), dbg-instr-ref(1, 0), dbg-instr-ref(1, 0), debug-location !7
+    renamable $eax = XOR32rr undef $eax, undef $eax, implicit-def dead $eflags
+    TEST8ri renamable $sil, 1, implicit-def $eflags, implicit killed $esi
+    JCC_1 %bb.2, 5, implicit $eflags
+
+  bb.1.then:
+    successors: %bb.2(0x80000000)
+    liveins: $rdi
+
+    renamable $edi = INC32r renamable $edi, implicit-def dead $eflags, implicit killed $rdi, implicit-def $rdi, debug-instr-number 1, debug-location !7
+    DBG_INSTR_REF !8, !DIExpression(), dbg-instr-ref(1, 0), debug-location !7
+    renamable $eax = LEA64_32r killed renamable $rdi, 2, renamable $rdi, 0, $noreg, debug-location !7
+
+  bb.2.exit:
+    liveins: $eax
+
+    RET64 $eax, debug-location !7
+...

``````````

</details>


https://github.com/llvm/llvm-project/pull/221929


More information about the llvm-commits mailing list