[flang-commits] [flang] [llvm] [OpenMPIRBuilder] Clear the debug location when emitting reduction helpers (PR #211395)

Spencer Bryngelson via flang-commits flang-commits at lists.llvm.org
Thu Jul 23 06:40:25 PDT 2026


https://github.com/sbryngelson updated https://github.com/llvm/llvm-project/pull/211395

>From 0197611147f026b594b2c46e9f700c026121786d Mon Sep 17 00:00:00 2001
From: Spencer Bryngelson <sbryngelson at gmail.com>
Date: Wed, 22 Jul 2026 17:23:23 -0500
Subject: [PATCH] [OpenMPIRBuilder] Clear the debug location when emitting
 reduction helpers

The device reduction helpers `_omp_reduction_shuffle_and_reduce_func` and
`_omp_reduction_inter_warp_copy_func` are created with no debug info of their
own, but the builder's current debug location is left set while their bodies
are emitted. Their instructions end up carrying `DILocation`s scoped to the
enclosing kernel's subprogram.

The verifier's subprogram check only applies to a function that has a
`DISubprogram`, so the helpers are never flagged themselves. The mismatch
becomes visible when the inliner folds a helper into a function that does have
one, at which point the module fails verification with "!dbg attachment points
at wrong subprogram for function" during the device LTO link. That is why the
pre-link IR is clean at every optimization level.

Clear the debug location after switching the insert point into each helper,
using `InsertPointGuard` so the location is restored for the caller.
`restoreIP` does not restore the debug location, so a bare
`SetCurrentDebugLocation(DebugLoc())` would leak the cleared location out.

The two `kmpc_barrier` calls inside `emitInterWarpCopyFunction` also have to
stop forwarding `Loc.DL`. They pass the caller's location explicitly, which
re-establishes it part way through the helper and leaves everything after it
scoped to the kernel again.

Fixes #211385.
---
 .../OpenMP/target-reduction-debug-loc.f90     | 43 +++++++++++++++++++
 llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp     | 16 +++++--
 2 files changed, 55 insertions(+), 4 deletions(-)
 create mode 100644 flang/test/Integration/OpenMP/target-reduction-debug-loc.f90

diff --git a/flang/test/Integration/OpenMP/target-reduction-debug-loc.f90 b/flang/test/Integration/OpenMP/target-reduction-debug-loc.f90
new file mode 100644
index 0000000000000..599bf5900032a
--- /dev/null
+++ b/flang/test/Integration/OpenMP/target-reduction-debug-loc.f90
@@ -0,0 +1,43 @@
+!===----------------------------------------------------------------------===!
+! This directory can be used to add Integration tests involving multiple
+! stages of the compiler (for eg. from Fortran to LLVM IR). It should not
+! contain executable tests. We should only add tests here sparingly and only
+! if there is no other way to test. Repeat this message in each test that is
+! added to this directory and sub-directories.
+!===----------------------------------------------------------------------===!
+
+! The device reduction helpers emitted by OpenMPIRBuilder have no debug info of
+! their own. If the builder's current debug location is left set while they are
+! emitted, their instructions carry locations scoped to the enclosing kernel's
+! subprogram. That is latent until the inliner folds a helper into a function
+! that does have a subprogram, at which point the module fails verification with
+! "!dbg attachment points at wrong subprogram for function".
+
+! REQUIRES: amdgpu-registered-target
+
+! RUN: %flang_fc1 -emit-llvm -fopenmp -fopenmp-is-target-device \
+! RUN:   -triple amdgcn-amd-amdhsa -debug-info-kind=standalone -O0 \
+! RUN:   -o - %s | FileCheck %s
+
+subroutine k(a, n, s)
+  real(8), intent(in)  :: a(*)
+  integer, intent(in)  :: n
+  real(8), intent(out) :: s
+  integer :: i
+  s = 0
+  !$omp target teams distribute parallel do reduction(+:s)
+  do i = 1, n
+     s = s + a(i)
+  end do
+end subroutine
+
+! The helpers must be emitted without debug locations, so no !dbg appears
+! between their definition and the closing brace.
+
+! CHECK-LABEL: define internal void @_omp_reduction_shuffle_and_reduce_func
+! CHECK-NOT:     !dbg
+! CHECK:       }
+
+! CHECK-LABEL: define internal void @_omp_reduction_inter_warp_copy_func
+! CHECK-NOT:     !dbg
+! CHECK:       }
diff --git a/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp b/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp
index e19ed110adf1e..ddcea63c62684 100644
--- a/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp
+++ b/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp
@@ -3526,7 +3526,8 @@ Error OpenMPIRBuilder::emitReductionListCopy(
 Expected<Function *> OpenMPIRBuilder::emitInterWarpCopyFunction(
     const LocationDescription &Loc, ArrayRef<ReductionInfo> ReductionInfos,
     AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
-  InsertPointTy SavedIP = Builder.saveIP();
+  // Restores the insertion point and the debug location on the way out.
+  IRBuilder<>::InsertPointGuard IPG(Builder);
   LLVMContext &Ctx = M.getContext();
   FunctionType *FuncTy = FunctionType::get(
       Builder.getVoidTy(), {Builder.getPtrTy(), Builder.getInt32Ty()},
@@ -3540,6 +3541,9 @@ Expected<Function *> OpenMPIRBuilder::emitInterWarpCopyFunction(
   WcFunc->addParamAttr(1, Attribute::NoUndef);
   BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", WcFunc);
   Builder.SetInsertPoint(EntryBB);
+  // The helper has no debug info of its own; a location left over from the
+  // caller would be scoped to the caller's subprogram.
+  Builder.SetCurrentDebugLocation(DebugLoc());
 
   // ReduceList: thread local Reduce list.
   // At the stage of the computation when this function is called, partially
@@ -3646,7 +3650,7 @@ Expected<Function *> OpenMPIRBuilder::emitInterWarpCopyFunction(
 
       // kmpc_barrier.
       InsertPointOrErrorTy BarrierIP1 =
-          createBarrier(LocationDescription(Builder.saveIP(), Loc.DL),
+          createBarrier(LocationDescription(Builder.saveIP(), DebugLoc()),
                         omp::Directive::OMPD_unknown,
                         /* ForceSimpleCall */ false,
                         /* CheckCancelFlag */ true);
@@ -3705,7 +3709,7 @@ Expected<Function *> OpenMPIRBuilder::emitInterWarpCopyFunction(
       // endif
       emitBlock(MergeBB, Builder.GetInsertBlock()->getParent());
       InsertPointOrErrorTy BarrierIP2 =
-          createBarrier(LocationDescription(Builder.saveIP(), Loc.DL),
+          createBarrier(LocationDescription(Builder.saveIP(), DebugLoc()),
                         omp::Directive::OMPD_unknown,
                         /* ForceSimpleCall */ false,
                         /* CheckCancelFlag */ true);
@@ -3778,7 +3782,6 @@ Expected<Function *> OpenMPIRBuilder::emitInterWarpCopyFunction(
   }
 
   Builder.CreateRetVoid();
-  Builder.restoreIP(SavedIP);
 
   return WcFunc;
 }
@@ -3786,6 +3789,8 @@ Expected<Function *> OpenMPIRBuilder::emitInterWarpCopyFunction(
 Expected<Function *> OpenMPIRBuilder::emitShuffleAndReduceFunction(
     ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,
     AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
+  // Restores the insertion point and the debug location on the way out.
+  IRBuilder<>::InsertPointGuard IPG(Builder);
   LLVMContext &Ctx = M.getContext();
   FunctionType *FuncTy =
       FunctionType::get(Builder.getVoidTy(),
@@ -3806,6 +3811,9 @@ Expected<Function *> OpenMPIRBuilder::emitShuffleAndReduceFunction(
   SarFunc->addParamAttr(3, Attribute::SExt);
   BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", SarFunc);
   Builder.SetInsertPoint(EntryBB);
+  // The helper has no debug info of its own; a location left over from the
+  // caller would be scoped to the caller's subprogram.
+  Builder.SetCurrentDebugLocation(DebugLoc());
 
   // Thread local Reduce list used to host the values of data to be reduced.
   Argument *ReduceListArg = SarFunc->getArg(0);



More information about the flang-commits mailing list