Skip to content

Commit 196a1ac

Browse files
authored
[OMPIRBuilder][debug] Fix debug info for variables in target region. (#118314)
When a new function is created to handle OpenMP target region, the variables used in it are passed as arguments. The scope and the location of these variable contains still point to te parent function of the target region. Such variables will fail in Verifier as the scope of the variables will be different from the containing functions. Currently, flang is the only user of createOutlinedFunction and it does not generate any debug data for the the variables in the target region to avoid this error. When this PR is in, we should be able to remove this limit in the flang (and anyother client) and have the better debug experience for the target region. This PR changes the location and scope of the variables in the target region to point to correct entities. It is similar to what fixupDebugInfoPostExtraction does for CodeExtractor. I initially tried to re-use that function but found quickly that it would require quite a bit of re-factoring and additions before it could be used. It was much simpler to make the changes locally.
1 parent f101899 commit 196a1ac

File tree

3 files changed

+198
-0
lines changed

3 files changed

+198
-0
lines changed

llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@
3838
#include "llvm/IR/Function.h"
3939
#include "llvm/IR/GlobalVariable.h"
4040
#include "llvm/IR/IRBuilder.h"
41+
#include "llvm/IR/InstIterator.h"
42+
#include "llvm/IR/IntrinsicInst.h"
4143
#include "llvm/IR/LLVMContext.h"
4244
#include "llvm/IR/MDBuilder.h"
4345
#include "llvm/IR/Metadata.h"
@@ -6810,6 +6812,72 @@ FunctionCallee OpenMPIRBuilder::createDispatchDeinitFunction() {
68106812
return getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_dispatch_deinit);
68116813
}
68126814

