[Mlir-commits] [llvm] [mlir] [MLIR][OpenMP] Give the target init/deinit calls a debug location. (PR #218458)
Jason Van Beusekom
llvmlistbot at llvm.org
Mon Aug 24 14:49:55 PDT 2026
https://github.com/Jason-Van-Beusekom updated https://github.com/llvm/llvm-project/pull/218458
>From 8c7e2cfd92b7ccf33397f85c435e2565583e3428 Mon Sep 17 00:00:00 2001
From: Abid Qadeer <haqadeer at amd.com>
Date: Mon, 24 Aug 2026 17:32:35 +0100
Subject: [PATCH 1/4] [MLIR][OpenMP] Give the target init/deinit calls a debug
location.
The __kmpc_target_init and __kmpc_target_deinit calls are emitted by
createOutlinedFunction, which resets the current debug location to empty
first because the location it inherits is scoped to the parent function
and would be in the wrong scope inside the newly created kernel. The
subprogram for the outlined function is only attached later, by the body
callback, so neither call ends up with a location.
That matters once a device runtime built with debug info is linked in:
the two runtime functions then have definitions with their own
DISubprogram, which makes these inlinable calls inside a function that
has debug info, and the verifier requires those to carry a !dbg. The
device link of a target region compiled with -g fails with
inlinable function call in a function with debug info must have a !dbg
location
convertOmpTarget already has the location these calls need. It saves
outlinedFnLoc, the location of the target construct scoped to the
subprogram that will be attached to the outlined function, before
re-scoping the builder to the parent function for the code it emits
there. Pass that down through createTarget so createOutlinedFunction can
install it instead of clearing the location, which gives everything
emitted into the outlined function a correct scope from the start.
The location cannot instead be carried in the LocationDescription that
createTarget already takes. That one is scoped to the parent function
and is what emitTargetCall uses for the host-side offloading code it
emits there, so re-scoping it to the kernel makes those instructions
fail the verifier with "!dbg attachment points at wrong subprogram for
function". Both locations are needed at once, so one of them has to be
passed separately.
The new parameter defaults to an empty location, so callers with no
subprogram to offer keep the current behaviour. MLIR's target lowering
is the only in-tree caller of createTarget; clang outlines target
regions through emitTargetRegionFunction with its own generate callback
and never reaches createOutlinedFunction, so the default only covers the
OMPIRBuilder unit tests.
Co-authored-by: Cursor <cursoragent at cursor.com>
---
.../llvm/Frontend/OpenMP/OMPIRBuilder.h | 8 +++-
llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp | 20 ++++++---
.../OpenMP/OpenMPToLLVMIRTranslation.cpp | 12 +++++-
.../omptarget-debug-runtime-call-loc.mlir | 43 +++++++++++++++++++
4 files changed, 74 insertions(+), 9 deletions(-)
create mode 100644 mlir/test/Target/LLVMIR/omptarget-debug-runtime-call-loc.mlir
diff --git a/llvm/include/llvm/Frontend/OpenMP/OMPIRBuilder.h b/llvm/include/llvm/Frontend/OpenMP/OMPIRBuilder.h
index d65924f2a8b3b..5f3da79cf53b1 100644
--- a/llvm/include/llvm/Frontend/OpenMP/OMPIRBuilder.h
+++ b/llvm/include/llvm/Frontend/OpenMP/OMPIRBuilder.h
@@ -3818,6 +3818,11 @@ class OpenMPIRBuilder {
/// cgroup.
/// \param DynCGroupMem The fallback mechanism to execute if the requested
/// cgroup memory cannot be provided.
+ /// \param OutlinedFnLoc Location scoped to the DISubprogram that the caller
+ /// will attach to the outlined function. \p Loc is scoped to the
+ /// parent function, so it cannot be used for code emitted inside the
+ /// outlined function. If this is empty, such code is emitted without a
+ /// debug location.
LLVM_ABI InsertPointOrErrorTy createTarget(
const LocationDescription &Loc, bool IsOffloadEntry,
OpenMPIRBuilder::InsertPointTy AllocaIP,
@@ -3833,7 +3838,8 @@ class OpenMPIRBuilder {
const DependenciesInfo &Dependencies = {}, bool HasNowait = false,
Value *DynCGroupMem = nullptr,
omp::OMPDynGroupprivateFallbackType DynCGroupMemFallback =
- omp::OMPDynGroupprivateFallbackType::Abort);
+ omp::OMPDynGroupprivateFallbackType::Abort,
+ DebugLoc OutlinedFnLoc = {});
/// Returns __kmpc_for_static_init_* runtime function for the specified
/// size \a IVSize and sign \a IVSigned. Will create a distribute call
diff --git a/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp b/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp
index 6f097fa205c34..d18a6e4158dd1 100644
--- a/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp
+++ b/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp
@@ -9224,7 +9224,8 @@ static Expected<Function *> createOutlinedFunction(
const OpenMPIRBuilder::TargetKernelDefaultAttrs &DefaultAttrs,
StringRef FuncName, SmallVectorImpl<Value *> &Inputs,
OpenMPIRBuilder::TargetBodyGenCallbackTy &CBFunc,
- OpenMPIRBuilder::TargetGenArgAccessorsCallbackTy &ArgAccessorFuncCB) {
+ OpenMPIRBuilder::TargetGenArgAccessorsCallbackTy &ArgAccessorFuncCB,
+ DebugLoc OutlinedFnLoc) {
SmallVector<Type *> ParameterTypes;
if (OMPBuilder.Config.isTargetDevice()) {
// All parameters to target devices are passed as pointers
@@ -9271,8 +9272,11 @@ static Expected<Function *> createOutlinedFunction(
// Save insert point.
IRBuilder<>::InsertPointGuard IPG(Builder);
// We will generate the entries in the outlined function but the debug
- // location may still be pointing to the parent function. Reset it now.
- Builder.SetCurrentDebugLocation(llvm::DebugLoc());
+ // location is still pointing to the parent function, which is the wrong
+ // scope. OutlinedFnLoc, when the caller provides one, is the same source
+ // position scoped to the subprogram that will be attached to the outlined
+ // function, so it is what everything emitted below needs.
+ Builder.SetCurrentDebugLocation(OutlinedFnLoc);
// Generate the region into the function.
BasicBlock *EntryBB = BasicBlock::Create(Builder.getContext(), "entry", Func);
@@ -9599,13 +9603,14 @@ static Error emitTargetOutlinedFunction(
Function *&OutlinedFn, Constant *&OutlinedFnID,
SmallVectorImpl<Value *> &Inputs,
OpenMPIRBuilder::TargetBodyGenCallbackTy &CBFunc,
- OpenMPIRBuilder::TargetGenArgAccessorsCallbackTy &ArgAccessorFuncCB) {
+ OpenMPIRBuilder::TargetGenArgAccessorsCallbackTy &ArgAccessorFuncCB,
+ DebugLoc OutlinedFnLoc) {
OpenMPIRBuilder::FunctionGenCallback &&GenerateOutlinedFunction =
[&](StringRef EntryFnName) {
return createOutlinedFunction(OMPBuilder, Builder, DefaultAttrs,
EntryFnName, Inputs, CBFunc,
- ArgAccessorFuncCB);
+ ArgAccessorFuncCB, OutlinedFnLoc);
};
return OMPBuilder.emitTargetRegionFunction(
@@ -10228,7 +10233,8 @@ OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::createTarget(
OpenMPIRBuilder::TargetGenArgAccessorsCallbackTy ArgAccessorFuncCB,
CustomMapperCallbackTy CustomMapperCB, const DependenciesInfo &Dependencies,
bool HasNowait, Value *DynCGroupMem,
- OMPDynGroupprivateFallbackType DynCGroupMemFallback) {
+ OMPDynGroupprivateFallbackType DynCGroupMemFallback,
+ DebugLoc OutlinedFnLoc) {
if (!updateToLocation(Loc))
return InsertPointTy();
@@ -10242,7 +10248,7 @@ OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::createTarget(
// and ArgAccessorFuncCB
if (Error Err = emitTargetOutlinedFunction(
*this, Builder, IsOffloadEntry, EntryInfo, DefaultAttrs, OutlinedFn,
- OutlinedFnID, Inputs, CBFunc, ArgAccessorFuncCB))
+ OutlinedFnID, Inputs, CBFunc, ArgAccessorFuncCB, OutlinedFnLoc))
return Err;
// If we are not on the target device, then we need to generate code
diff --git a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
index 47a8493708d0f..6d75c4e4a8a19 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
@@ -9296,6 +9296,16 @@ convertOmpTarget(Operation &opInst, llvm::IRBuilderBase &builder,
parentLLVMFn->getContext(), outlinedFnLoc.getLine(),
outlinedFnLoc.getCol(), SP, outlinedFnLoc.getInlinedAt()));
+ // OMPIRBuilder emits runtime calls into the outlined function before bodyCB
+ // below gets a chance to attach the subprogram to it, so it needs the
+ // outlined function's location handed to it separately. Only pass it under
+ // the same condition that decides whether the subprogram is attached at all:
+ // a location may not be attached to an instruction in a function that has no
+ // subprogram.
+ llvm::DebugLoc outlinedFnDbgLoc;
+ if (outlinedFnLoc && parentLLVMFn->getSubprogram())
+ outlinedFnDbgLoc = outlinedFnLoc;
+
llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder();
bool isTargetDevice = ompBuilder->Config.isTargetDevice();
bool isGPU = ompBuilder->Config.isGPU();
@@ -9695,7 +9705,7 @@ convertOmpTarget(Operation &opInst, llvm::IRBuilderBase &builder,
ompLoc, isOffloadEntry, allocaIP, builder.saveIP(), deallocBlocks,
info, entryInfo, defaultAttrs, runtimeAttrs, ifCond, kernelInput,
genMapInfoCB, bodyCB, argAccessorCB, customMapperCB, dds,
- targetOp.getNowait(), dynSizeVal, fallbackType);
+ targetOp.getNowait(), dynSizeVal, fallbackType, outlinedFnDbgLoc);
if (failed(handleError(afterIP, opInst)))
return failure();
diff --git a/mlir/test/Target/LLVMIR/omptarget-debug-runtime-call-loc.mlir b/mlir/test/Target/LLVMIR/omptarget-debug-runtime-call-loc.mlir
new file mode 100644
index 0000000000000..3baed4c9b36fb
--- /dev/null
+++ b/mlir/test/Target/LLVMIR/omptarget-debug-runtime-call-loc.mlir
@@ -0,0 +1,43 @@
+// RUN: mlir-translate -mlir-to-llvmir %s | FileCheck %s
+
+// The __kmpc_target_init and __kmpc_target_deinit calls are emitted into the
+// outlined kernel by OpenMPIRBuilder, which has no location of its own for
+// them: the translation's current location is scoped to the parent function at
+// that point. If they are left without a !dbg, the verifier rejects the module
+// once a device runtime that carries debug info is linked in, because they
+// become inlinable calls inside a function that has debug info. Check that both
+// get a location scoped to the outlined function's subprogram.
+
+module attributes {dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry<"dlti.alloca_memory_space", 5 : ui32>>, llvm.target_triple = "amdgcn-amd-amdhsa", omp.is_target_device = true} {
+ llvm.func @_QQmain() {
+ %0 = llvm.mlir.constant(1 : i32) : i32
+ %1 = llvm.alloca %0 x i32 : (i32) -> !llvm.ptr<5>
+ %ascast = llvm.addrspacecast %1 : !llvm.ptr<5> to !llvm.ptr
+ %9 = omp.map.info var_ptr(%ascast : !llvm.ptr, i32) map_clauses(tofrom) capture(ByRef) name("") -> !llvm.ptr
+ omp.target kernel_type(generic) map_entries(%9 -> %arg0 : !llvm.ptr) {
+ %13 = llvm.mlir.constant(1 : i32) : i32
+ llvm.store %13, %arg0 : i32, !llvm.ptr loc(#loc2)
+ omp.terminator
+ } loc(#loc4)
+ llvm.return
+ } loc(#loc3)
+}
+#file = #llvm.di_file<"target.f90" in "">
+#cu = #llvm.di_compile_unit<id = distinct[0]<>,
+ sourceLanguage = DW_LANG_Fortran95, file = #file, isOptimized = false,
+ emissionKind = LineTablesOnly>
+#sp_ty = #llvm.di_subroutine_type<callingConvention = DW_CC_normal>
+#sp = #llvm.di_subprogram<id = distinct[1]<>, compileUnit = #cu, scope = #file,
+ name = "_QQmain", file = #file, subprogramFlags = "Definition", type = #sp_ty>
+#sp1 = #llvm.di_subprogram<id = distinct[2]<>, compileUnit = #cu, scope = #file,
+ name = "__omp_offloading_target", file = #file, subprogramFlags = "Definition",
+ type = #sp_ty>
+#loc1 = loc("target.f90":12:5)
+#loc2 = loc("target.f90":46:3)
+#loc3 = loc(fused<#sp>[#loc1])
+#loc4 = loc(fused<#sp1>[#loc1])
+
+// CHECK: call i32 @__kmpc_target_init({{.*}}), !dbg ![[LOC:[0-9]+]]
+// CHECK: call void @__kmpc_target_deinit(), !dbg ![[LOC]]
+// CHECK-DAG: ![[SP:[0-9]+]] = distinct !DISubprogram(name: "__omp_offloading_target"
+// CHECK-DAG: ![[LOC]] = !DILocation(line: 12, column: 5, scope: ![[SP]])
>From 99e19defb2afc9d6c34b0f492fc7e32c3e6b2dc0 Mon Sep 17 00:00:00 2001
From: Jason Van Beusekom <jason.van-beusekom at hpe.com>
Date: Wed, 19 Aug 2026 12:27:56 -0500
Subject: [PATCH 2/4] [OpenMP] Set a debug location for
__kmpc_target_init/deinit calls
(cherry picked from commit 8553b39693bfb7d5bccb21edeb3479aad11294d9)
---
llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp | 20 +++++
.../Frontend/OpenMPIRBuilderTest.cpp | 79 +++++++++++++++++++
2 files changed, 99 insertions(+)
diff --git a/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp b/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp
index d18a6e4158dd1..edabadde9fd54 100644
--- a/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp
+++ b/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp
@@ -8692,6 +8692,26 @@ void OpenMPIRBuilder::createTargetDeinit(const LocationDescription &Loc,
if (!updateToLocation(Loc))
return;
+ // Ensure a debug location so the inlinable __kmpc_target_deinit call verifies
+ // inside a kernel that has debug info.
+ if (!Builder.getCurrentDebugLocation())
+ if (DISubprogram *SP =
+ Builder.GetInsertBlock()->getParent()->getSubprogram())
+ Builder.SetCurrentDebugLocation(
+ DILocation::get(M.getContext(), SP->getLine(), /*Column=*/0, SP));
+
+ // The matching __kmpc_target_init call is emitted by createTargetInit before
+ // the kernel's subprogram is attached, so it can be left without a debug
+ // location; fix it up here, where the subprogram is available.
+ if (DebugLoc DbgLoc = Builder.getCurrentDebugLocation()) {
+ Function *Kernel = Builder.GetInsertBlock()->getParent();
+ Function *InitFn = getOrCreateRuntimeFunctionPtr(
+ omp::RuntimeFunction::OMPRTL___kmpc_target_init);
+ for (Instruction &I : Kernel->getEntryBlock())
+ if (auto *CI = dyn_cast<CallInst>(&I))
+ if (CI->getCalledFunction() == InitFn && !CI->getDebugLoc())
+ CI->setDebugLoc(DbgLoc);
+ }
Function *Fn = getOrCreateRuntimeFunctionPtr(
omp::RuntimeFunction::OMPRTL___kmpc_target_deinit);
diff --git a/llvm/unittests/Frontend/OpenMPIRBuilderTest.cpp b/llvm/unittests/Frontend/OpenMPIRBuilderTest.cpp
index 5ecf397e24994..2aeec3327346c 100644
--- a/llvm/unittests/Frontend/OpenMPIRBuilderTest.cpp
+++ b/llvm/unittests/Frontend/OpenMPIRBuilderTest.cpp
@@ -6798,6 +6798,85 @@ TEST_F(OpenMPIRBuilderTest, TargetRegionDevice) {
cast<ConstantInt>(ExecModeValue)->getZExtValue());
}
+// When the enclosing kernel has debug info (-g offload), the inlinable
+// __kmpc_target_init/__kmpc_target_deinit calls must carry a !dbg location or
+// the verifier rejects the module during the device link.
+TEST_F(OpenMPIRBuilderTest, TargetInitDeinitDebugLoc) {
+ OpenMPIRBuilder OMPBuilder(*M);
+ OMPBuilder.setConfig(OpenMPIRBuilderConfig(
+ /*IsTargetDevice=*/true, false, false, false, false, false, false));
+ OMPBuilder.initialize();
+
+ FunctionType *KernelFTy =
+ FunctionType::get(Type::getVoidTy(Ctx), {PointerType::get(Ctx, 0)},
+ /*isVarArg=*/false);
+ Function *Kernel = Function::Create(KernelFTy, Function::WeakODRLinkage,
+ "__omp_offloading_test_kernel", M.get());
+ BasicBlock *KernelBB = BasicBlock::Create(Ctx, "entry", Kernel);
+
+ IRBuilder<> Builder(KernelBB);
+ // No debug location and, initially, no subprogram: the state in which flang
+ // emits the init call.
+ Builder.SetCurrentDebugLocation(DebugLoc());
+ OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DebugLoc()});
+
+ OpenMPIRBuilder::TargetKernelDefaultAttrs DefaultAttrs = {
+ /*ExecFlags=*/omp::OMPTgtExecModeFlags::OMP_TGT_EXEC_MODE_GENERIC,
+ /*MaxTeams=*/{-1}, /*MinTeams=*/0, /*MaxThreads=*/{0}, /*MinThreads=*/0};
+
+ OpenMPIRBuilder::InsertPointTy AfterIP =
+ OMPBuilder.createTargetInit(Loc, DefaultAttrs);
+
+ // The init call exists but has no !dbg yet, since the kernel had no
+ // subprogram when it was emitted.
+ CallInst *InitCall = nullptr;
+ for (Instruction &I : Kernel->getEntryBlock())
+ if (auto *CI = dyn_cast<CallInst>(&I))
+ if (CI->getCalledFunction()->getName() == "__kmpc_target_init")
+ InitCall = CI;
+ ASSERT_NE(InitCall, nullptr);
+ EXPECT_FALSE(InitCall->getDebugLoc());
+
+ // Attach a subprogram now, as the flang debug pass does after outlining.
+ DIBuilder DIB(*M);
+ DIFile *File = DIB.createFile("kernel.f90", "/");
+ DICompileUnit *CU = DIB.createCompileUnit(
+ DISourceLanguageName(dwarf::DW_LANG_C), File, "flang", true, "", 0);
+ DISubroutineType *SPTy =
+ DIB.createSubroutineType(DIB.getOrCreateTypeArray({}));
+ DISubprogram *SP = DIB.createFunction(
+ CU, "test_kernel", "", File, 10, SPTy, 10, DINode::FlagZero,
+ DISubprogram::SPFlagDefinition | DISubprogram::SPFlagOptimized);
+ Kernel->setSubprogram(SP);
+ DIB.finalize();
+
+ // Emit the matching deinit, again with no current debug location.
+ Builder.restoreIP(AfterIP);
+ Builder.SetCurrentDebugLocation(DebugLoc());
+ OpenMPIRBuilder::LocationDescription DeinitLoc(
+ {Builder.saveIP(), DebugLoc()});
+ OMPBuilder.createTargetDeinit(DeinitLoc);
+ Builder.CreateRetVoid();
+
+ CallInst *DeinitCall = nullptr;
+ for (BasicBlock &FnBB : *Kernel)
+ for (Instruction &I : FnBB)
+ if (auto *CI = dyn_cast<CallInst>(&I))
+ if (CI->getCalledFunction()->getName() == "__kmpc_target_deinit")
+ DeinitCall = CI;
+ ASSERT_NE(DeinitCall, nullptr);
+
+ // Both runtime calls now carry a !dbg location scoped to the kernel's
+ // subprogram: the init call was patched retroactively, the deinit call got
+ // the fallback location directly.
+ ASSERT_TRUE(InitCall->getDebugLoc());
+ EXPECT_EQ(InitCall->getDebugLoc()->getScope()->getSubprogram(), SP);
+ ASSERT_TRUE(DeinitCall->getDebugLoc());
+ EXPECT_EQ(DeinitCall->getDebugLoc()->getScope()->getSubprogram(), SP);
+
+ EXPECT_FALSE(verifyFunction(*Kernel, &errs()));
+}
+
TEST_F(OpenMPIRBuilderTest, TargetRegionSPMD) {
using InsertPointTy = OpenMPIRBuilder::InsertPointTy;
OpenMPIRBuilder OMPBuilder(*M);
>From 5bf905ae82442c33bbc3e0a3e00ffb05871f60e1 Mon Sep 17 00:00:00 2001
From: Jason Van Beusekom <jason.van-beusekom at hpe.com>
Date: Mon, 24 Aug 2026 16:48:58 -0500
Subject: [PATCH 3/4] Update test , remove orig implmentation
---
llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp | 20 ------
.../Frontend/OpenMPIRBuilderTest.cpp | 63 +++++++++----------
2 files changed, 30 insertions(+), 53 deletions(-)
diff --git a/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp b/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp
index edabadde9fd54..d18a6e4158dd1 100644
--- a/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp
+++ b/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp
@@ -8692,26 +8692,6 @@ void OpenMPIRBuilder::createTargetDeinit(const LocationDescription &Loc,
if (!updateToLocation(Loc))
return;
- // Ensure a debug location so the inlinable __kmpc_target_deinit call verifies
- // inside a kernel that has debug info.
- if (!Builder.getCurrentDebugLocation())
- if (DISubprogram *SP =
- Builder.GetInsertBlock()->getParent()->getSubprogram())
- Builder.SetCurrentDebugLocation(
- DILocation::get(M.getContext(), SP->getLine(), /*Column=*/0, SP));
-
- // The matching __kmpc_target_init call is emitted by createTargetInit before
- // the kernel's subprogram is attached, so it can be left without a debug
- // location; fix it up here, where the subprogram is available.
- if (DebugLoc DbgLoc = Builder.getCurrentDebugLocation()) {
- Function *Kernel = Builder.GetInsertBlock()->getParent();
- Function *InitFn = getOrCreateRuntimeFunctionPtr(
- omp::RuntimeFunction::OMPRTL___kmpc_target_init);
- for (Instruction &I : Kernel->getEntryBlock())
- if (auto *CI = dyn_cast<CallInst>(&I))
- if (CI->getCalledFunction() == InitFn && !CI->getDebugLoc())
- CI->setDebugLoc(DbgLoc);
- }
Function *Fn = getOrCreateRuntimeFunctionPtr(
omp::RuntimeFunction::OMPRTL___kmpc_target_deinit);
diff --git a/llvm/unittests/Frontend/OpenMPIRBuilderTest.cpp b/llvm/unittests/Frontend/OpenMPIRBuilderTest.cpp
index 2aeec3327346c..34f0f79b03421 100644
--- a/llvm/unittests/Frontend/OpenMPIRBuilderTest.cpp
+++ b/llvm/unittests/Frontend/OpenMPIRBuilderTest.cpp
@@ -6800,7 +6800,9 @@ TEST_F(OpenMPIRBuilderTest, TargetRegionDevice) {
// When the enclosing kernel has debug info (-g offload), the inlinable
// __kmpc_target_init/__kmpc_target_deinit calls must carry a !dbg location or
-// the verifier rejects the module during the device link.
+// the verifier rejects the module during the device link. createOutlinedFunction
+// installs the outlined function's location as the builder's current debug
+// location before emitting these calls, so both must inherit it.
TEST_F(OpenMPIRBuilderTest, TargetInitDeinitDebugLoc) {
OpenMPIRBuilder OMPBuilder(*M);
OMPBuilder.setConfig(OpenMPIRBuilderConfig(
@@ -6814,30 +6816,7 @@ TEST_F(OpenMPIRBuilderTest, TargetInitDeinitDebugLoc) {
"__omp_offloading_test_kernel", M.get());
BasicBlock *KernelBB = BasicBlock::Create(Ctx, "entry", Kernel);
- IRBuilder<> Builder(KernelBB);
- // No debug location and, initially, no subprogram: the state in which flang
- // emits the init call.
- Builder.SetCurrentDebugLocation(DebugLoc());
- OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DebugLoc()});
-
- OpenMPIRBuilder::TargetKernelDefaultAttrs DefaultAttrs = {
- /*ExecFlags=*/omp::OMPTgtExecModeFlags::OMP_TGT_EXEC_MODE_GENERIC,
- /*MaxTeams=*/{-1}, /*MinTeams=*/0, /*MaxThreads=*/{0}, /*MinThreads=*/0};
-
- OpenMPIRBuilder::InsertPointTy AfterIP =
- OMPBuilder.createTargetInit(Loc, DefaultAttrs);
-
- // The init call exists but has no !dbg yet, since the kernel had no
- // subprogram when it was emitted.
- CallInst *InitCall = nullptr;
- for (Instruction &I : Kernel->getEntryBlock())
- if (auto *CI = dyn_cast<CallInst>(&I))
- if (CI->getCalledFunction()->getName() == "__kmpc_target_init")
- InitCall = CI;
- ASSERT_NE(InitCall, nullptr);
- EXPECT_FALSE(InitCall->getDebugLoc());
-
- // Attach a subprogram now, as the flang debug pass does after outlining.
+ // Attach a subprogram to the kernel, as the outlining does.
DIBuilder DIB(*M);
DIFile *File = DIB.createFile("kernel.f90", "/");
DICompileUnit *CU = DIB.createCompileUnit(
@@ -6850,14 +6829,33 @@ TEST_F(OpenMPIRBuilderTest, TargetInitDeinitDebugLoc) {
Kernel->setSubprogram(SP);
DIB.finalize();
- // Emit the matching deinit, again with no current debug location.
+ IRBuilder<> Builder(KernelBB);
+ // The kernel-scoped location createOutlinedFunction installs from
+ // OutlinedFnLoc before emitting the runtime calls.
+ DebugLoc DL = DILocation::get(Ctx, 10, /*Column=*/0, SP);
+ Builder.SetCurrentDebugLocation(DL);
+
+ OpenMPIRBuilder::TargetKernelDefaultAttrs DefaultAttrs = {
+ /*ExecFlags=*/omp::OMPTgtExecModeFlags::OMP_TGT_EXEC_MODE_GENERIC,
+ /*MaxTeams=*/{-1}, /*MinTeams=*/{0}, /*MaxThreads=*/{0},
+ /*MinThreads=*/{0}};
+
+ // createTargetInit/createTargetDeinit capture the builder's current debug
+ // location, mirroring how createOutlinedFunction emits them.
+ OpenMPIRBuilder::InsertPointTy AfterIP =
+ OMPBuilder.createTargetInit(Builder, DefaultAttrs);
Builder.restoreIP(AfterIP);
- Builder.SetCurrentDebugLocation(DebugLoc());
- OpenMPIRBuilder::LocationDescription DeinitLoc(
- {Builder.saveIP(), DebugLoc()});
- OMPBuilder.createTargetDeinit(DeinitLoc);
+ Builder.SetCurrentDebugLocation(DL);
+ OMPBuilder.createTargetDeinit(Builder);
Builder.CreateRetVoid();
+ CallInst *InitCall = nullptr;
+ for (Instruction &I : Kernel->getEntryBlock())
+ if (auto *CI = dyn_cast<CallInst>(&I))
+ if (CI->getCalledFunction()->getName() == "__kmpc_target_init")
+ InitCall = CI;
+ ASSERT_NE(InitCall, nullptr);
+
CallInst *DeinitCall = nullptr;
for (BasicBlock &FnBB : *Kernel)
for (Instruction &I : FnBB)
@@ -6866,9 +6864,8 @@ TEST_F(OpenMPIRBuilderTest, TargetInitDeinitDebugLoc) {
DeinitCall = CI;
ASSERT_NE(DeinitCall, nullptr);
- // Both runtime calls now carry a !dbg location scoped to the kernel's
- // subprogram: the init call was patched retroactively, the deinit call got
- // the fallback location directly.
+ // Both runtime calls carry a !dbg location scoped to the kernel's subprogram,
+ // so the kernel verifies.
ASSERT_TRUE(InitCall->getDebugLoc());
EXPECT_EQ(InitCall->getDebugLoc()->getScope()->getSubprogram(), SP);
ASSERT_TRUE(DeinitCall->getDebugLoc());
>From 6601c06941bc9d6a387b3cf4e8ff14c3bdb9b4bf Mon Sep 17 00:00:00 2001
From: Jason Van Beusekom <jason.van-beusekom at hpe.com>
Date: Mon, 24 Aug 2026 16:49:39 -0500
Subject: [PATCH 4/4] format
---
llvm/unittests/Frontend/OpenMPIRBuilderTest.cpp | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/llvm/unittests/Frontend/OpenMPIRBuilderTest.cpp b/llvm/unittests/Frontend/OpenMPIRBuilderTest.cpp
index 34f0f79b03421..31c6956e6e8d2 100644
--- a/llvm/unittests/Frontend/OpenMPIRBuilderTest.cpp
+++ b/llvm/unittests/Frontend/OpenMPIRBuilderTest.cpp
@@ -6800,9 +6800,10 @@ TEST_F(OpenMPIRBuilderTest, TargetRegionDevice) {
// When the enclosing kernel has debug info (-g offload), the inlinable
// __kmpc_target_init/__kmpc_target_deinit calls must carry a !dbg location or
-// the verifier rejects the module during the device link. createOutlinedFunction
-// installs the outlined function's location as the builder's current debug
-// location before emitting these calls, so both must inherit it.
+// the verifier rejects the module during the device link.
+// createOutlinedFunction installs the outlined function's location as the
+// builder's current debug location before emitting these calls, so both must
+// inherit it.
TEST_F(OpenMPIRBuilderTest, TargetInitDeinitDebugLoc) {
OpenMPIRBuilder OMPBuilder(*M);
OMPBuilder.setConfig(OpenMPIRBuilderConfig(
More information about the Mlir-commits
mailing list