[llvm] [KeyInstr][DwarfDebug] Add is_stmt emission support (PR #133495)

Orlando Cazalet-Hyams via llvm-commits llvm-commits at lists.llvm.org
Fri May 9 05:06:29 PDT 2025


https://github.com/OCHyams updated https://github.com/llvm/llvm-project/pull/133495

>From 0b5166a2a2e6e7a166518d56e14c877095fa01be Mon Sep 17 00:00:00 2001
From: Orlando Cazalet-Hyams <orlando.hyams at sony.com>
Date: Thu, 27 Mar 2025 16:53:00 +0000
Subject: [PATCH 01/13] [KeyInstr][DwarfDebug] Add is_stmt emission support

Interpret Key Instructions metadata to determine is_stmt placement.

The lowest rank (highest precedent) instructions in each {InlinedAt, atomGroup}
set are candidates for is_stmt. Only the last instruction in each set in a given
block gets is_stmt. Calls always get is_stmt.
---
 llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp    | 210 +++++++++++++++++-
 llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h      |   9 +
 .../KeyInstructions/X86/dwarf-basic-ranks.ll  |  68 ++++++
 .../KeyInstructions/X86/dwarf-basic.ll        |  62 ++++++
 .../X86/dwarf-buoy-multi-key.mir              |  78 +++++++
 .../KeyInstructions/X86/dwarf-buoy.mir        |  66 ++++++
 .../KeyInstructions/X86/dwarf-calls.ll        | 117 ++++++++++
 .../KeyInstructions/X86/dwarf-ranks-blocks.ll |  65 ++++++
 8 files changed, 663 insertions(+), 12 deletions(-)
 create mode 100644 llvm/test/DebugInfo/KeyInstructions/X86/dwarf-basic-ranks.ll
 create mode 100644 llvm/test/DebugInfo/KeyInstructions/X86/dwarf-basic.ll
 create mode 100644 llvm/test/DebugInfo/KeyInstructions/X86/dwarf-buoy-multi-key.mir
 create mode 100644 llvm/test/DebugInfo/KeyInstructions/X86/dwarf-buoy.mir
 create mode 100644 llvm/test/DebugInfo/KeyInstructions/X86/dwarf-calls.ll
 create mode 100644 llvm/test/DebugInfo/KeyInstructions/X86/dwarf-ranks-blocks.ll

diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
index b331854d1ee8f..792441607eec2 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
@@ -17,6 +17,7 @@
 #include "DwarfExpression.h"
 #include "DwarfUnit.h"
 #include "llvm/ADT/APInt.h"
+#include "llvm/ADT/ScopeExit.h"
 #include "llvm/ADT/Statistic.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/Twine.h"
@@ -170,6 +171,9 @@ static cl::opt<DwarfDebug::MinimizeAddrInV5> MinimizeAddrInV5Option(
                           "Stuff")),
     cl::init(DwarfDebug::MinimizeAddrInV5::Default));
 
