[llvm] [OpenMPOpt] Preserve debug locations when emitting barriers. (PR #220554)
Abid Qadeer via llvm-commits
llvm-commits at lists.llvm.org
Wed Sep 2 04:05:52 PDT 2026
https://github.com/abidh created https://github.com/llvm/llvm-project/pull/220554
One of the OpenMPIRBuilder::LocationDescription constructors takes an insertion point alone and leaves the debug location default-constructed. It is not explicit, so passing a bare insertion point to a builder entry point converts through it silently and everything the callee emits comes out without a !dbg.
That becomes a hard error once a runtime built with debug info is linked in, because the verifier requires an inlinable call inside a function with debug info to have a location. Otherwise the link fails with
inlinable function call in a function with debug info must have a !dbg location
%omp_global_thread_num1 = call i32 @__kmpc_global_thread_num(ptr @4)
inlinable function call in a function with debug info must have a !dbg location
call void @__kmpc_barrier(ptr @3, i32 %omp_global_thread_num1)
error: linked module is broken!
The same module links clean against a runtime without debug info, which is why this stays latent until someone builds one with it.
This is part of an ongoing effort to remove those accidental drops, after https://github.com/llvm/llvm-project/pull/211254 for the MLIR translation layer and https://github.com/llvm/llvm-project/pull/218961 for OpenMPIRBuilder.
Three of the barriers here were emitted through a bare insertion point, so the __kmpc_barrier calls came out with no !dbg even though a suitable location was sitting right there in each case.
The remaining case in createIdent is left explicitly empty as the insertion point is only set so the builder can reach the module, according to the comment above it.
That comment is out of date though: the module is passed explicitly now, so nothing there needs the insertion point. The workaround is dead code and will be removed in a separate commit.
>From aebc6c4dac7003372e7d415119d043070229c8cb Mon Sep 17 00:00:00 2001
From: Abid Qadeer <haqadeer at amd.com>
Date: Wed, 2 Sep 2026 11:11:41 +0100
Subject: [PATCH] [OpenMPOpt] Preserve debug locations when emitting barriers.
One of the OpenMPIRBuilder::LocationDescription constructors takes an insertion
point alone and leaves the debug location default-constructed. It is not
explicit, so passing a bare insertion point to a builder entry point converts
through it silently and everything the callee emits comes out without a !dbg.
That becomes a hard error once a runtime built with debug info is linked in,
because the verifier requires an inlinable call inside a function with debug
info to have a location. Otherwise the link fails with
inlinable function call in a function with debug info must have a !dbg location
%omp_global_thread_num1 = call i32 @__kmpc_global_thread_num(ptr @4)
inlinable function call in a function with debug info must have a !dbg location
call void @__kmpc_barrier(ptr @3, i32 %omp_global_thread_num1)
error: linked module is broken!
The same module links clean against a runtime without debug info, which is why
this stays latent until someone builds one with it.
This is part of an ongoing effort to remove those accidental drops, after
https://github.com/llvm/llvm-project/pull/211254 for the MLIR translation layer
and https://github.com/llvm/llvm-project/pull/218961 for OpenMPIRBuilder, both
of which are merged.
Three of the barriers here were emitted through a bare insertion point, so the
__kmpc_barrier calls came out with no !dbg even though a suitable location was
sitting right there in each case.
The remaining case in createIdent is left explicitly empty as the
insertion point is only set so the builder can reach the module, according to
the comment above it.
That comment is out of date though: the module is passed explicitly now, so
nothing there needs the insertion point. The workaround is dead code and will
be removed in a separate commit.
Co-authored-by: Cursor <cursoragent at cursor.com>
---
llvm/lib/Transforms/IPO/OpenMPOpt.cpp | 25 +++---
.../parallel_region_merging_debug_loc.ll | 82 +++++++++++++++++++
2 files changed, 97 insertions(+), 10 deletions(-)
create mode 100644 llvm/test/Transforms/OpenMP/parallel_region_merging_debug_loc.ll
diff --git a/llvm/lib/Transforms/IPO/OpenMPOpt.cpp b/llvm/lib/Transforms/IPO/OpenMPOpt.cpp
index 22a5efb402504..f02a41eeb3c6b 100644
--- a/llvm/lib/Transforms/IPO/OpenMPOpt.cpp
+++ b/llvm/lib/Transforms/IPO/OpenMPOpt.cpp
@@ -1199,8 +1199,8 @@ struct OpenMPOpt {
InsertPointTy(ParentBB, ParentBB->end()), DL);
OpenMPIRBuilder::InsertPointTy SeqAfterIP = cantFail(
OMPInfoCache.OMPBuilder.createMaster(Loc, BodyGenCB, FiniCB));
- cantFail(
- OMPInfoCache.OMPBuilder.createBarrier(SeqAfterIP, OMPD_parallel));
+ cantFail(OMPInfoCache.OMPBuilder.createBarrier({SeqAfterIP, DL},
+ OMPD_parallel));
UncondBrInst::Create(SeqAfterBB, SeqAfterIP.getBlock());
@@ -1314,8 +1314,9 @@ struct OpenMPOpt {
// TODO: Remove barrier if the merged parallel region includes the
// 'nowait' clause.
cantFail(OMPInfoCache.OMPBuilder.createBarrier(
- InsertPointTy(NewCI->getParent(),
- NewCI->getNextNode()->getIterator()),
+ {InsertPointTy(NewCI->getParent(),
+ NewCI->getNextNode()->getIterator()),
+ NewCI->getDebugLoc()},
OMPD_parallel));
}
@@ -1819,10 +1820,13 @@ struct OpenMPOpt {
if (!Ident || !SingleChoice) {
// The IRBuilder uses the insertion block to get to the module, this is
- // unfortunate but we work around it for now.
+ // unfortunate but we work around it for now. No instruction is emitted
+ // here, so there is no debug location to preserve.
if (!OMPInfoCache.OMPBuilder.getInsertionPoint().getBlock())
- OMPInfoCache.OMPBuilder.updateToLocation(OpenMPIRBuilder::InsertPointTy(
- &F.getEntryBlock(), F.getEntryBlock().begin()));
+ OMPInfoCache.OMPBuilder.updateToLocation(
+ {OpenMPIRBuilder::InsertPointTy(&F.getEntryBlock(),
+ F.getEntryBlock().begin()),
+ DebugLoc()});
// Create a fallback location if non was found.
// TODO: Use the debug locations of the calls instead.
uint32_t SrcLocStrSize;
@@ -4150,11 +4154,12 @@ struct AAKernelInfoFunction : AAKernelInfo {
FunctionCallee BarrierFn =
OMPInfoCache.OMPBuilder.getOrCreateRuntimeFunction(
M, OMPRTL___kmpc_barrier_simple_spmd);
- OMPInfoCache.OMPBuilder.updateToLocation(InsertPointTy(
- RegionBarrierBB, RegionBarrierBB->getFirstInsertionPt()));
+ OMPInfoCache.OMPBuilder.updateToLocation(
+ {InsertPointTy(RegionBarrierBB,
+ RegionBarrierBB->getFirstInsertionPt()),
+ DL});
CallInst *Barrier =
OMPInfoCache.OMPBuilder.Builder.CreateCall(BarrierFn, {Ident, Tid});
- Barrier->setDebugLoc(DL);
OMPInfoCache.setCallingConvention(BarrierFn, Barrier);
// Second barrier ensures workers have read broadcast values.
diff --git a/llvm/test/Transforms/OpenMP/parallel_region_merging_debug_loc.ll b/llvm/test/Transforms/OpenMP/parallel_region_merging_debug_loc.ll
new file mode 100644
index 0000000000000..15776176acaca
--- /dev/null
+++ b/llvm/test/Transforms/OpenMP/parallel_region_merging_debug_loc.ll
@@ -0,0 +1,82 @@
+; RUN: opt -S -aa-pipeline= -passes='attributor,cgscc(openmp-opt-cgscc)' -openmp-opt-enable-merging < %s | FileCheck %s
+
+; void merge_seq(int a) {
+; #pragma omp parallel // line 13
+; use(a);
+; ++a; // line 16, sequentialized
+; #pragma omp parallel // line 17
+; use(a);
+; use(a); // line 20, after both regions
+; }
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+
+%struct.ident_t = type { i32, i32, i32, i32, ptr }
+
+ at 0 = private unnamed_addr constant [23 x i8] c";unknown;unknown;0;0;;\00", align 1
+ at 1 = private unnamed_addr constant %struct.ident_t { i32 0, i32 2, i32 0, i32 0, ptr @0 }, align 8
+
+; CHECK-LABEL: define internal void @merge_seq..omp_par
+; CHECK: omp.par.merged:
+; CHECK-NEXT: call void (ptr, ptr, ...) @.omp_outlined.(
+; CHECK-NEXT: call i32 @__kmpc_global_thread_num(ptr {{.*}}), !dbg
+; CHECK-NEXT: call void @__kmpc_barrier(ptr {{.*}}), !dbg
+; CHECK: omp_region.end:
+; CHECK-NEXT: call i32 @__kmpc_global_thread_num(ptr {{.*}}), !dbg
+; CHECK-NEXT: call void @__kmpc_barrier(ptr {{.*}}), !dbg
+
+define dso_local void @merge_seq(i32 %a) local_unnamed_addr !dbg !9 {
+entry:
+ %a.addr = alloca i32, align 4
+ store i32 %a, ptr %a.addr, align 4, !dbg !11
+ call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr nonnull @1, i32 1, ptr @.omp_outlined., ptr nonnull %a.addr), !dbg !12
+ %0 = load i32, ptr %a.addr, align 4, !dbg !13
+ %add = add nsw i32 %0, 1, !dbg !13
+ store i32 %add, ptr %a.addr, align 4, !dbg !13
+ call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr nonnull @1, i32 1, ptr @.omp_outlined..1, ptr nonnull %a.addr), !dbg !14
+ %1 = load i32, ptr %a.addr, align 4, !dbg !15
+ call void @use(i32 %1), !dbg !15
+ ret void, !dbg !15
+}
+
+define internal void @.omp_outlined.(ptr noalias nocapture readnone %.global_tid., ptr noalias nocapture readnone %.bound_tid., ptr nocapture nonnull readonly align 4 dereferenceable(4) %a) !dbg !16 {
+entry:
+ %0 = load i32, ptr %a, align 4, !dbg !17
+ call void @use(i32 %0), !dbg !17
+ ret void, !dbg !17
+}
+
+define internal void @.omp_outlined..1(ptr noalias nocapture readnone %.global_tid., ptr noalias nocapture readnone %.bound_tid., ptr nocapture nonnull readonly align 4 dereferenceable(4) %a) !dbg !18 {
+entry:
+ %0 = load i32, ptr %a, align 4, !dbg !19
+ call void @use(i32 %0), !dbg !19
+ ret void, !dbg !19
+}
+
+declare dso_local void @use(i32) local_unnamed_addr
+
+declare !callback !1 void @__kmpc_fork_call(ptr, i32, ptr, ...) local_unnamed_addr
+
+!llvm.module.flags = !{!0, !3, !4, !5}
+!llvm.dbg.cu = !{!6}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{!2}
+!2 = !{i64 2, i64 -1, i64 -1, i1 true}
+!3 = !{i32 7, !"openmp", i32 50}
+!4 = !{i32 7, !"Debug Info Version", i32 3}
+!5 = !{i32 2, !"Dwarf Version", i32 5}
+!6 = distinct !DICompileUnit(language: DW_LANG_C11, file: !7, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
+!7 = !DIFile(filename: "merge.c", directory: "/tmp")
+!8 = !DISubroutineType(types: !{null})
+!9 = distinct !DISubprogram(name: "merge_seq", scope: !7, file: !7, line: 12, type: !8, scopeLine: 12, spFlags: DISPFlagDefinition, unit: !6, retainedNodes: !10)
+!10 = !{}
+!11 = !DILocation(line: 12, column: 1, scope: !9)
+!12 = !DILocation(line: 13, column: 1, scope: !9)
+!13 = !DILocation(line: 16, column: 1, scope: !9)
+!14 = !DILocation(line: 17, column: 1, scope: !9)
+!15 = !DILocation(line: 20, column: 1, scope: !9)
+!16 = distinct !DISubprogram(name: "outlined_1", scope: !7, file: !7, line: 13, type: !8, scopeLine: 13, spFlags: DISPFlagDefinition, unit: !6, retainedNodes: !10)
+!17 = !DILocation(line: 14, column: 1, scope: !16)
+!18 = distinct !DISubprogram(name: "outlined_2", scope: !7, file: !7, line: 17, type: !8, scopeLine: 17, spFlags: DISPFlagDefinition, unit: !6, retainedNodes: !10)
+!19 = !DILocation(line: 18, column: 1, scope: !18)
More information about the llvm-commits
mailing list