6815+
static void FixupDebugInfoForOutlinedFunction(
6816+
OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, Function *Func,
6817+
DenseMap<Value *, std::tuple<Value *, unsigned>> &ValueReplacementMap) {
6818+
6819+
DISubprogram *NewSP = Func->getSubprogram();
6820+
if (!NewSP)
6821+
return;
6822+
6823+
DenseMap<const MDNode *, MDNode *> Cache;
6824+
SmallDenseMap<DILocalVariable *, DILocalVariable *> RemappedVariables;
6825+
6826+
auto GetUpdatedDIVariable = [&](DILocalVariable *OldVar, unsigned arg) {
6827+
auto NewSP = Func->getSubprogram();
6828+
DILocalVariable *&NewVar = RemappedVariables[OldVar];
6829+
// Only use cached variable if the arg number matches. This is important
6830+
// so that DIVariable created for privatized variables are not discarded.
6831+
if (NewVar && (arg == NewVar->getArg()))
6832+
return NewVar;
6833+
6834+
DILocalScope *NewScope = DILocalScope::cloneScopeForSubprogram(
6835+
*OldVar->getScope(), *NewSP, Builder.getContext(), Cache);
6836+
NewVar = llvm::DILocalVariable::get(
6837+
Builder.getContext(), NewScope, OldVar->getName(), OldVar->getFile(),
6838+
OldVar->getLine(), OldVar->getType(), arg, OldVar->getFlags(),
6839+
OldVar->getAlignInBits(), OldVar->getAnnotations());
6840+
return NewVar;
6841+
};
6842+
6843+
auto UpdateDebugRecord = [&](auto *DR) {
6844+
DILocalVariable *OldVar = DR->getVariable();
6845+
unsigned ArgNo = 0;
6846+
for (auto Loc : DR->location_ops()) {
6847+
auto Iter = ValueReplacementMap.find(Loc);
6848+
if (Iter != ValueReplacementMap.end()) {
6849+
DR->replaceVariableLocationOp(Loc, std::get<0>(Iter->second));
6850+
ArgNo = std::get<1>(Iter->second) + 1;
6851+
}
6852+
}
6853+
DR->setVariable(GetUpdatedDIVariable(OldVar, ArgNo));
6854+
};
6855+
6856+
// The location and scope of variable intrinsics and records still point to
6857+
// the parent function of the target region. Update them.
6858+
for (Instruction &I : instructions(Func)) {
6859+
if (auto *DDI = dyn_cast<llvm::DbgVariableIntrinsic>(&I))
6860+
UpdateDebugRecord(DDI);
6861+
6862+
for (DbgVariableRecord &DVR : filterDbgVars(I.getDbgRecordRange()))
6863+
UpdateDebugRecord(&DVR);
6864+
}
6865+
// An extra argument is passed to the device. Create the debug data for it.
6866+
if (OMPBuilder.Config.isTargetDevice()) {
6867+
DICompileUnit *CU = NewSP->getUnit();
6868+
Module *M = Func->getParent();
6869+
DIBuilder DB(*M, true, CU);
6870+
DIType *VoidPtrTy =
6871+
DB.createQualifiedType(dwarf::DW_TAG_pointer_type, nullptr);
6872+
DILocalVariable *Var = DB.createParameterVariable(
6873+
NewSP, "dyn_ptr", /*ArgNo*/ 1, NewSP->getFile(), /*LineNo=*/0,
6874+
VoidPtrTy, /*AlwaysPreserve=*/false, DINode::DIFlags::FlagArtificial);
6875+
auto Loc = DILocation::get(Func->getContext(), 0, 0, NewSP, 0);
6876+
DB.insertDeclare(&(*Func->arg_begin()), Var, DB.createExpression(), Loc,
6877+
&(*Func->begin()));
6878+
}
6879+
}
6880+
68136881
static Expected<Function *> createOutlinedFunction(
68146882
OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder,
68156883
const OpenMPIRBuilder::TargetKernelDefaultAttrs &DefaultAttrs,
@@ -6935,6 +7003,8 @@ static Expected<Function *> createOutlinedFunction(
69357003
? make_range(Func->arg_begin() + 1, Func->arg_end())
69367004
: Func->args();
69377005

7006+
DenseMap<Value *, std::tuple<Value *, unsigned>> ValueReplacementMap;
7007+
69387008
auto ReplaceValue = [](Value *Input, Value *InputCopy, Function *Func) {
69397009
// Things like GEP's can come in the form of Constants. Constants and
69407010
// ConstantExpr's do not have access to the knowledge of what they're
@@ -6976,6 +7046,7 @@ static Expected<Function *> createOutlinedFunction(
69767046
if (!AfterIP)
69777047
return AfterIP.takeError();
69787048
Builder.restoreIP(*AfterIP);
7049+
ValueReplacementMap[Input] = std::make_tuple(InputCopy, Arg.getArgNo());
69797050

69807051
// In certain cases a Global may be set up for replacement, however, this
69817052
// Global may be used in multiple arguments to the kernel, just segmented
@@ -7007,6 +7078,8 @@ static Expected<Function *> createOutlinedFunction(
70077078
for (auto Deferred : DeferredReplacement)
70087079
ReplaceValue(std::get<0>(Deferred), std::get<1>(Deferred), Func);
70097080

7081+
FixupDebugInfoForOutlinedFunction(OMPBuilder, Builder, Func,
7082+
ValueReplacementMap);
70107083
return Func;
70117084
}
70127085

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// RUN: mlir-translate -mlir-to-llvmir %s | FileCheck %s
2+
3+
#int_ty = #llvm.di_basic_type<tag = DW_TAG_base_type, name = "integer",
4+
sizeInBits = 32, encoding = DW_ATE_signed>
5+
#real_ty = #llvm.di_basic_type<tag = DW_TAG_base_type, name = "real",
6+
sizeInBits = 32, encoding = DW_ATE_float>
7+
#file = #llvm.di_file<"target.f90" in "">
8+
#di_null_type = #llvm.di_null_type
9+
#cu = #llvm.di_compile_unit<id = distinct[0]<>,
10+
sourceLanguage = DW_LANG_Fortran95, file = #file, isOptimized = false,
11+
emissionKind = Full>
12+
#array_ty = #llvm.di_composite_type<tag = DW_TAG_array_type,
13+
baseType = #int_ty, elements = #llvm.di_subrange<count = 10 : i64>>
14+
#sp_ty = #llvm.di_subroutine_type<callingConvention = DW_CC_program,
15+
types = #di_null_type>
16+
#g_var = #llvm.di_global_variable<scope = #cu, name = "arr",
17+
linkageName = "_QFEarr", file = #file, line = 4,
18+
type = #array_ty, isDefined = true>
19+
#g_var_expr = #llvm.di_global_variable_expression<var = #g_var>
20+
#sp = #llvm.di_subprogram<id = distinct[2]<>, compileUnit = #cu, scope = #file,
21+
name = "test", file = #file, subprogramFlags = "Definition", type = #sp_ty>
22+
#var_arr = #llvm.di_local_variable<scope = #sp,
23+
name = "arr", file = #file, line = 4, type = #array_ty>
24+
#var_i = #llvm.di_local_variable<scope = #sp,
25+
name = "i", file = #file, line = 13, type = #int_ty>
26+
#var_x = #llvm.di_local_variable<scope = #sp,
27+
name = "x", file = #file, line = 12, type = #real_ty>
28+
29+
module attributes {llvm.target_triple = "amdgcn-amd-amdhsa", omp.is_target_device = true} {
30+
llvm.func @test() {
31+
%0 = llvm.mlir.constant(1 : i64) : i64
32+
%1 = llvm.alloca %0 x f32 : (i64) -> !llvm.ptr
33+
%4 = llvm.alloca %0 x i32 : (i64) -> !llvm.ptr
34+
%6 = llvm.mlir.constant(9 : index) : i64
35+
%7 = llvm.mlir.constant(0 : index) : i64
36+
%8 = llvm.mlir.constant(1 : index) : i64
37+
%10 = llvm.mlir.constant(10 : index) : i64
38+
%11 = llvm.mlir.addressof @_QFEarr : !llvm.ptr
39+
%14 = omp.map.info var_ptr(%1 : !llvm.ptr, f32) map_clauses(tofrom) capture(ByRef) -> !llvm.ptr
40+
%15 = omp.map.bounds lower_bound(%7 : i64) upper_bound(%6 : i64) extent(%10 : i64) stride(%8 : i64) start_idx(%8 : i64)
41+
%16 = omp.map.info var_ptr(%11 : !llvm.ptr, !llvm.array<10 x i32>) map_clauses(tofrom) capture(ByRef) bounds(%15) -> !llvm.ptr
42+
%17 = omp.map.info var_ptr(%4 : !llvm.ptr, i32) map_clauses(implicit, exit_release_or_enter_alloc) capture(ByCopy) -> !llvm.ptr
43+
omp.target map_entries(%14 -> %arg0, %16 -> %arg1, %17 -> %arg2 : !llvm.ptr, !llvm.ptr, !llvm.ptr) {
44+
llvm.intr.dbg.declare #var_x = %arg0 : !llvm.ptr
45+
llvm.intr.dbg.declare #var_arr = %arg1 : !llvm.ptr
46+
llvm.intr.dbg.declare #var_i = %arg2 : !llvm.ptr
47+
omp.terminator
48+
}
49+
llvm.return
50+
} loc(#loc3)
51+
llvm.mlir.global internal @_QFEarr() {addr_space = 0 : i32, dbg_exprs = [#g_var_expr]} : !llvm.array<10 x i32> {
52+
} loc(#loc4)
53+
}
54+
#loc1 = loc("target.f90":4:7)
55+
#loc2 = loc("target.f90":11:7)
56+
#loc3 = loc(fused<#sp>[#loc2])
57+
#loc4 = loc(fused<#g_var>[#loc1])
58+
59+
// CHECK: ![[SP:[0-9]+]] = distinct !DISubprogram(name: "__omp_offloading{{.*}}test{{.*}})
60+
// CHECK: !DILocalVariable(name: "dyn_ptr", arg: 1, scope: ![[SP]]{{.*}}flags: DIFlagArtificial)
61+
// CHECK: !DILocalVariable(name: "x", arg: 2, scope: ![[SP]]{{.*}})
62+
// CHECK: !DILocalVariable(name: "arr", arg: 3, scope: ![[SP]]{{.*}})
63+
// CHECK: !DILocalVariable(name: "i", arg: 4, scope: ![[SP]]{{.*}})
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// RUN: mlir-translate -mlir-to-llvmir %s | FileCheck %s
2+
3+
#int_ty = #llvm.di_basic_type<tag = DW_TAG_base_type, name = "integer",
4+
sizeInBits = 32, encoding = DW_ATE_signed>
5+
#real_ty = #llvm.di_basic_type<tag = DW_TAG_base_type, name = "real",
6+
sizeInBits = 32, encoding = DW_ATE_float>
7+
#file = #llvm.di_file<"target.f90" in "">
8+
#di_null_type = #llvm.di_null_type
9+
#cu = #llvm.di_compile_unit<id = distinct[0]<>,
10+
sourceLanguage = DW_LANG_Fortran95, file = #file, isOptimized = false,
11+
emissionKind = Full>
12+
#array_ty = #llvm.di_composite_type<tag = DW_TAG_array_type,
13+
baseType = #int_ty, elements = #llvm.di_subrange<count = 10 : i64>>
14+
#sp_ty = #llvm.di_subroutine_type<callingConvention = DW_CC_program,
15+
types = #di_null_type>
16+
#g_var = #llvm.di_global_variable<scope = #cu, name = "arr",
17+
linkageName = "_QFEarr", file = #file, line = 4,
18+
type = #array_ty, isDefined = true>
19+
#g_var_expr = #llvm.di_global_variable_expression<var = #g_var>
20+
#sp = #llvm.di_subprogram<id = distinct[2]<>, compileUnit = #cu, scope = #file,
21+
name = "test", file = #file, subprogramFlags = "Definition", type = #sp_ty>
22+
#var_arr = #llvm.di_local_variable<scope = #sp,
23+
name = "arr", file = #file, line = 4, type = #array_ty>
24+
#var_i = #llvm.di_local_variable<scope = #sp,
25+
name = "i", file = #file, line = 13, type = #int_ty>
26+
#var_x = #llvm.di_local_variable<scope = #sp,
27+
name = "x", file = #file, line = 12, type = #real_ty>
28+
29+
module attributes {omp.is_target_device = false} {
30+
llvm.func @test() {
31+
%0 = llvm.mlir.constant(1 : i64) : i64
32+
%1 = llvm.alloca %0 x f32 : (i64) -> !llvm.ptr
33+
%4 = llvm.alloca %0 x i32 : (i64) -> !llvm.ptr
34+
%6 = llvm.mlir.constant(9 : index) : i64
35+
%7 = llvm.mlir.constant(0 : index) : i64
36+
%8 = llvm.mlir.constant(1 : index) : i64
37+
%10 = llvm.mlir.constant(10 : index) : i64
38+
%11 = llvm.mlir.addressof @_QFEarr : !llvm.ptr
39+
%14 = omp.map.info var_ptr(%1 : !llvm.ptr, f32) map_clauses(tofrom) capture(ByRef) -> !llvm.ptr
40+
%15 = omp.map.bounds lower_bound(%7 : i64) upper_bound(%6 : i64) extent(%10 : i64) stride(%8 : i64) start_idx(%8 : i64)
41+
%16 = omp.map.info var_ptr(%11 : !llvm.ptr, !llvm.array<10 x i32>) map_clauses(tofrom) capture(ByRef) bounds(%15) -> !llvm.ptr
42+
%17 = omp.map.info var_ptr(%4 : !llvm.ptr, i32) map_clauses(implicit, exit_release_or_enter_alloc) capture(ByCopy) -> !llvm.ptr
43+
omp.target map_entries(%14 -> %arg0, %16 -> %arg1, %17 -> %arg2 : !llvm.ptr, !llvm.ptr, !llvm.ptr) {
44+
llvm.intr.dbg.declare #var_x = %arg0 : !llvm.ptr
45+
llvm.intr.dbg.declare #var_arr = %arg1 : !llvm.ptr
46+
llvm.intr.dbg.declare #var_i = %arg2 : !llvm.ptr
47+
omp.terminator
48+
}
49+
llvm.return
50+
} loc(#loc3)
51+
llvm.mlir.global internal @_QFEarr() {addr_space = 0 : i32, dbg_exprs = [#g_var_expr]} : !llvm.array<10 x i32> {
52+
} loc(#loc4)
53+
}
54+
#loc1 = loc("target.f90":4:7)
55+
#loc2 = loc("target.f90":11:7)
56+
#loc3 = loc(fused<#sp>[#loc2])
57+
#loc4 = loc(fused<#g_var>[#loc1])
58+
59+
// CHECK: ![[SP:[0-9]+]] = distinct !DISubprogram(name: "__omp_offloading{{.*}}test{{.*}})
60+
// CHECK: !DILocalVariable(name: "x", arg: 1, scope: ![[SP]]{{.*}})
61+
// CHECK: !DILocalVariable(name: "arr", arg: 2, scope: ![[SP]]{{.*}})
62+
// CHECK: !DILocalVariable(name: "i", arg: 3, scope: ![[SP]]{{.*}})

0 commit comments

Comments
 (0)