+static cl::opt<bool> KeyInstructionsAreStmts("dwarf-use-key-instructions",
+                                             cl::Hidden, cl::init(false));
+
 static constexpr unsigned ULEB128PadSize = 4;
 
 void DebugLocDwarfExpression::emitOp(uint8_t Op, const char *Comment) {
@@ -2070,6 +2074,10 @@ void DwarfDebug::beginInstruction(const MachineInstr *MI) {
   unsigned LastAsmLine =
       Asm->OutStreamer->getContext().getCurrentDwarfLoc().getLine();
 
+  bool IsKey = false;
+  if (KeyInstructionsAreStmts && DL && DL.getLine())
+    IsKey = KeyInstructions.contains(MI);
+
   if (!DL && MI == PrologEndLoc) {
     // In rare situations, we might want to place the end of the prologue
     // somewhere that doesn't have a source location already. It should be in
@@ -2088,13 +2096,18 @@ void DwarfDebug::beginInstruction(const MachineInstr *MI) {
     // If we have an ongoing unspecified location, nothing to do here.
     if (!DL)
       return;
-    // We have an explicit location, same as the previous location.
-    // But we might be coming back to it after a line 0 record.
-    if ((LastAsmLine == 0 && DL.getLine() != 0) || Flags) {
-      // Reinstate the source location but not marked as a statement.
-      RecordSourceLine(DL, Flags);
+
+    // Skip this if the instruction is Key, else we might accidentally miss an
+    // is_stmt.
+    if (!IsKey) {
+      // We have an explicit location, same as the previous location.
+      // But we might be coming back to it after a line 0 record.
+      if ((LastAsmLine == 0 && DL.getLine() != 0) || Flags) {
+        // Reinstate the source location but not marked as a statement.
+        RecordSourceLine(DL, Flags);
+      }
+      return;
     }
-    return;
   }
 
   if (!DL) {
@@ -2141,11 +2154,17 @@ void DwarfDebug::beginInstruction(const MachineInstr *MI) {
     Flags |= DWARF2_FLAG_PROLOGUE_END | DWARF2_FLAG_IS_STMT;
     PrologEndLoc = nullptr;
   }
-  // If the line changed, we call that a new statement; unless we went to
-  // line 0 and came back, in which case it is not a new statement.
-  unsigned OldLine = PrevInstLoc ? PrevInstLoc.getLine() : LastAsmLine;
-  if (DL.getLine() && (DL.getLine() != OldLine || ForceIsStmt))
-    Flags |= DWARF2_FLAG_IS_STMT;
+
+  if (KeyInstructionsAreStmts) {
+    if (IsKey)
+      Flags |= DWARF2_FLAG_IS_STMT;
+  } else {
+    // If the line changed, we call that a new statement; unless we went to
+    // line 0 and came back, in which case it is not a new statement.
+    unsigned OldLine = PrevInstLoc ? PrevInstLoc.getLine() : LastAsmLine;
+    if (DL.getLine() && (DL.getLine() != OldLine || ForceIsStmt))
+      Flags |= DWARF2_FLAG_IS_STMT;
+  }
 
   RecordSourceLine(DL, Flags);
 
@@ -2338,6 +2357,170 @@ DwarfDebug::emitInitialLocDirective(const MachineFunction &MF, unsigned CUID) {
   return PrologEndLoc;
 }
 
+void DwarfDebug::findKeyInstructions(const MachineFunction *MF) {
+  // New function - reset KeyInstructions.
+  KeyInstructions.clear();
+
+  // The current candidate is_stmt instructions for each source atom.
+  // Map {(InlinedAt, Group): (Rank, Instructions)}.
+  DenseMap<std::pair<DILocation *, uint32_t>,
+           std::pair<uint16_t, SmallVector<const MachineInstr *>>>
+      GroupCandidates;
+
+  // For each instruction:
+  //   * Skip insts without DebugLoc, AtomGroup or AtomRank, and line zeros.
+  //   * Check if insts in this group have been seen already in GroupCandidates.
+  //     * If this instr rank is equal, add this instruction to KeyInstructions.
+  //       Remove existing instructions from KeyInstructions if they have the
+  //       same parent.
+  //     * If this instr rank is higher (lower precedence), ignore it.
+  //     * If this instr rank is lower (higher precedence), erase existing
+  //       instructions from KeyInstructions. Add this instr to KeyInstructions.
+
+  for (auto &MBB : *MF) {
+    // Rather than apply is_stmt directly to Key Instructions, we "float"
+    // is_stmt up to the 1st instruction with the same line number in a
+    // contiguous block. That instruction is called the "buoy". The
+    // buoy gets reset if we encouner an instruction with an atom
+    // group.
+    const MachineInstr *Buoy = nullptr;
+    // The atom group number associated with Buoy which may be 0 if we haven't
+    // encountered an atom group yet in this blob of instructions with the same
+    // line number.
+    uint64_t BuoyAtom = 0;
+
+    for (auto &MI : MBB) {
+      if (MI.isMetaInstruction())
+        continue;
+
+      if (!MI.getDebugLoc() || !MI.getDebugLoc().getLine())
+        continue;
+
+      // Reset the Buoy to this instruciton if it has a different line number.
+      if (!Buoy ||
+          Buoy->getDebugLoc().getLine() != MI.getDebugLoc().getLine()) {
+        Buoy = &MI;
+        BuoyAtom = 0;
+      }
+
+      // Call instructions are handled specially - we always mark them as key
+      // regardless of atom info.
+      const auto &TII =
+          *MI.getParent()->getParent()->getSubtarget().getInstrInfo();
+      if (MI.isCall() || TII.isTailCall(MI)) {
+        assert(MI.getDebugLoc() && "Unexpectedly missing DL");
+
+        // Calls are always key.
+        KeyInstructions.insert(Buoy);
+
+        uint64_t Group = MI.getDebugLoc()->getAtomGroup();
+        uint8_t Rank = MI.getDebugLoc()->getAtomRank();
+        if (Group && Rank) {
+          auto *InlinedAt = MI.getDebugLoc()->getInlinedAt();
+          auto &[CandidateRank, CandidateInsts] = GroupCandidates[{InlinedAt, Group}];
+
+          // This looks similar to the non-call handling code, except that
+          // we don't put the call into CandidateInsts so that they can't be
+          // made un-key. As a result, we also have to take special care not
+          // to erase the is_stmt from the buoy, and prevent that happening
+          // in the future.
+
+          if (CandidateRank == Rank) {
+            // We've seen other instructions in this group of this rank. Discard
+            // ones we've seen in this block, keep the others.
+            assert(!CandidateInsts.empty());
+            SmallVector<const MachineInstr *> Insts;
+            Insts.reserve(CandidateInsts.size());
+            for (auto &PrevInst : CandidateInsts) {
+              if (PrevInst->getParent() != MI.getParent())
+                Insts.push_back(PrevInst);
+              else if (PrevInst != Buoy)
+                KeyInstructions.erase(PrevInst);
+            }
+
+            if (Insts.empty()) {
+              CandidateInsts.clear();
+              CandidateRank = 0;
+            } else {
+              CandidateInsts = std::move(Insts);
+            }
+
+          } else if (CandidateRank > Rank) {
+            // We've seen other instructions in this group of lower precedence
+            // (higher rank). Discard them.
+            for (auto *Supplanted : CandidateInsts) {
+              // Don't erase the is_stmt we're using for this call.
+              if (Supplanted != Buoy)
+                KeyInstructions.erase(Supplanted);
+            }
+            CandidateInsts.clear();
+            CandidateRank = 0;
+          }
+        }
+
+        // Avoid floating any future is_stmts up to the call.
+        Buoy = nullptr;
+        continue;
+      }
+
+      auto *InlinedAt = MI.getDebugLoc()->getInlinedAt();
+      uint64_t Group = MI.getDebugLoc()->getAtomGroup();
+      uint8_t Rank = MI.getDebugLoc()->getAtomRank();
+      if (!Group || !Rank)
+        continue;
+
+      // Don't let is_stmts float past instructions from different source atoms.
+      if (BuoyAtom && BuoyAtom != Group) {
+        Buoy = &MI;
+        BuoyAtom = MI.getDebugLoc()->getAtomGroup();
+      }
+
+      auto &[CandidateRank, CandidateInsts] = GroupCandidates[{InlinedAt, Group}];
+
+      if (CandidateRank == 0) {
+        // This is the first time we're seeing an instruction in this atom
+        // group. Add it to the map.
+        assert(CandidateInsts.empty());
+        CandidateRank = Rank;
+        CandidateInsts.push_back(Buoy);
+
+      } else if (CandidateRank == Rank) {
+        // We've seen other instructions in this group of this rank. Discard
+        // ones we've seen in this block, keep the others, add this one.
+        assert(!CandidateInsts.empty());
+        SmallVector<const MachineInstr *> Insts;
+        Insts.reserve(CandidateInsts.size() + 1);
+        for (auto &PrevInst : CandidateInsts) {
+          if (PrevInst->getParent() != MI.getParent())
+            Insts.push_back(PrevInst);
+          else
+            KeyInstructions.erase(PrevInst);
+        }
+        Insts.push_back(Buoy);
+        CandidateInsts = std::move(Insts);
+
+      } else if (CandidateRank > Rank) {
+        // We've seen other instructions in this group of lower precedence
+        // (higher rank). Discard them, add this one.
+        assert(!CandidateInsts.empty());
+        CandidateRank = Rank;
+        for (auto *Supplanted : CandidateInsts)
+          KeyInstructions.erase(Supplanted);
+        CandidateInsts = {Buoy};
+
+      } else {
+        // We've seen other instructions in this group with higher precedence
+        // (lower rank). Discard this one.
+        assert(Rank != 0 && CandidateRank < Rank && CandidateRank != 0);
+        continue;
+      }
+      KeyInstructions.insert(Buoy);
+      assert(!BuoyAtom || BuoyAtom == MI.getDebugLoc()->getAtomGroup());
+      BuoyAtom = MI.getDebugLoc()->getAtomGroup();
+    }
+  }
+}
+
 /// For the function \p MF, finds the set of instructions which may represent a
 /// change in line number from one or more of the preceding MBBs. Stores the
 /// resulting set of instructions, which should have is_stmt set, in
@@ -2496,7 +2679,10 @@ void DwarfDebug::beginFunctionImpl(const MachineFunction *MF) {
   PrologEndLoc = emitInitialLocDirective(
       *MF, Asm->OutStreamer->getContext().getDwarfCompileUnitID());
 
-  findForceIsStmtInstrs(MF);
+  if (KeyInstructionsAreStmts)
+    findKeyInstructions(MF);
+  else
+    findForceIsStmtInstrs(MF);
 }
 
 unsigned
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h
index 58e6d39f76ae0..cd232a8e94ecf 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h
@@ -464,6 +464,10 @@ class DwarfDebug : public DebugHandlerBase {
   };
 
 private:
+  /// Instructions which should get is_stmt applied because they implement key
+  /// functionality for a source atom.
+  SmallDenseSet<const MachineInstr *> KeyInstructions;
+
   /// Force the use of DW_AT_ranges even for single-entry range lists.
   MinimizeAddrInV5 MinimizeAddr = MinimizeAddrInV5::Disabled;
 
@@ -701,6 +705,11 @@ class DwarfDebug : public DebugHandlerBase {
 
   void findForceIsStmtInstrs(const MachineFunction *MF);
 
+  /// Find instructions which should get is_stmt applied because they implement
+  /// key functionality for a source atom, store results in
+  /// DwarfDebug::KeyInstructions.
+  void findKeyInstructions(const MachineFunction *MF);
+
 protected:
   /// Gather pre-function debug information.
   void beginFunctionImpl(const MachineFunction *MF) override;
diff --git a/llvm/test/DebugInfo/KeyInstructions/X86/dwarf-basic-ranks.ll b/llvm/test/DebugInfo/KeyInstructions/X86/dwarf-basic-ranks.ll
new file mode 100644
index 0000000000000..71ecf1dc41238
--- /dev/null
+++ b/llvm/test/DebugInfo/KeyInstructions/X86/dwarf-basic-ranks.ll
@@ -0,0 +1,68 @@
+; RUN: llc %s --filetype=obj -o - --dwarf-use-key-instructions \
+; RUN: | llvm-objdump -d - --no-show-raw-insn \
+; RUN: | FileCheck %s --check-prefix=OBJ
+
+; RUN: llc %s --filetype=obj -o - --dwarf-use-key-instructions \
+; RUN: | llvm-dwarfdump - --debug-line \
+; RUN: | FileCheck %s --check-prefix=DBG
+
+; OBJ: 0000000000000000 <_Z1fPiii>:
+; OBJ-NEXT:  0: pushq   %rbp
+; OBJ-NEXT:  1: pushq   %r14
+; OBJ-NEXT:  3: pushq   %rbx
+; OBJ-NEXT:  4: movl    %edx, %ebx
+; OBJ-NEXT:  6: movl    %esi, %ebp
+; OBJ-NEXT:  8: movq    %rdi, %r14
+; OBJ-NEXT:  b: callq   0x10 <_Z1fPiii+0x10>
+; OBJ-NEXT: 10: addl    %ebx, %ebp
+; OBJ-NEXT: 12: movl    %ebp, (%r14)
+; OBJ-NEXT: 15: movl    %ebp, %eax
+; OBJ-NEXT: 17: popq    %rbx
+; OBJ-NEXT: 18: popq    %r14
+; OBJ-NEXT: 1a: popq    %rbp
+
+; DBG:      Address            Line   Column File   ISA Discriminator OpIndex Flags
+; DBG-NEXT: ------------------ ------ ------ ------ --- ------------- ------- -------------
+; DBG-NEXT: 0x0000000000000000      3      0      0   0             0       0  is_stmt
+; DBG-NEXT: 0x000000000000000b      4      0      0   0             0       0  is_stmt prologue_end
+; DBG-NEXT: 0x0000000000000010      6      0      0   0             0       0
+; DBG-NEXT: 0x0000000000000012      5      0      0   0             0       0  is_stmt
+; DBG-NEXT: 0x0000000000000015      7      0      0   0             0       0  is_stmt
+; DBG-NEXT: 0x0000000000000017      7      0      0   0             0       0  epilogue_begin
+; DBG-NEXT: 0x000000000000001c      7      0      0   0             0       0  end_sequence
+
+;; 1. [[gnu::nodebug]] void prologue_end();
+;; 2.
+;; 3. int f(int *a, int b, int c) {
+;; 4.   prologue_end();
+;; 5.   *a = 
+;; 6.     b + c;
+;; 7.   return *a;
+;; 8. }
+
+;; The add and store are in the same goup (1). The add (line 6) has lower
+;; precedence (rank 2) so should not get is_stmt applied.
+target triple = "x86_64-unknown-linux-gnu"
+
+define hidden noundef i32 @_Z1fPiii(ptr %a, i32 %b, i32 %c) local_unnamed_addr !dbg !11 {
+entry:
+  tail call void @_Z12prologue_endv(), !dbg !DILocation(line: 4, scope: !11)
+  %add = add nsw i32 %c, %b,           !dbg !DILocation(line: 6, scope: !11, atomGroup: 1, atomRank: 2)
+  store i32 %add, ptr %a, align 4,     !dbg !DILocation(line: 5, scope: !11, atomGroup: 1, atomRank: 1)
+  ret i32 %add,                        !dbg !DILocation(line: 7, scope: !11, atomGroup: 2, atomRank: 1)
+}
+
+declare void @_Z12prologue_endv() local_unnamed_addr #1
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3}
+!llvm.ident = !{!10}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_17, file: !1, producer: "clang version 19.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: LineTablesOnly, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "test.cpp", directory: "/")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!10 = !{!"clang version 19.0.0"}
+!11 = distinct !DISubprogram(name: "f", scope: !1, file: !1, line: 3, type: !12, scopeLine: 3, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0)
+!12 = !DISubroutineType(types: !13)
+!13 = !{}
diff --git a/llvm/test/DebugInfo/KeyInstructions/X86/dwarf-basic.ll b/llvm/test/DebugInfo/KeyInstructions/X86/dwarf-basic.ll
new file mode 100644
index 0000000000000..e3b0184a837f8
--- /dev/null
+++ b/llvm/test/DebugInfo/KeyInstructions/X86/dwarf-basic.ll
@@ -0,0 +1,62 @@
+; RUN: llc %s --filetype=obj -o - --dwarf-use-key-instructions \
+; RUN: | llvm-objdump -d - --no-show-raw-insn \
+; RUN: | FileCheck %s --check-prefix=OBJ
+
+; RUN: llc %s --filetype=obj -o - --dwarf-use-key-instructions \
+; RUN: | llvm-dwarfdump - --debug-line \
+; RUN: | FileCheck %s --check-prefix=DBG
+
+; OBJ: 0000000000000000 <_Z1fi>:
+; OBJ-NEXT: 0: leal    0x1(%rdi), %eax
+; OBJ-NEXT: 3: retq
+; OBJ: 0000000000000010 <_Z1gi>:
+; OBJ-NEXT: 10: leal    0x1(%rdi), %eax
+; OBJ-NEXT: 13: retq
+
+; DBG:      Address            Line   Column File   ISA Discriminator OpIndex Flags
+; DBG-NEXT: ------------------ ------ ------ ------ --- ------------- ------- -------------
+; DBG-NEXT: 0x0000000000000000      2      0      0   0             0       0  is_stmt prologue_end
+; DBG-NEXT: 0x0000000000000003      3      0      0   0             0       0  is_stmt
+; DBG-NEXT: 0x0000000000000010      2      0      0   0             0       0  is_stmt prologue_end
+; DBG-NEXT: 0x0000000000000013      6      0      0   0             0       0  is_stmt
+
+;; 1. int f(int a) {
+;; 2.   int x = a + 1;
+;; 3.   return x;
+;; 4. }
+;; 5. int g(int b) { 
+;; 6.   return f(b);
+;; 7. }
+;;
+;; Both functions contain 2 instructions in unique atom groups. In f we see
+;; groups 1 and 3, and in g we see {!18, 1} and 1. All of these instructions
+;; should get is_stmt.
+
+target triple = "x86_64-unknown-linux-gnu"
+
+define hidden noundef i32 @_Z1fi(i32 noundef %a) local_unnamed_addr !dbg !11 {
+entry:
+  %add = add nsw i32 %a, 1,   !dbg !DILocation(line: 2, scope: !11, atomGroup: 1, atomRank: 2)
+  ret i32 %add,               !dbg !DILocation(line: 3, scope: !11, atomGroup: 3, atomRank: 1)
+}
+
+define hidden noundef i32 @_Z1gi(i32 noundef %b) local_unnamed_addr !dbg !16 {
+entry:
+  %add.i = add nsw i32 %b, 1, !dbg !DILocation(line: 2, scope: !11, inlinedAt: !18, atomGroup: 1, atomRank: 2)
+  ret i32 %add.i,             !dbg !DILocation(line: 6, scope: !16, atomGroup: 1, atomRank: 1)
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3}
+!llvm.ident = !{!10}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_17, file: !1, producer: "clang version 19.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: LineTablesOnly, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "test.cpp", directory: "/")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!10 = !{!"clang version 19.0.0"}
+!11 = distinct !DISubprogram(name: "f", scope: !1, file: !1, line: 1, type: !12, scopeLine: 1, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0)
+!12 = !DISubroutineType(types: !13)
+!13 = !{}
+!16 = distinct !DISubprogram(name: "g", scope: !1, file: !1, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0)
+!18 = distinct !DILocation(line: 6, scope: !16)
diff --git a/llvm/test/DebugInfo/KeyInstructions/X86/dwarf-buoy-multi-key.mir b/llvm/test/DebugInfo/KeyInstructions/X86/dwarf-buoy-multi-key.mir
new file mode 100644
index 0000000000000..c8459b4ced600
--- /dev/null
+++ b/llvm/test/DebugInfo/KeyInstructions/X86/dwarf-buoy-multi-key.mir
@@ -0,0 +1,78 @@
+# RUN: llc %s --start-after=livedebugvalues --dwarf-use-key-instructions --filetype=obj -o - \
+# RUN: | llvm-objdump -d - --no-show-raw-insn \
+# RUN: | FileCheck %s --check-prefix=OBJ
+
+# RUN: llc %s --start-after=livedebugvalues --dwarf-use-key-instructions --filetype=obj -o - \
+# RUN: | llvm-dwarfdump - --debug-line \
+# RUN: | FileCheck %s --check-prefix=DBG
+
+# OBJ: 0000000000000000 <_Z1fPiii>:
+# OBJ-NEXT:  0:       movl    $0x0, %ebx
+# OBJ-NEXT:  5:       movl    $0x1, %ebx
+# OBJ-NEXT:  a:       movl    $0x2, %ebx
+# OBJ-NEXT:  f:       movl    $0x3, %ebx
+# OBJ-NEXT: 14:       movl    $0x4, %eax
+# OBJ-NEXT: 19:       movl    $0x5, %eax
+# OBJ-NEXT: 1e:       movl    $0x6, %eax
+# OBJ-NEXT: 23:       movl    $0x7, %eax
+# OBJ-NEXT: 28:       retq
+
+# DBG:      Address            Line   Column File   ISA Discriminator OpIndex Flags
+# DBG-NEXT: ------------------ ------ ------ ------ --- ------------- ------- -------------
+# DBG-NEXT: 0x0000000000000000      1      0      0   0             0       0  is_stmt prologue_end
+# DBG-NEXT: 0x0000000000000005      2      0      0   0             0       0  is_stmt
+# DBG-NEXT: 0x000000000000000a      2      0      0   0             0       0
+# DBG-NEXT: 0x000000000000000f      2      0      0   0             0       0
+# DBG-NEXT: 0x0000000000000014      2      0      0   0             0       0
+# DBG-NEXT: 0x0000000000000019      2      0      0   0             0       0
+# DBG-NEXT: 0x000000000000001e      2      0      0   0             0       0  is_stmt
+# DBG-NEXT: 0x0000000000000023      2      0      0   0             0       0  is_stmt
+# DBG-NEXT: 0x0000000000000029      2      0      0   0             0       0  is_stmt end_sequence
+
+## Check that interleaving atoms on the same line still produces reasonable
+## is_stmt placement (the is_stmts want to "float up" to the first instruction
+## in a contiguous set with the same line, but we don't let them float past
+## other atom groups).
+
+--- |
+  target triple = "x86_64-unknown-linux-gnu"
+
+  define hidden noundef i32 @_Z1fPiii(ptr %a, i32 %b, i32 %c, i1 %cond) local_unnamed_addr !dbg !5 {
+  entry:
+    ret i32 2
+  }
+
+  !llvm.dbg.cu = !{!0}
+  !llvm.module.flags = !{!2, !3}
+  !llvm.ident = !{!4}
+
+  !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_17, file: !1, producer: "clang version 19.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: LineTablesOnly, splitDebugInlining: false, nameTableKind: None)
+  !1 = !DIFile(filename: "test.cpp", directory: "/")
+  !2 = !{i32 7, !"Dwarf Version", i32 5}
+  !3 = !{i32 2, !"Debug Info Version", i32 3}
+  !4 = !{!"clang version 19.0.0"}
+  !5 = distinct !DISubprogram(name: "f", scope: !1, file: !1, line: 1, type: !6, scopeLine: 1, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0)
+  !6 = !DISubroutineType(types: !7)
+  !7 = !{}
+  !8 = !DILocalVariable(name: "x", scope: !5, file: !1, line: 1, type: !7)
+
+...
+---
+name:            _Z1fPiii
+alignment:       16
+body:             |
+  bb.0.entry:
+    $ebx = MOV32ri 0, debug-location !DILocation(line: 1, scope: !5) 
+    ;; is_stmt floats up here from mov 3.
+    $ebx = MOV32ri 1, debug-location !DILocation(line: 2, scope: !5, atomGroup: 1, atomRank: 1)
+    $ebx = MOV32ri 2, debug-location !DILocation(line: 2, scope: !5, atomGroup: 1, atomRank: 2)
+    $ebx = MOV32ri 3, debug-location !DILocation(line: 2, scope: !5, atomGroup: 1, atomRank: 1)
+    $eax = MOV32ri 4, debug-location !DILocation(line: 2, scope: !5)
+    $eax = MOV32ri 5, debug-location !DILocation(line: 2, scope: !5, atomGroup: 2, atomRank: 1)
+    ;; is_stmt for this group can't float higher due to atom group above.
+    $eax = MOV32ri 6, debug-location !DILocation(line: 2, scope: !5, atomGroup: 3, atomRank: 1)
+    ;; Same again.
+    $eax = MOV32ri 7, debug-location !DILocation(line: 2, scope: !5, atomGroup: 2, atomRank: 1)
+    RET64 $eax
+
+...
diff --git a/llvm/test/DebugInfo/KeyInstructions/X86/dwarf-buoy.mir b/llvm/test/DebugInfo/KeyInstructions/X86/dwarf-buoy.mir
new file mode 100644
index 0000000000000..3aff5e22326ec
--- /dev/null
+++ b/llvm/test/DebugInfo/KeyInstructions/X86/dwarf-buoy.mir
@@ -0,0 +1,66 @@
+# RUN: llc %s --start-after=livedebugvalues --dwarf-use-key-instructions --filetype=obj -o - \
+# RUN: | llvm-objdump -d - --no-show-raw-insn \
+# RUN: | FileCheck %s --check-prefix=OBJ
+
+# RUN: llc %s --start-after=livedebugvalues --dwarf-use-key-instructions --filetype=obj -o - \
+# RUN: | llvm-dwarfdump - --debug-line \
+# RUN: | FileCheck %s --check-prefix=DBG
+
+# OBJ: 0000000000000000 <_Z1fPiii>:
+# OBJ-NEXT:  0:       movl    $0x1, %ebx
+# OBJ-NEXT:  5:       movl    $0x2, %eax
+# OBJ-NEXT:  a:       movl    $0x0, %eax
+# OBJ-NEXT:  f:       movl    $0x0, %eax
+# OBJ-NEXT: 14:       retq
+
+# DBG:      Address            Line   Column File   ISA Discriminator OpIndex Flags
+# DBG-NEXT: ------------------ ------ ------ ------ --- ------------- ------- -------------
+# DBG-NEXT: 0x0000000000000000      1      0      0   0             0       0  is_stmt prologue_end
+# DBG-NEXT: 0x0000000000000005      2      0      0   0             0       0  is_stmt
+# DBG-NEXT: 0x000000000000000a      0      0      0   0             0       0
+# DBG-NEXT: 0x0000000000000014      2      0      0   0             0       0
+# DBG-NEXT: 0x0000000000000015      2      0      0   0             0       0  end_sequence
+
+## The `RET64` is the only key instruction. The `MOV32ri 2` has the same line
+## number so the is_stmt should "float up" from the ret to the mov. Check this
+## happens; check that the DBG_VALUE wedged between them with a different line
+## number, the line zero, and missing DebugLoc, don't disrupt that; and check
+## the is_stmt doesn't float too far up onto the `MOV32ri 1`.
+
+--- |
+  target triple = "x86_64-unknown-linux-gnu"
+
+  define hidden noundef i32 @_Z1fPiii(ptr %a, i32 %b, i32 %c, i1 %cond) local_unnamed_addr !dbg !5 {
+  entry:
+    ret i32 2
+  }
+
+  !llvm.dbg.cu = !{!0}
+  !llvm.module.flags = !{!2, !3}
+  !llvm.ident = !{!4}
+
+  !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_17, file: !1, producer: "clang version 19.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: LineTablesOnly, splitDebugInlining: false, nameTableKind: None)
+  !1 = !DIFile(filename: "test.cpp", directory: "/")
+  !2 = !{i32 7, !"Dwarf Version", i32 5}
+  !3 = !{i32 2, !"Debug Info Version", i32 3}
+  !4 = !{!"clang version 19.0.0"}
+  !5 = distinct !DISubprogram(name: "f", scope: !1, file: !1, line: 1, type: !6, scopeLine: 1, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0)
+  !6 = !DISubroutineType(types: !7)
+  !7 = !{}
+  !8 = !DILocalVariable(name: "x", scope: !5, file: !1, line: 1, type: !7)
+
+...
+---
+name:            _Z1fPiii
+alignment:       16
+body:             |
+  bb.0.entry:
+
+    $ebx = MOV32ri 1,                              debug-location !DILocation(line: 1, scope: !5)
+    $eax = MOV32ri 2,                              debug-location !DILocation(line: 2, scope: !5)
+    $eax = MOV32ri 0,                              debug-location !DILocation(line: 0, scope: !5)
+    $eax = MOV32ri 0
+    DBG_VALUE $noreg, $noreg, !8, !DIExpression(), debug-location !DILocation(line: 1, scope: !5)
+    RET64 $eax,                                    debug-location !DILocation(line: 2, scope: !5, atomGroup: 1, atomRank: 2)
+
+...
diff --git a/llvm/test/DebugInfo/KeyInstructions/X86/dwarf-calls.ll b/llvm/test/DebugInfo/KeyInstructions/X86/dwarf-calls.ll
new file mode 100644
index 0000000000000..780374127707e
--- /dev/null
+++ b/llvm/test/DebugInfo/KeyInstructions/X86/dwarf-calls.ll
@@ -0,0 +1,117 @@
+; RUN: llc %s --filetype=obj -o - --dwarf-use-key-instructions \
+; RUN: | llvm-objdump -d - --no-show-raw-insn \
+; RUN: | FileCheck %s --check-prefix=OBJ
+
+; RUN: llc %s --filetype=obj -o - --dwarf-use-key-instructions \
+; RUN: | llvm-dwarfdump - --debug-line \
+; RUN: | FileCheck %s --check-prefix=DBG
+
+; OBJ:0000000000000000 <fun>:
+; OBJ-NEXT:  0:       pushq   %rbp
+; OBJ-NEXT:  1:       pushq   %r14
+; OBJ-NEXT:  3:       pushq   %rbx
+; OBJ-NEXT:  4:       movq    (%rip), %rax
+; OBJ-NEXT:  b:       movl    (%rax), %ebp
+; OBJ-NEXT:  d:       callq   0x12 <fun+0x12>
+; OBJ-NEXT: 12:       callq   0x17 <fun+0x17>
+; OBJ-NEXT: 17:       movl    %eax, %ebx
+; OBJ-NEXT: 19:       addl    %ebp, %ebx
+; OBJ-NEXT: 1b:       movq    (%rip), %r14
+; OBJ-NEXT: 22:       movl    $0x1, (%r14)
+; OBJ-NEXT: 29:       callq   0x2e <fun+0x2e>
+; OBJ-NEXT: 2e:       movl    $0x2, (%r14)
+; OBJ-NEXT: 35:       callq   0x3a <fun+0x3a>
+; OBJ-NEXT: 3a:       movl    $0x3, (%r14)
+; OBJ-NEXT: 41:       callq   0x46 <fun+0x46>
+; OBJ-NEXT: 46:       movl    $0x4, (%r14)
+; OBJ-NEXT: 4d:       callq   0x52 <fun+0x52>
+; OBJ-NEXT: 52:       movl    %ebx, %eax
+; OBJ-NEXT: 54:       popq    %rbx
+; OBJ-NEXT: 55:       popq    %r14
+; OBJ-NEXT: 57:       popq    %rbp
+; OBJ-NEXT: 58:       retq
+
+; DBG:      Address            Line   Column File   ISA Discriminator OpIndex Flags
+; DBG-NEXT: ------------------ ------ ------ ------ --- ------------- ------- -------------
+; DBG-NEXT: 0x0000000000000000      1      0      0   0             0       0  is_stmt
+; DBG-NEXT: 0x0000000000000004      2      0      0   0             0       0  is_stmt prologue_end
+
+;; Test A:
+;; Check the 1st call (line 3) gets is_stmt despite having no atom group.
+; DBG-NEXT: 0x000000000000000d      3      0      0   0             0       0  is_stmt
+
+;; Test B:
+;; Check the 2nd call (line 4) gets is_stmt applied despite being part of group
+;; 1 and having lower precedence than the add. Check that the add stil gets
+;; is_stmt applied.
+;; There are two is_stmt line 4 entries are is_stmt because we don't float
+;; is_stmts up on the same line past other key instructions. The call is
+;; key, so the add's is_stmt floats up to the movl on the same line, but
+;; not past the call.
+; DBG-NEXT: 0x0000000000000012      4      0      0   0             0       0  is_stmt
+; DBG-NEXT: 0x0000000000000017      4      0      0   0             0       0  is_stmt
+; DBG-NEXT: 0x0000000000000019      4      0      0   0             0       0
+
+;; Test C:
+;; Check that is_stmt floats up from the call to the store.
+; DBG-NEXT: 0x000000000000001b      5      0      0   0             0       0  is_stmt
+; DBG-NEXT: 0x0000000000000029      5      0      0   0             0       0
+
+;; Test D:
+;; Check the is_stmt is not applied to the lower ranking instruction.
+; DBG-NEXT: 0x000000000000002e      6      0      0   0             0       0
+; DBG-NEXT: 0x0000000000000035      7      0      0   0             0       0  is_stmt
+
+;; Test E:
+;; Check the is_stmt floats up to an instruction in the same group of the same
+;; or lower precedence.
+; DBG-NEXT: 0x000000000000003a      8      0      0   0             0       0  is_stmt
+; DBG-NEXT: 0x0000000000000041      8      0      0   0             0       0
+; DBG-NEXT: 0x0000000000000046      9      0      0   0             0       0  is_stmt
+
+; DBG-NEXT: 0x0000000000000052     10      0      0   0             0       0
+; DBG-NEXT: 0x0000000000000054     10      0      0   0             0       0  epilogue_begin
+; DBG-NEXT: 0x0000000000000059     10      0      0   0             0       0  end_sequence
+
+target triple = "x86_64-unknown-linux-gnu"
+
+ at a = global i32 0
+ at z = global i32 0
+
+define hidden i32 @fun() local_unnamed_addr !dbg !11 {
+entry:
+  %b = load i32, ptr @a,   !dbg !DILocation(line: 2, scope: !11)
+;; Test A:
+  tail call void @f(),     !dbg !DILocation(line: 3, scope: !11)
+;; Test B:
+  %x = tail call i32 @g(), !dbg !DILocation(line: 4, scope: !11, atomGroup: 1, atomRank: 2)
+  %y = add i32 %x, %b,     !dbg !DILocation(line: 4, scope: !11, atomGroup: 1, atomRank: 1)
+;; Test C:
+  store i32 1, ptr @z,     !dbg !DILocation(line: 5, scope: !11, atomGroup: 2, atomRank: 2)
+  tail call void @f(),     !dbg !DILocation(line: 5, scope: !11, atomGroup: 2, atomRank: 1)
+;; Test D:
+  store i32 2, ptr @z,     !dbg !DILocation(line: 6, scope: !11, atomGroup: 3, atomRank: 2)
+  tail call void @f(),     !dbg !DILocation(line: 7, scope: !11, atomGroup: 3, atomRank: 1)
+;; Test E:
+  store i32 3, ptr @z,     !dbg !DILocation(line: 8, scope: !11, atomGroup: 4, atomRank: 2)
+  tail call void @f(),     !dbg !DILocation(line: 8, scope: !11, atomGroup: 4, atomRank: 1)
+  store i32 4, ptr @z,     !dbg !DILocation(line: 9, scope: !11, atomGroup: 5, atomRank: 1)
+  tail call void @f(),     !dbg !DILocation(line: 9, scope: !11, atomGroup: 5, atomRank: 1)
+  ret i32 %y,              !dbg !DILocation(line: 10, scope: !11)
+}
+
+declare void @f() local_unnamed_addr
+declare i32  @g() local_unnamed_addr
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3}
+!llvm.ident = !{!10}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_17, file: !1, producer: "clang version 19.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: LineTablesOnly, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "test.cpp", directory: "/")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!10 = !{!"clang version 19.0.0"}
+!11 = distinct !DISubprogram(name: "fun", scope: !1, file: !1, line: 1, type: !12, scopeLine: 1, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0)
+!12 = !DISubroutineType(types: !13)
+!13 = !{}
diff --git a/llvm/test/DebugInfo/KeyInstructions/X86/dwarf-ranks-blocks.ll b/llvm/test/DebugInfo/KeyInstructions/X86/dwarf-ranks-blocks.ll
new file mode 100644
index 0000000000000..cd840ecce07f5
--- /dev/null
+++ b/llvm/test/DebugInfo/KeyInstructions/X86/dwarf-ranks-blocks.ll
@@ -0,0 +1,65 @@
+; RUN: llc %s --filetype=obj -o - --dwarf-use-key-instructions \
+; RUN: | llvm-objdump -d - --no-show-raw-insn \
+; RUN: | FileCheck %s --check-prefix=OBJ
+
+; RUN: llc %s --filetype=obj -o - --dwarf-use-key-instructions \
+; RUN: | llvm-dwarfdump - --debug-line \
+; RUN: | FileCheck %s --check-prefix=DBG
+
+;; Hand written. The stores and add are in the same atom group. Check that,
+;; despite each instruction belonging to a separate block, the stores (line 3)
+;; get is_stmt (both rank 1) and the add (line 2) does not (it's rank 2).
+
+; OBJ:      16: addl    %ebp, %ebx
+; OBJ-NEXT: 18: testb   $0x1, %r15b
+; OBJ-NEXT: 1c: je      0x23 <_Z1fPiii+0x23>
+; OBJ-NEXT: 1e: movl    %ebx, (%r14)
+; OBJ-NEXT: 21: jmp     0x26 <_Z1fPiii+0x26>
+; OBJ-NEXT: 23: movl    %ebp, (%r14)
+
+; DBG:      Address            Line   Column File   ISA Discriminator OpIndex Flags
+; DBG-NEXT: ------------------ ------ ------ ------ --- ------------- ------- -------------
+; DBG-NEXT: 0x0000000000000000      1      0      0   0             0       0  is_stmt
+; DBG-NEXT: 0x0000000000000011      1      0      0   0             0       0  is_stmt prologue_end
+;; The add: no is_stmt
+; DBG-NEXT: 0x0000000000000016      2      0      0   0             0       0
+; DBG-NEXT: 0x0000000000000018      1      0      0   0             0       0
+;; Both stores: is_stmt
+; DBG-NEXT: 0x000000000000001e      3      0      0   0             0       0  is_stmt
+; DBG-NEXT: 0x0000000000000023      3      0      0   0             0       0  is_stmt
+; DBG-NEXT: 0x0000000000000026      1      0      0   0             0       0
+; DBG-NEXT: 0x0000000000000028      1      0      0   0             0       0  epilogue_begin
+; DBG-NEXT: 0x0000000000000033      1      0      0   0             0       0  end_sequence
+
+target triple = "x86_64-unknown-linux-gnu"
+
+define hidden noundef i32 @_Z1fPiii(ptr %a, i32 %b, i32 %c, i1 %cond) local_unnamed_addr !dbg !11 {
+entry:
+  tail call void @_Z12prologue_endv(), !dbg !DILocation(line: 1, scope: !11)
+  %add = add nsw i32 %c, %b,           !dbg !DILocation(line: 2, scope: !11, atomGroup: 1, atomRank: 2)
+  br i1 %cond, label %bb1, label %bb2, !dbg !DILocation(line: 1, scope: !11)
+
+bb1:
+  store i32 %add, ptr %a, align 4,     !dbg !DILocation(line: 3, scope: !11, atomGroup: 1, atomRank: 1)
+  ret i32 %add,                        !dbg !DILocation(line: 1, scope: !11)
+
+bb2:
+  store i32 %b, ptr %a, align 4,       !dbg !DILocation(line: 3, scope: !11, atomGroup: 1, atomRank: 1)
+  store i32 %c, ptr %a, align 4,       !dbg !DILocation(line: 3, scope: !11, atomGroup: 1, atomRank: 1)
+  ret i32 %add,                        !dbg !DILocation(line: 1, scope: !11)
+}
+
+declare void @_Z12prologue_endv() local_unnamed_addr #1
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3}
+!llvm.ident = !{!10}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_17, file: !1, producer: "clang version 19.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: LineTablesOnly, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "test.cpp", directory: "/")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!10 = !{!"clang version 19.0.0"}
+!11 = distinct !DISubprogram(name: "f", scope: !1, file: !1, line: 1, type: !12, scopeLine: 1, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0)
+!12 = !DISubroutineType(types: !13)
+!13 = !{}

>From 3b8ef1a64088d1f89840ae609de8e78e31bc0298 Mon Sep 17 00:00:00 2001
From: Orlando Cazalet-Hyams <orlando.hyams at sony.com>
Date: Thu, 8 May 2025 15:24:26 +0100
Subject: [PATCH 02/13] format

---
 llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
index 792441607eec2..a1de323ec322d 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
@@ -2417,7 +2417,8 @@ void DwarfDebug::findKeyInstructions(const MachineFunction *MF) {
         uint8_t Rank = MI.getDebugLoc()->getAtomRank();
         if (Group && Rank) {
           auto *InlinedAt = MI.getDebugLoc()->getInlinedAt();
-          auto &[CandidateRank, CandidateInsts] = GroupCandidates[{InlinedAt, Group}];
+          auto &[CandidateRank, CandidateInsts] =
+              GroupCandidates[{InlinedAt, Group}];
 
           // This looks similar to the non-call handling code, except that
           // we don't put the call into CandidateInsts so that they can't be
@@ -2475,7 +2476,8 @@ void DwarfDebug::findKeyInstructions(const MachineFunction *MF) {
         BuoyAtom = MI.getDebugLoc()->getAtomGroup();
       }
 
-      auto &[CandidateRank, CandidateInsts] = GroupCandidates[{InlinedAt, Group}];
+      auto &[CandidateRank, CandidateInsts] =
+          GroupCandidates[{InlinedAt, Group}];
 
       if (CandidateRank == 0) {
         // This is the first time we're seeing an instruction in this atom

>From f9cca03779c1be0c27a504a18132a3d1be5e7275 Mon Sep 17 00:00:00 2001
From: Orlando Cazalet-Hyams <orlando.hyams at sony.com>
Date: Thu, 8 May 2025 15:40:38 +0100
Subject: [PATCH 03/13] nits

---
 llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp    | 90 ++++++++++---------
 llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h      |  6 +-
 .../KeyInstructions/X86/dwarf-calls.ll        |  2 +-
 3 files changed, 50 insertions(+), 48 deletions(-)

diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
index a1de323ec322d..0703dff0cfeca 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
@@ -2357,7 +2357,7 @@ DwarfDebug::emitInitialLocDirective(const MachineFunction &MF, unsigned CUID) {
   return PrologEndLoc;
 }
 
-void DwarfDebug::findKeyInstructions(const MachineFunction *MF) {
+void DwarfDebug::computeKeyInstructions(const MachineFunction *MF) {
   // New function - reset KeyInstructions.
   KeyInstructions.clear();
 
@@ -2396,7 +2396,7 @@ void DwarfDebug::findKeyInstructions(const MachineFunction *MF) {
       if (!MI.getDebugLoc() || !MI.getDebugLoc().getLine())
         continue;
 
-      // Reset the Buoy to this instruciton if it has a different line number.
+      // Reset the Buoy to this instruction if it has a different line number.
       if (!Buoy ||
           Buoy->getDebugLoc().getLine() != MI.getDebugLoc().getLine()) {
         Buoy = &MI;
@@ -2415,52 +2415,54 @@ void DwarfDebug::findKeyInstructions(const MachineFunction *MF) {
 
         uint64_t Group = MI.getDebugLoc()->getAtomGroup();
         uint8_t Rank = MI.getDebugLoc()->getAtomRank();
-        if (Group && Rank) {
-          auto *InlinedAt = MI.getDebugLoc()->getInlinedAt();
-          auto &[CandidateRank, CandidateInsts] =
-              GroupCandidates[{InlinedAt, Group}];
-
-          // This looks similar to the non-call handling code, except that
-          // we don't put the call into CandidateInsts so that they can't be
-          // made un-key. As a result, we also have to take special care not
-          // to erase the is_stmt from the buoy, and prevent that happening
-          // in the future.
-
-          if (CandidateRank == Rank) {
-            // We've seen other instructions in this group of this rank. Discard
-            // ones we've seen in this block, keep the others.
-            assert(!CandidateInsts.empty());
-            SmallVector<const MachineInstr *> Insts;
-            Insts.reserve(CandidateInsts.size());
-            for (auto &PrevInst : CandidateInsts) {
-              if (PrevInst->getParent() != MI.getParent())
-                Insts.push_back(PrevInst);
-              else if (PrevInst != Buoy)
-                KeyInstructions.erase(PrevInst);
-            }
-
-            if (Insts.empty()) {
-              CandidateInsts.clear();
-              CandidateRank = 0;
-            } else {
-              CandidateInsts = std::move(Insts);
-            }
-
-          } else if (CandidateRank > Rank) {
-            // We've seen other instructions in this group of lower precedence
-            // (higher rank). Discard them.
-            for (auto *Supplanted : CandidateInsts) {
-              // Don't erase the is_stmt we're using for this call.
-              if (Supplanted != Buoy)
-                KeyInstructions.erase(Supplanted);
-            }
+        if (!Group || !Rank) {
+          Buoy = nullptr; // Avoid floating any future is_stmts up to the call.
+          continue;
+        }
+
+        auto *InlinedAt = MI.getDebugLoc()->getInlinedAt();
+        auto &[CandidateRank, CandidateInsts] =
+            GroupCandidates[{InlinedAt, Group}];
+
+        // This looks similar to the non-call handling code, except that
+        // we don't put the call into CandidateInsts so that they can't be
+        // made un-key. As a result, we also have to take special care not
+        // to erase the is_stmt from the buoy, and prevent that happening
+        // in the future.
+
+        if (CandidateRank == Rank) {
+          // We've seen other instructions in this group of this rank. Discard
+          // ones we've seen in this block, keep the others.
+          assert(!CandidateInsts.empty());
+          SmallVector<const MachineInstr *> Insts;
+          Insts.reserve(CandidateInsts.size());
+          for (auto &PrevInst : CandidateInsts) {
+            if (PrevInst->getParent() != MI.getParent())
+              Insts.push_back(PrevInst);
+            else if (PrevInst != Buoy)
+              KeyInstructions.erase(PrevInst);
+          }
+
+          if (Insts.empty()) {
             CandidateInsts.clear();
             CandidateRank = 0;
+          } else {
+            CandidateInsts = std::move(Insts);
+          }
+
+        } else if (CandidateRank > Rank) {
+          // We've seen other instructions in this group of lower precedence
+          // (higher rank). Discard them.
+          for (auto *Supplanted : CandidateInsts) {
+            // Don't erase the is_stmt we're using for this call.
+            if (Supplanted != Buoy)
+              KeyInstructions.erase(Supplanted);
           }
+          CandidateInsts.clear();
+          CandidateRank = 0;
         }
 
-        // Avoid floating any future is_stmts up to the call.
-        Buoy = nullptr;
+        Buoy = nullptr; // Avoid floating any future is_stmts up to the call.
         continue;
       }
 
@@ -2682,7 +2684,7 @@ void DwarfDebug::beginFunctionImpl(const MachineFunction *MF) {
       *MF, Asm->OutStreamer->getContext().getDwarfCompileUnitID());
 
   if (KeyInstructionsAreStmts)
-    findKeyInstructions(MF);
+    computeKeyInstructions(MF);
   else
     findForceIsStmtInstrs(MF);
 }
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h
index cd232a8e94ecf..89813dcf0fdab 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h
@@ -705,10 +705,10 @@ class DwarfDebug : public DebugHandlerBase {
 
   void findForceIsStmtInstrs(const MachineFunction *MF);
 
-  /// Find instructions which should get is_stmt applied because they implement
-  /// key functionality for a source atom, store results in
+  /// Compute instructions which should get is_stmt applied because they
+  /// implement key functionality for a source location atom, store results in
   /// DwarfDebug::KeyInstructions.
-  void findKeyInstructions(const MachineFunction *MF);
+  void computeKeyInstructions(const MachineFunction *MF);
 
 protected:
   /// Gather pre-function debug information.
diff --git a/llvm/test/DebugInfo/KeyInstructions/X86/dwarf-calls.ll b/llvm/test/DebugInfo/KeyInstructions/X86/dwarf-calls.ll
index 780374127707e..6f3640a40932a 100644
--- a/llvm/test/DebugInfo/KeyInstructions/X86/dwarf-calls.ll
+++ b/llvm/test/DebugInfo/KeyInstructions/X86/dwarf-calls.ll
@@ -44,7 +44,7 @@
 ;; Check the 2nd call (line 4) gets is_stmt applied despite being part of group
 ;; 1 and having lower precedence than the add. Check that the add stil gets
 ;; is_stmt applied.
-;; There are two is_stmt line 4 entries are is_stmt because we don't float
+;; There are two is_stmt line 4 entries because we don't float
 ;; is_stmts up on the same line past other key instructions. The call is
 ;; key, so the add's is_stmt floats up to the movl on the same line, but
 ;; not past the call.

>From 2fb14b5f9bfa4c7e1eeb8791aa70892de018e30b Mon Sep 17 00:00:00 2001
From: Orlando Cazalet-Hyams <orlando.hyams at sony.com>
Date: Thu, 8 May 2025 18:23:14 +0100
Subject: [PATCH 04/13] defer insertion into KeyInstructions - simplifies code

---
 llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp | 25 +++++++---------------
 1 file changed, 8 insertions(+), 17 deletions(-)

diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
index 0703dff0cfeca..f4c7a83eaa697 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
@@ -2410,7 +2410,9 @@ void DwarfDebug::computeKeyInstructions(const MachineFunction *MF) {
       if (MI.isCall() || TII.isTailCall(MI)) {
         assert(MI.getDebugLoc() && "Unexpectedly missing DL");
 
-        // Calls are always key.
+        // Calls are always key. Put the buoy (may not be the call) into
+        // KeyInstructions directly rather than the candidate map to avoid it
+        // being erased (and we may not have a group number for the call).
         KeyInstructions.insert(Buoy);
 
         uint64_t Group = MI.getDebugLoc()->getAtomGroup();
@@ -2426,10 +2428,7 @@ void DwarfDebug::computeKeyInstructions(const MachineFunction *MF) {
 
         // This looks similar to the non-call handling code, except that
         // we don't put the call into CandidateInsts so that they can't be
-        // made un-key. As a result, we also have to take special care not
-        // to erase the is_stmt from the buoy, and prevent that happening
-        // in the future.
-
+        // made un-key.
         if (CandidateRank == Rank) {
           // We've seen other instructions in this group of this rank. Discard
           // ones we've seen in this block, keep the others.
@@ -2439,8 +2438,6 @@ void DwarfDebug::computeKeyInstructions(const MachineFunction *MF) {
           for (auto &PrevInst : CandidateInsts) {
             if (PrevInst->getParent() != MI.getParent())
               Insts.push_back(PrevInst);
-            else if (PrevInst != Buoy)
-              KeyInstructions.erase(PrevInst);
           }
 
           if (Insts.empty()) {
@@ -2453,11 +2450,6 @@ void DwarfDebug::computeKeyInstructions(const MachineFunction *MF) {
         } else if (CandidateRank > Rank) {
           // We've seen other instructions in this group of lower precedence
           // (higher rank). Discard them.
-          for (auto *Supplanted : CandidateInsts) {
-            // Don't erase the is_stmt we're using for this call.
-            if (Supplanted != Buoy)
-              KeyInstructions.erase(Supplanted);
-          }
           CandidateInsts.clear();
           CandidateRank = 0;
         }
@@ -2497,8 +2489,6 @@ void DwarfDebug::computeKeyInstructions(const MachineFunction *MF) {
         for (auto &PrevInst : CandidateInsts) {
           if (PrevInst->getParent() != MI.getParent())
             Insts.push_back(PrevInst);
-          else
-            KeyInstructions.erase(PrevInst);
         }
         Insts.push_back(Buoy);
         CandidateInsts = std::move(Insts);
@@ -2508,8 +2498,6 @@ void DwarfDebug::computeKeyInstructions(const MachineFunction *MF) {
         // (higher rank). Discard them, add this one.
         assert(!CandidateInsts.empty());
         CandidateRank = Rank;
-        for (auto *Supplanted : CandidateInsts)
-          KeyInstructions.erase(Supplanted);
         CandidateInsts = {Buoy};
 
       } else {
@@ -2518,11 +2506,14 @@ void DwarfDebug::computeKeyInstructions(const MachineFunction *MF) {
         assert(Rank != 0 && CandidateRank < Rank && CandidateRank != 0);
         continue;
       }
-      KeyInstructions.insert(Buoy);
       assert(!BuoyAtom || BuoyAtom == MI.getDebugLoc()->getAtomGroup());
       BuoyAtom = MI.getDebugLoc()->getAtomGroup();
     }
   }
+
+  for (const auto &[_, Insts] : GroupCandidates.values())
+    for (auto *I : Insts)
+      KeyInstructions.insert(I);
 }
 
 /// For the function \p MF, finds the set of instructions which may represent a

>From 911905f9309dfbb302b89d8a13244daa6f83bf2b Mon Sep 17 00:00:00 2001
From: Orlando Cazalet-Hyams <orlando.hyams at sony.com>
Date: Thu, 8 May 2025 18:44:56 +0100
Subject: [PATCH 05/13] simplfy by using use remove_if

---
 llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp | 29 +++++++---------------
 1 file changed, 9 insertions(+), 20 deletions(-)

diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
index f4c7a83eaa697..9f4301e217a3f 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
@@ -2433,19 +2433,12 @@ void DwarfDebug::computeKeyInstructions(const MachineFunction *MF) {
           // We've seen other instructions in this group of this rank. Discard
           // ones we've seen in this block, keep the others.
           assert(!CandidateInsts.empty());
-          SmallVector<const MachineInstr *> Insts;
-          Insts.reserve(CandidateInsts.size());
-          for (auto &PrevInst : CandidateInsts) {
-            if (PrevInst->getParent() != MI.getParent())
-              Insts.push_back(PrevInst);
-          }
-
-          if (Insts.empty()) {
-            CandidateInsts.clear();
+          llvm::remove_if(CandidateInsts, [&MI](const MachineInstr *Candidate) {
+            return MI.getParent() == Candidate->getParent();
+          });
+
+          if (CandidateInsts.empty())
             CandidateRank = 0;
-          } else {
-            CandidateInsts = std::move(Insts);
-          }
 
         } else if (CandidateRank > Rank) {
           // We've seen other instructions in this group of lower precedence
@@ -2484,14 +2477,10 @@ void DwarfDebug::computeKeyInstructions(const MachineFunction *MF) {
         // We've seen other instructions in this group of this rank. Discard
         // ones we've seen in this block, keep the others, add this one.
         assert(!CandidateInsts.empty());
-        SmallVector<const MachineInstr *> Insts;
-        Insts.reserve(CandidateInsts.size() + 1);
-        for (auto &PrevInst : CandidateInsts) {
-          if (PrevInst->getParent() != MI.getParent())
-            Insts.push_back(PrevInst);
-        }
-        Insts.push_back(Buoy);
-        CandidateInsts = std::move(Insts);
+        llvm::remove_if(CandidateInsts, [&MI](const MachineInstr *Candidate) {
+          return MI.getParent() == Candidate->getParent();
+        });
+        CandidateInsts.push_back(Buoy);
 
       } else if (CandidateRank > Rank) {
         // We've seen other instructions in this group of lower precedence

>From ed775f4c4a33f5a4168d304141ebd0bd5b3f42e0 Mon Sep 17 00:00:00 2001
From: Orlando Cazalet-Hyams <orlando.hyams at sony.com>
Date: Thu, 8 May 2025 18:47:34 +0100
Subject: [PATCH 06/13] improve comments

---
 llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
index 9f4301e217a3f..cab3ca64f8c31 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
@@ -2370,12 +2370,14 @@ void DwarfDebug::computeKeyInstructions(const MachineFunction *MF) {
   // For each instruction:
   //   * Skip insts without DebugLoc, AtomGroup or AtomRank, and line zeros.
   //   * Check if insts in this group have been seen already in GroupCandidates.
-  //     * If this instr rank is equal, add this instruction to KeyInstructions.
-  //       Remove existing instructions from KeyInstructions if they have the
+  //     * If this instr rank is equal, add this instruction to GroupCandidates.
+  //       Remove existing instructions from GroupCandidates if they have the
   //       same parent.
   //     * If this instr rank is higher (lower precedence), ignore it.
   //     * If this instr rank is lower (higher precedence), erase existing
-  //       instructions from KeyInstructions. Add this instr to KeyInstructions.
+  //       instructions from GroupCandidates and add this one.
+  //
+  // Then insert each GroupCandidates instruction into KeyInstructions.
 
   for (auto &MBB : *MF) {
     // Rather than apply is_stmt directly to Key Instructions, we "float"
@@ -2400,7 +2402,7 @@ void DwarfDebug::computeKeyInstructions(const MachineFunction *MF) {
       if (!Buoy ||
           Buoy->getDebugLoc().getLine() != MI.getDebugLoc().getLine()) {
         Buoy = &MI;
-        BuoyAtom = 0;
+        BuoyAtom = 0; // Set later when we know which atom the buoy is used by.
       }
 
       // Call instructions are handled specially - we always mark them as key

>From 2428132841bf0a747aa7a78ebeee497d0752e0b1 Mon Sep 17 00:00:00 2001
From: Orlando Cazalet-Hyams <orlando.hyams at sony.com>
Date: Thu, 8 May 2025 19:04:00 +0100
Subject: [PATCH 07/13] simplify: sink common bits

---
 llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
index cab3ca64f8c31..b7aa6812e49f3 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
@@ -2472,8 +2472,6 @@ void DwarfDebug::computeKeyInstructions(const MachineFunction *MF) {
         // This is the first time we're seeing an instruction in this atom
         // group. Add it to the map.
         assert(CandidateInsts.empty());
-        CandidateRank = Rank;
-        CandidateInsts.push_back(Buoy);
 
       } else if (CandidateRank == Rank) {
         // We've seen other instructions in this group of this rank. Discard
@@ -2482,14 +2480,12 @@ void DwarfDebug::computeKeyInstructions(const MachineFunction *MF) {
         llvm::remove_if(CandidateInsts, [&MI](const MachineInstr *Candidate) {
           return MI.getParent() == Candidate->getParent();
         });
-        CandidateInsts.push_back(Buoy);
 
       } else if (CandidateRank > Rank) {
         // We've seen other instructions in this group of lower precedence
         // (higher rank). Discard them, add this one.
         assert(!CandidateInsts.empty());
-        CandidateRank = Rank;
-        CandidateInsts = {Buoy};
+        CandidateInsts.clear();
 
       } else {
         // We've seen other instructions in this group with higher precedence
@@ -2499,6 +2495,9 @@ void DwarfDebug::computeKeyInstructions(const MachineFunction *MF) {
       }
       assert(!BuoyAtom || BuoyAtom == MI.getDebugLoc()->getAtomGroup());
       BuoyAtom = MI.getDebugLoc()->getAtomGroup();
+
+      CandidateInsts.push_back(Buoy);
+      CandidateRank = Rank;
     }
   }
 

>From 69938cf6919a6f23a49e098744e64a983cb2bfd8 Mon Sep 17 00:00:00 2001
From: Orlando Cazalet-Hyams <orlando.hyams at sony.com>
Date: Thu, 8 May 2025 19:09:50 +0100
Subject: [PATCH 08/13] remove some now useless ctrl-flow, keeping asserts

---
 llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp | 18 +++++++++++-------
 1 file changed, 11 insertions(+), 7 deletions(-)

diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
index b7aa6812e49f3..260907e4cc711 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
@@ -2468,12 +2468,15 @@ void DwarfDebug::computeKeyInstructions(const MachineFunction *MF) {
       auto &[CandidateRank, CandidateInsts] =
           GroupCandidates[{InlinedAt, Group}];
 
-      if (CandidateRank == 0) {
-        // This is the first time we're seeing an instruction in this atom
-        // group. Add it to the map.
-        assert(CandidateInsts.empty());
-
-      } else if (CandidateRank == Rank) {
+      // If CandidateRank is zero then CandidateInsts should be empty: there
+      // are no other candidates for this group yet. If CandidateRank is nonzero
+      // then CandidateInsts shouldn't be empty: we've got existing candidate
+      // instructions.
+      assert((CandidateRank == 0 && CandidateInsts.empty()) ||
+             (CandidateRank != 0 && !CandidateInsts.empty()));
+
+      assert(Rank && "expected nonzero rank");
+      if (CandidateRank == Rank) {
         // We've seen other instructions in this group of this rank. Discard
         // ones we've seen in this block, keep the others, add this one.
         assert(!CandidateInsts.empty());
@@ -2487,12 +2490,13 @@ void DwarfDebug::computeKeyInstructions(const MachineFunction *MF) {
         assert(!CandidateInsts.empty());
         CandidateInsts.clear();
 
-      } else {
+      } else if (CandidateRank) {
         // We've seen other instructions in this group with higher precedence
         // (lower rank). Discard this one.
         assert(Rank != 0 && CandidateRank < Rank && CandidateRank != 0);
         continue;
       }
+
       assert(!BuoyAtom || BuoyAtom == MI.getDebugLoc()->getAtomGroup());
       BuoyAtom = MI.getDebugLoc()->getAtomGroup();
 

>From 9854b9532944eafc3762edb6b522680ae5a58619 Mon Sep 17 00:00:00 2001
From: Orlando Cazalet-Hyams <orlando.hyams at sony.com>
Date: Thu, 8 May 2025 19:15:39 +0100
Subject: [PATCH 09/13] simplify remaining ctrl-flow

---
 llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp | 32 ++++++++++------------
 1 file changed, 14 insertions(+), 18 deletions(-)

diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
index 260907e4cc711..5f2d7b465c399 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
@@ -2476,32 +2476,28 @@ void DwarfDebug::computeKeyInstructions(const MachineFunction *MF) {
              (CandidateRank != 0 && !CandidateInsts.empty()));
 
       assert(Rank && "expected nonzero rank");
-      if (CandidateRank == Rank) {
-        // We've seen other instructions in this group of this rank. Discard
-        // ones we've seen in this block, keep the others, add this one.
-        assert(!CandidateInsts.empty());
+      // If we've seen other instructions in this group with higher precedence
+      // (lower nonzero rank), don't add this one as a candidate.
+      if (CandidateRank && CandidateRank < Rank)
+        continue;
+
+      // If we've seen other instructions in this group of the same rank,
+      // discard any from this block (keeping the others). Else if we've
+      // seen other instructions in this group of lower precedence (higher
+      // rank), discard them all.
+      if (CandidateRank == Rank)
         llvm::remove_if(CandidateInsts, [&MI](const MachineInstr *Candidate) {
           return MI.getParent() == Candidate->getParent();
         });
-
-      } else if (CandidateRank > Rank) {
-        // We've seen other instructions in this group of lower precedence
-        // (higher rank). Discard them, add this one.
-        assert(!CandidateInsts.empty());
+      else if (CandidateRank > Rank)
         CandidateInsts.clear();
 
-      } else if (CandidateRank) {
-        // We've seen other instructions in this group with higher precedence
-        // (lower rank). Discard this one.
-        assert(Rank != 0 && CandidateRank < Rank && CandidateRank != 0);
-        continue;
-      }
+      // Add this candidate.
+      CandidateInsts.push_back(Buoy);
+      CandidateRank = Rank;
 
       assert(!BuoyAtom || BuoyAtom == MI.getDebugLoc()->getAtomGroup());
       BuoyAtom = MI.getDebugLoc()->getAtomGroup();
-
-      CandidateInsts.push_back(Buoy);
-      CandidateRank = Rank;
     }
   }
 

>From 317d98b0b028ea293a01da56e8e3150f9cdb015a Mon Sep 17 00:00:00 2001
From: Orlando Cazalet-Hyams <orlando.hyams at sony.com>
Date: Thu, 8 May 2025 19:21:47 +0100
Subject: [PATCH 10/13] simplify call handling

---
 llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp | 60 ++++++++--------------
 1 file changed, 21 insertions(+), 39 deletions(-)

diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
index 5f2d7b465c399..9069def6b96ec 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
@@ -2409,7 +2409,8 @@ void DwarfDebug::computeKeyInstructions(const MachineFunction *MF) {
       // regardless of atom info.
       const auto &TII =
           *MI.getParent()->getParent()->getSubtarget().getInstrInfo();
-      if (MI.isCall() || TII.isTailCall(MI)) {
+      bool IsCallLike = MI.isCall() || TII.isTailCall(MI);
+      if (IsCallLike) {
         assert(MI.getDebugLoc() && "Unexpectedly missing DL");
 
         // Calls are always key. Put the buoy (may not be the call) into
@@ -2417,40 +2418,13 @@ void DwarfDebug::computeKeyInstructions(const MachineFunction *MF) {
         // being erased (and we may not have a group number for the call).
         KeyInstructions.insert(Buoy);
 
-        uint64_t Group = MI.getDebugLoc()->getAtomGroup();
-        uint8_t Rank = MI.getDebugLoc()->getAtomRank();
-        if (!Group || !Rank) {
-          Buoy = nullptr; // Avoid floating any future is_stmts up to the call.
-          continue;
-        }
-
-        auto *InlinedAt = MI.getDebugLoc()->getInlinedAt();
-        auto &[CandidateRank, CandidateInsts] =
-            GroupCandidates[{InlinedAt, Group}];
-
-        // This looks similar to the non-call handling code, except that
-        // we don't put the call into CandidateInsts so that they can't be
-        // made un-key.
-        if (CandidateRank == Rank) {
-          // We've seen other instructions in this group of this rank. Discard
-          // ones we've seen in this block, keep the others.
-          assert(!CandidateInsts.empty());
-          llvm::remove_if(CandidateInsts, [&MI](const MachineInstr *Candidate) {
-            return MI.getParent() == Candidate->getParent();
-          });
-
-          if (CandidateInsts.empty())
-            CandidateRank = 0;
-
-        } else if (CandidateRank > Rank) {
-          // We've seen other instructions in this group of lower precedence
-          // (higher rank). Discard them.
-          CandidateInsts.clear();
-          CandidateRank = 0;
-        }
+        // Avoid floating any future is_stmts up to the call.
+        Buoy = nullptr;
+        BuoyAtom = 0;
 
-        Buoy = nullptr; // Avoid floating any future is_stmts up to the call.
-        continue;
+        if (!MI.getDebugLoc()->getAtomGroup() ||
+            !MI.getDebugLoc()->getAtomRank())
+          continue;
       }
 
       auto *InlinedAt = MI.getDebugLoc()->getInlinedAt();
@@ -2492,12 +2466,20 @@ void DwarfDebug::computeKeyInstructions(const MachineFunction *MF) {
       else if (CandidateRank > Rank)
         CandidateInsts.clear();
 
-      // Add this candidate.
-      CandidateInsts.push_back(Buoy);
-      CandidateRank = Rank;
+      if (Buoy) {
+        // Add this candidate.
+        CandidateInsts.push_back(Buoy);
+        CandidateRank = Rank;
 
-      assert(!BuoyAtom || BuoyAtom == MI.getDebugLoc()->getAtomGroup());
-      BuoyAtom = MI.getDebugLoc()->getAtomGroup();
+        assert(!BuoyAtom || BuoyAtom == MI.getDebugLoc()->getAtomGroup());
+        BuoyAtom = MI.getDebugLoc()->getAtomGroup();
+      } else {
+        // Don't add calls, because they've been dealt with already. This means
+        // CandidateInsts might now be empty - handle that.
+        assert(IsCallLike);
+        if (CandidateInsts.empty())
+          CandidateRank = 0;
+      }
     }
   }
 

>From 80b8bb4ce6569c2d9ff9548b2ad505d2e8a19091 Mon Sep 17 00:00:00 2001
From: Orlando Cazalet-Hyams <orlando.hyams at sony.com>
Date: Fri, 9 May 2025 12:16:41 +0100
Subject: [PATCH 11/13] Update llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp

review nit, use `Group`,  "Avoids the appearance that this could be different from the one earlier."

Co-authored-by: Jeremy Morse <jeremy.morse at sony.com>
---
 llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
index 9069def6b96ec..fe6bdce228906 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
@@ -2436,7 +2436,7 @@ void DwarfDebug::computeKeyInstructions(const MachineFunction *MF) {
       // Don't let is_stmts float past instructions from different source atoms.
       if (BuoyAtom && BuoyAtom != Group) {
         Buoy = &MI;
-        BuoyAtom = MI.getDebugLoc()->getAtomGroup();
+        BuoyAtom = Group;
       }
 
       auto &[CandidateRank, CandidateInsts] =

>From a1ae08ed1e7f08045657b61a51647287f1671bc4 Mon Sep 17 00:00:00 2001
From: Orlando Cazalet-Hyams <orlando.hyams at sony.com>
Date: Fri, 9 May 2025 13:00:54 +0100
Subject: [PATCH 12/13] fix line entries not folding together properly

---
 llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp               | 2 +-
 .../KeyInstructions/X86/dwarf-buoy-multi-key.mir         | 5 +----
 llvm/test/DebugInfo/KeyInstructions/X86/dwarf-calls.ll   | 9 +++------
 3 files changed, 5 insertions(+), 11 deletions(-)

diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
index fe6bdce228906..95cac03c446b4 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
@@ -2092,7 +2092,7 @@ void DwarfDebug::beginInstruction(const MachineInstr *MI) {
       (!PrevInstBB ||
        PrevInstBB->getSectionID() == MI->getParent()->getSectionID());
   bool ForceIsStmt = ForceIsStmtInstrs.contains(MI);
-  if (DL == PrevInstLoc && PrevInstInSameSection && !ForceIsStmt) {
+  if (PrevInstInSameSection && !ForceIsStmt && DL.isSameSourceLocation(PrevInstLoc)) {
     // If we have an ongoing unspecified location, nothing to do here.
     if (!DL)
       return;
diff --git a/llvm/test/DebugInfo/KeyInstructions/X86/dwarf-buoy-multi-key.mir b/llvm/test/DebugInfo/KeyInstructions/X86/dwarf-buoy-multi-key.mir
index c8459b4ced600..3b7d5850e7e83 100644
--- a/llvm/test/DebugInfo/KeyInstructions/X86/dwarf-buoy-multi-key.mir
+++ b/llvm/test/DebugInfo/KeyInstructions/X86/dwarf-buoy-multi-key.mir
@@ -21,10 +21,7 @@
 # DBG-NEXT: ------------------ ------ ------ ------ --- ------------- ------- -------------
 # DBG-NEXT: 0x0000000000000000      1      0      0   0             0       0  is_stmt prologue_end
 # DBG-NEXT: 0x0000000000000005      2      0      0   0             0       0  is_stmt
-# DBG-NEXT: 0x000000000000000a      2      0      0   0             0       0
-# DBG-NEXT: 0x000000000000000f      2      0      0   0             0       0
-# DBG-NEXT: 0x0000000000000014      2      0      0   0             0       0
-# DBG-NEXT: 0x0000000000000019      2      0      0   0             0       0
+# DBG-NEXT: 0x0000000000000019      2      0      0   0             0       0  is_stmt
 # DBG-NEXT: 0x000000000000001e      2      0      0   0             0       0  is_stmt
 # DBG-NEXT: 0x0000000000000023      2      0      0   0             0       0  is_stmt
 # DBG-NEXT: 0x0000000000000029      2      0      0   0             0       0  is_stmt end_sequence
diff --git a/llvm/test/DebugInfo/KeyInstructions/X86/dwarf-calls.ll b/llvm/test/DebugInfo/KeyInstructions/X86/dwarf-calls.ll
index 6f3640a40932a..14dfa8a6a9ccb 100644
--- a/llvm/test/DebugInfo/KeyInstructions/X86/dwarf-calls.ll
+++ b/llvm/test/DebugInfo/KeyInstructions/X86/dwarf-calls.ll
@@ -50,23 +50,20 @@
 ;; not past the call.
 ; DBG-NEXT: 0x0000000000000012      4      0      0   0             0       0  is_stmt
 ; DBG-NEXT: 0x0000000000000017      4      0      0   0             0       0  is_stmt
-; DBG-NEXT: 0x0000000000000019      4      0      0   0             0       0
 
 ;; Test C:
-;; Check that is_stmt floats up from the call to the store.
+;; Check that is_stmt floats up from the call (0x29) to the store (0x1b).
 ; DBG-NEXT: 0x000000000000001b      5      0      0   0             0       0  is_stmt
-; DBG-NEXT: 0x0000000000000029      5      0      0   0             0       0
 
 ;; Test D:
-;; Check the is_stmt is not applied to the lower ranking instruction.
+;; Check the is_stmt is not applied to the lower ranking instruction (0x2e).
 ; DBG-NEXT: 0x000000000000002e      6      0      0   0             0       0
 ; DBG-NEXT: 0x0000000000000035      7      0      0   0             0       0  is_stmt
 
 ;; Test E:
 ;; Check the is_stmt floats up to an instruction in the same group of the same
-;; or lower precedence.
+;; or lower precedence (from call, 0x41, to `store 3`, 0x3a).
 ; DBG-NEXT: 0x000000000000003a      8      0      0   0             0       0  is_stmt
-; DBG-NEXT: 0x0000000000000041      8      0      0   0             0       0
 ; DBG-NEXT: 0x0000000000000046      9      0      0   0             0       0  is_stmt
 
 ; DBG-NEXT: 0x0000000000000052     10      0      0   0             0       0

>From 8c67d683f23d88319e865605369803549ab3fae6 Mon Sep 17 00:00:00 2001
From: Orlando Cazalet-Hyams <orlando.hyams at sony.com>
Date: Fri, 9 May 2025 13:06:11 +0100
Subject: [PATCH 13/13] shuffle input source comment around in tests

---
 .../KeyInstructions/X86/dwarf-basic-ranks.ll  | 23 +++++++++---------
 .../KeyInstructions/X86/dwarf-basic.ll        | 24 +++++++++----------
 2 files changed, 24 insertions(+), 23 deletions(-)

diff --git a/llvm/test/DebugInfo/KeyInstructions/X86/dwarf-basic-ranks.ll b/llvm/test/DebugInfo/KeyInstructions/X86/dwarf-basic-ranks.ll
index 71ecf1dc41238..6988a6fb2ec3f 100644
--- a/llvm/test/DebugInfo/KeyInstructions/X86/dwarf-basic-ranks.ll
+++ b/llvm/test/DebugInfo/KeyInstructions/X86/dwarf-basic-ranks.ll
@@ -6,6 +6,18 @@
 ; RUN: | llvm-dwarfdump - --debug-line \
 ; RUN: | FileCheck %s --check-prefix=DBG
 
+;; 1. [[gnu::nodebug]] void prologue_end();
+;; 2.
+;; 3. int f(int *a, int b, int c) {
+;; 4.   prologue_end();
+;; 5.   *a =
+;; 6.     b + c;
+;; 7.   return *a;
+;; 8. }
+;;
+;; The add and store are in the same group (1). The add (line 6) has lower
+;; precedence (rank 2) so should not get is_stmt applied.
+
 ; OBJ: 0000000000000000 <_Z1fPiii>:
 ; OBJ-NEXT:  0: pushq   %rbp
 ; OBJ-NEXT:  1: pushq   %r14
@@ -31,17 +43,6 @@
 ; DBG-NEXT: 0x0000000000000017      7      0      0   0             0       0  epilogue_begin
 ; DBG-NEXT: 0x000000000000001c      7      0      0   0             0       0  end_sequence
 
-;; 1. [[gnu::nodebug]] void prologue_end();
-;; 2.
-;; 3. int f(int *a, int b, int c) {
-;; 4.   prologue_end();
-;; 5.   *a = 
-;; 6.     b + c;
-;; 7.   return *a;
-;; 8. }
-
-;; The add and store are in the same goup (1). The add (line 6) has lower
-;; precedence (rank 2) so should not get is_stmt applied.
 target triple = "x86_64-unknown-linux-gnu"
 
 define hidden noundef i32 @_Z1fPiii(ptr %a, i32 %b, i32 %c) local_unnamed_addr !dbg !11 {
diff --git a/llvm/test/DebugInfo/KeyInstructions/X86/dwarf-basic.ll b/llvm/test/DebugInfo/KeyInstructions/X86/dwarf-basic.ll
index e3b0184a837f8..ba38bb2ffe903 100644
--- a/llvm/test/DebugInfo/KeyInstructions/X86/dwarf-basic.ll
+++ b/llvm/test/DebugInfo/KeyInstructions/X86/dwarf-basic.ll
@@ -6,6 +6,18 @@
 ; RUN: | llvm-dwarfdump - --debug-line \
 ; RUN: | FileCheck %s --check-prefix=DBG
 
+;; 1. int f(int a) {
+;; 2.   int x = a + 1;
+;; 3.   return x;
+;; 4. }
+;; 5. int g(int b) {
+;; 6.   return f(b);
+;; 7. }
+;;
+;; Both functions contain 2 instructions in unique atom groups. In f we see
+;; groups 1 and 3, and in g we see {!18, 1} and 1. All of these instructions
+;; should get is_stmt.
+
 ; OBJ: 0000000000000000 <_Z1fi>:
 ; OBJ-NEXT: 0: leal    0x1(%rdi), %eax
 ; OBJ-NEXT: 3: retq
@@ -20,18 +32,6 @@
 ; DBG-NEXT: 0x0000000000000010      2      0      0   0             0       0  is_stmt prologue_end
 ; DBG-NEXT: 0x0000000000000013      6      0      0   0             0       0  is_stmt
 
-;; 1. int f(int a) {
-;; 2.   int x = a + 1;
-;; 3.   return x;
-;; 4. }
-;; 5. int g(int b) { 
-;; 6.   return f(b);
-;; 7. }
-;;
-;; Both functions contain 2 instructions in unique atom groups. In f we see
-;; groups 1 and 3, and in g we see {!18, 1} and 1. All of these instructions
-;; should get is_stmt.
-
 target triple = "x86_64-unknown-linux-gnu"
 
 define hidden noundef i32 @_Z1fi(i32 noundef %a) local_unnamed_addr !dbg !11 {



More information about the llvm-commits mailing list