[llvm] Merge debug locations when combining stores (PR #225358)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Sep 22 03:29:42 PDT 2026
https://github.com/anatasluo created https://github.com/llvm/llvm-project/pull/225358
Constant and extracted-vector-element store merging currently inherits only the first store's location. For stores from different inline instances, this can attribute the whole memory access to the wrong inline frame.
Merge the locations of the stores actually combined, using the existing DebugLoc policy while preserving SDLoc's IR order. Add regression coverage for sibling scopes, identical and missing locations, and partially merged candidate groups.
Assisted-by: OpenAI Codex
>From 791e90cce7e77deed4a8420c7cb8028e0da3080e Mon Sep 17 00:00:00 2001
From: Longjun Luo <luolongjuna at gmail.com>
Date: Tue, 22 Sep 2026 09:47:59 +0000
Subject: [PATCH] Merge debug locations when combining stores
Constant and extracted-vector-element store merging currently inherits
only the first store's location. For stores from different inline
instances, this can attribute the whole memory access to the wrong
inline frame.
Merge the locations of the stores actually combined, using the existing
DebugLoc policy while preserving SDLoc's IR order. Add regression coverage
for sibling scopes, identical and missing locations, and partially merged
candidate groups.
Assisted-by: OpenAI Codex
---
llvm/include/llvm/CodeGen/SelectionDAGNodes.h | 5 ++
llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp | 3 +-
.../X86/sdag-merge-stores-debugloc.ll | 80 +++++++++++++++++++
3 files changed, 87 insertions(+), 1 deletion(-)
create mode 100644 llvm/test/DebugInfo/X86/sdag-merge-stores-debugloc.ll
diff --git a/llvm/include/llvm/CodeGen/SelectionDAGNodes.h b/llvm/include/llvm/CodeGen/SelectionDAGNodes.h
index 667c98f086827a..fede733a5ffcdc 100644
--- a/llvm/include/llvm/CodeGen/SelectionDAGNodes.h
+++ b/llvm/include/llvm/CodeGen/SelectionDAGNodes.h
@@ -1266,6 +1266,11 @@ class SDLoc {
unsigned getIROrder() const { return IROrder; }
const DebugLoc &getDebugLoc() const { return DL; }
+
+ /// Merge the source location without changing the IR order.
+ void mergeDebugLoc(const DebugLoc &Other) {
+ DL = DebugLoc::getMergedLocation(DL, Other);
+ }
};
// Define inline functions from the SDValue class.
diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index 17e2ab01bc11fb..fa093565c0dbdb 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -23256,7 +23256,7 @@ bool DAGCombiner::mergeStoresOfConstantsOrVecElts(
assert((!UseTrunc || !UseVector) &&
"This optimization cannot emit a vector truncating store");
- // The latest Node in the DAG.
+ // Preserve the first store's IR order, but merge the source locations.
SDLoc DL(StoreNodes[0].MemNode);
TypeSize ElementSizeBits = MemVT.getStoreSizeInBits();
@@ -23275,6 +23275,7 @@ bool DAGCombiner::mergeStoresOfConstantsOrVecElts(
// Skip merging if there's an inconsistent flag.
if (Flags != St->getMemOperand()->getFlags())
return false;
+ DL.mergeDebugLoc(St->getDebugLoc());
// Concatenate AA metadata.
AAInfo = AAInfo.concat(St->getAAInfo());
}
diff --git a/llvm/test/DebugInfo/X86/sdag-merge-stores-debugloc.ll b/llvm/test/DebugInfo/X86/sdag-merge-stores-debugloc.ll
new file mode 100644
index 00000000000000..9db28a984f46a3
--- /dev/null
+++ b/llvm/test/DebugInfo/X86/sdag-merge-stores-debugloc.ll
@@ -0,0 +1,80 @@
+; RUN: llc -O2 -mtriple=x86_64-unknown-linux-gnu -stop-after=finalize-isel -o - %s | FileCheck %s
+
+;; Consecutive stores combined into a single store must merge their locations,
+;; rather than attributing all of the memory effects to the first inline scope.
+
+; CHECK-DAG: ![[CALLER:[0-9]+]] = distinct !DISubprogram(name: "siblings",
+; CHECK-DAG: ![[FIRST:[0-9]+]] = distinct !DISubprogram(name: "clear_first",
+; CHECK-DAG: ![[SAME_CALL:[0-9]+]] = !DILocation(line: 21, column: 3, scope: ![[#]])
+; CHECK-DAG: ![[SAME:[0-9]+]] = !DILocation(line: 4, column: 3, scope: ![[FIRST]], inlinedAt: ![[SAME_CALL]])
+; CHECK-DAG: ![[PARTIAL_CALL:[0-9]+]] = !DILocation(line: 41, column: 3, scope: ![[#]])
+; CHECK-DAG: ![[PARTIAL:[0-9]+]] = !DILocation(line: 4, column: 3, scope: ![[FIRST]], inlinedAt: ![[PARTIAL_CALL]])
+
+; CHECK-LABEL: name: siblings
+; CHECK: MOV64mi32 {{.*}}, debug-location !DILocation(line: 0, scope: ![[CALLER]]) :: (store (s64)
+define void @siblings(ptr %p) !dbg !10 {
+ store i32 0, ptr %p, align 4, !dbg !13
+ %q = getelementptr i32, ptr %p, i64 1
+ store i32 0, ptr %q, align 4, !dbg !14
+ ret void
+}
+
+;; Identical locations retain their line and inline scope.
+; CHECK-LABEL: name: same_location
+; CHECK: MOV64mi32 {{.*}}, debug-location ![[SAME]] :: (store (s64)
+define void @same_location(ptr %p) !dbg !20 {
+ store i32 0, ptr %p, align 4, !dbg !22
+ %q = getelementptr i32, ptr %p, i64 1
+ store i32 0, ptr %q, align 4, !dbg !22
+ ret void
+}
+
+;; Do not retain one store's scope when the other's location is unknown.
+; CHECK-LABEL: name: missing_location
+; CHECK: MOV64mi32 {{.*}}, 0 :: (store (s64)
+define void @missing_location(ptr %p) !dbg !30 {
+ store i32 0, ptr %p, align 4, !dbg !32
+ %q = getelementptr i32, ptr %p, i64 1
+ store i32 0, ptr %q, align 4
+ ret void
+}
+
+;; Only the first two candidates are merged. Do not include the third store's
+;; location when computing the wider store's location.
+; CHECK-LABEL: name: partial_merge
+; CHECK: MOV64mi32 {{.*}}, debug-location ![[PARTIAL]] :: (store (s64)
+; CHECK: MOV32mi
+define void @partial_merge(ptr %p) !dbg !40 {
+ store i32 0, ptr %p, align 4, !dbg !43
+ %q = getelementptr i32, ptr %p, i64 1
+ store i32 0, ptr %q, align 4, !dbg !43
+ %r = getelementptr i32, ptr %p, i64 2
+ store i32 0, ptr %r, align 4, !dbg !44
+ ret void
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!3}
+!0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "test", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
+!1 = !DIFile(filename: "store-merge.c", directory: "/")
+!2 = !DISubroutineType(types: !4)
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = !{}
+!5 = distinct !DISubprogram(name: "clear_first", scope: !1, file: !1, line: 3, type: !2, scopeLine: 3, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0)
+!6 = distinct !DISubprogram(name: "clear_second", scope: !1, file: !1, line: 6, type: !2, scopeLine: 6, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0)
+!10 = distinct !DISubprogram(name: "siblings", scope: !1, file: !1, line: 10, type: !2, scopeLine: 10, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0)
+!11 = !DILocation(line: 11, column: 3, scope: !10)
+!12 = !DILocation(line: 12, column: 3, scope: !10)
+!13 = !DILocation(line: 4, column: 3, scope: !5, inlinedAt: !11)
+!14 = !DILocation(line: 7, column: 3, scope: !6, inlinedAt: !12)
+!20 = distinct !DISubprogram(name: "same_location", scope: !1, file: !1, line: 20, type: !2, scopeLine: 20, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0)
+!21 = !DILocation(line: 21, column: 3, scope: !20)
+!22 = !DILocation(line: 4, column: 3, scope: !5, inlinedAt: !21)
+!30 = distinct !DISubprogram(name: "missing_location", scope: !1, file: !1, line: 30, type: !2, scopeLine: 30, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0)
+!31 = !DILocation(line: 31, column: 3, scope: !30)
+!32 = !DILocation(line: 4, column: 3, scope: !5, inlinedAt: !31)
+!40 = distinct !DISubprogram(name: "partial_merge", scope: !1, file: !1, line: 40, type: !2, scopeLine: 40, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0)
+!41 = !DILocation(line: 41, column: 3, scope: !40)
+!42 = !DILocation(line: 42, column: 3, scope: !40)
+!43 = !DILocation(line: 4, column: 3, scope: !5, inlinedAt: !41)
+!44 = !DILocation(line: 7, column: 3, scope: !6, inlinedAt: !42)
More information about the llvm-commits
mailing list