Skip to content

Commit 576842c

Browse files
committed
[flang][debug] Generate DISubprogramAttr for omp::TargetOp.
There are DeclareOp present for the variables mapped into target region. That allow us to generate debug information for them. Bu the TargetOp is still part of parent function and those variables get the parent function's DISubprogram as a scope. In OMPIRBuilder, a new function is created for the TargetOp. We also create a new DISubprogram for it. All the variables that were in the target region now have to be updated to have the correct scope. This after the fact updating of debug information becomes very difficult in certain cases. Take the example of variable arrays. The type of those arrays depend on the artificial DILocalVariable(s) which hold the size(s) of the array. This new function will now require that we generate the new variable and and new types. Similar issue exist for character type variables too. To avoid this after the fact updating, this PR generates a DISubprogramAttr for the TargetOp while generating the debug info in flang. This help us avoid updating later. This PR is flang side of the change. I will open another PR which will make the required changes in OMPIRBuilder.
1 parent d8e8ab7 commit 576842c

File tree

3 files changed

+186
-1
lines changed

3 files changed

+186
-1
lines changed

flang/lib/Optimizer/Transforms/AddDebugInfo.cpp

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include "llvm/BinaryFormat/Dwarf.h"
3636
#include "llvm/Support/Debug.h"
3737
#include "llvm/Support/FileSystem.h"
38+
#include "llvm/Support/FormatVariadic.h"
3839
#include "llvm/Support/Path.h"
3940
#include "llvm/Support/raw_ostream.h"
4041

@@ -104,6 +105,37 @@ bool debugInfoIsAlreadySet(mlir::Location loc) {
104105
return false;
105106
}
106107

108+
// Generates the name for the artificial DISubprogram that we are going to
109+
// generate for omp::TargetOp. Its logic is borrowed from
110+
// getTargetEntryUniqueInfo and
111+
// TargetRegionEntryInfo::getTargetRegionEntryFnName to generate the same name.
112+
// But even if there was a slight mismatch, it is not a problem because this
113+
// name is artifical and not important to debug experience.
114+
mlir::StringAttr getTargetFunctionName(mlir::MLIRContext *context,
115+
mlir::Location Loc,
116+
llvm::StringRef parentName) {
117+
auto fileLoc = Loc->findInstanceOf<mlir::FileLineColLoc>();
118+
119+
assert(fileLoc && "No file found from location");
120+
llvm::StringRef fileName = fileLoc.getFilename().getValue();
121+
122+
llvm::sys::fs::UniqueID id;
123+
uint64_t line = fileLoc.getLine();
124+
size_t fileId;
125+
size_t deviceId;
126+
if (auto ec = llvm::sys::fs::getUniqueID(fileName, id)) {
127+
fileId = llvm::hash_value(fileName.str());
128+
deviceId = 0xdeadf17e;
129+
} else {
130+
fileId = id.getFile();
131+
deviceId = id.getDevice();
132+
}
133+
return mlir::StringAttr::get(
134+
context,
135+
std::string(llvm::formatv("__omp_offloading_{0:x-}_{1:x-}_{2}_l{3}",
136+
deviceId, fileId, parentName, line)));
137+
}
138+
107139
} // namespace
108140

109141
bool AddDebugInfoPass::createCommonBlockGlobal(
@@ -511,8 +543,73 @@ void AddDebugInfoPass::handleFuncOp(mlir::func::FuncOp funcOp,
511543
subTypeAttr, entities, /*annotations=*/{});
512544
funcOp->setLoc(builder.getFusedLoc({l}, spAttr));
513545

546+
/* When we process the DeclareOp inside the OpenMP target region, all the
547+
variables get the DISubprogram of the parent function of the target op as
548+
the scope. In the codegen (to llvm ir), OpenMP target op results in the
549+
creation of a separate function. As the variables in the debug info have
550+
the DISubprogram of the parent function as the scope, the variables
551+
need to be updated at codegen time to avoid verification failures.
552+
553+
This updating after the fact becomes more and more difficult when types
554+
are dependent on local variables like in the case of variable size arrays
555+
or string. We not only have to generate new variables but also new types.
556+
We can avoid this problem by generating a DISubprogramAttr here for the
557+
target op and make sure that all the variables inside the target region
558+
get the correct scope in the first place. */
559+
funcOp.walk([&](mlir::omp::TargetOp targetOp) {
560+
unsigned line = getLineFromLoc(targetOp.getLoc());
561+
mlir::StringAttr Name =
562+
getTargetFunctionName(context, targetOp.getLoc(), funcOp.getName());
563+
mlir::LLVM::DISubprogramFlags flags =
564+
mlir::LLVM::DISubprogramFlags::Definition |
565+
mlir::LLVM::DISubprogramFlags::LocalToUnit;
566+
if (isOptimized)
567+
flags = flags | mlir::LLVM::DISubprogramFlags::Optimized;
568+
569+
mlir::DistinctAttr recId =
570+
mlir::DistinctAttr::create(mlir::UnitAttr::get(context));
571+
mlir::DistinctAttr Id =
572+
mlir::DistinctAttr::create(mlir::UnitAttr::get(context));
573+
llvm::SmallVector<mlir::LLVM::DITypeAttr> types;
574+
types.push_back(mlir::LLVM::DINullTypeAttr::get(context));
575+
mlir::LLVM::DISubroutineTypeAttr spTy =
576+
mlir::LLVM::DISubroutineTypeAttr::get(context, CC, types);
577+
auto spAttr = mlir::LLVM::DISubprogramAttr::get(
578+
context, recId, /*isRecSelf=*/true, Id, compilationUnit, Scope, Name,
579+
Name, funcFileAttr, line, line, flags, spTy, /*retainedNodes=*/{},
580+
/*annotations=*/{});
581+
582+
// Make sure that information about the imported modules in copied from the
583+
// parent function.
584+
llvm::SmallVector<mlir::LLVM::DINodeAttr> OpEntities;
585+
for (mlir::LLVM::DINodeAttr N : entities) {
586+
if (auto entity = mlir::dyn_cast<mlir::LLVM::DIImportedEntityAttr>(N)) {
587+
auto importedEntity = mlir::LLVM::DIImportedEntityAttr::get(
588+
context, llvm::dwarf::DW_TAG_imported_module, spAttr,
589+
entity.getEntity(), fileAttr, /*line=*/1, /*name=*/nullptr,
590+
/*elements*/ {});
591+
OpEntities.push_back(importedEntity);
592+
}
593+
}
594+
595+
Id = mlir::DistinctAttr::create(mlir::UnitAttr::get(context));
596+
spAttr = mlir::LLVM::DISubprogramAttr::get(
597+
context, recId, /*isRecSelf=*/false, Id, compilationUnit, Scope, Name,
598+
Name, funcFileAttr, line, line, flags, spTy, OpEntities,
599+
/*annotations=*/{});
600+
targetOp->setLoc(builder.getFusedLoc({targetOp.getLoc()}, spAttr));
601+
});
602+
514603
funcOp.walk([&](fir::cg::XDeclareOp declOp) {
515-
handleDeclareOp(declOp, fileAttr, spAttr, typeGen, symbolTable);
604+
mlir::LLVM::DISubprogramAttr spTy = spAttr;
605+
if (auto tOp = declOp->getParentOfType<mlir::omp::TargetOp>()) {
606+
if (auto fusedLoc = llvm::dyn_cast<mlir::FusedLoc>(tOp.getLoc())) {
607+
if (auto sp = llvm::dyn_cast<mlir::LLVM::DISubprogramAttr>(
608+
fusedLoc.getMetadata()))
609+
spTy = sp;
610+
}
611+
}
612+
handleDeclareOp(declOp, fileAttr, spTy, typeGen, symbolTable);
516613
});
517614
// commonBlockMap ensures that we don't create multiple DICommonBlockAttr of
518615
// the same name in one function. But it is ok (rather required) to create
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// RUN: fir-opt --add-debug-info --mlir-print-debuginfo %s | FileCheck %s
2+
3+
module attributes {dlti.dl_spec = #dlti.dl_spec<>} {
4+
func.func @_QQmain() attributes {fir.bindc_name = "test"} {
5+
%c13_i32 = arith.constant 13 : i32
6+
%c12_i32 = arith.constant 12 : i32
7+
%c6_i32 = arith.constant 6 : i32
8+
%c1_i32 = arith.constant 1 : i32
9+
%c5_i32 = arith.constant 5 : i32
10+
%0 = fir.alloca i32 {bindc_name = "x", uniq_name = "_QFEx"} loc(#loc1)
11+
%1 = fircg.ext_declare %0 {uniq_name = "_QFEx"} : (!fir.ref<i32>) -> !fir.ref<i32> loc(#loc1)
12+
%2 = fir.alloca i32 {bindc_name = "y", uniq_name = "_QFEy"} loc(#loc2)
13+
%3 = fircg.ext_declare %2 {uniq_name = "_QFEy"} : (!fir.ref<i32>) -> !fir.ref<i32> loc(#loc2)
14+
%4 = omp.map.info var_ptr(%1 : !fir.ref<i32>, i32) map_clauses(tofrom) capture(ByRef) -> !fir.ref<i32> {name = "x"}
15+
%5 = omp.map.info var_ptr(%3 : !fir.ref<i32>, i32) map_clauses(tofrom) capture(ByRef) -> !fir.ref<i32> {name = "y"}
16+
omp.target map_entries(%4 -> %arg0, %5 -> %arg1 : !fir.ref<i32>, !fir.ref<i32>) {
17+
%16 = fircg.ext_declare %arg0 {uniq_name = "_QFEx"} : (!fir.ref<i32>) -> !fir.ref<i32> loc(#loc3)
18+
%17 = fircg.ext_declare %arg1 {uniq_name = "_QFEy"} : (!fir.ref<i32>) -> !fir.ref<i32> loc(#loc4)
19+
omp.terminator
20+
} loc(#loc5)
21+
return
22+
}
23+
}
24+
#loc1 = loc("test.f90":1:1)
25+
#loc2 = loc("test.f90":3:1)
26+
#loc3 = loc("test.f90":7:1)
27+
#loc4 = loc("test.f90":8:1)
28+
#loc5 = loc("test.f90":6:1)
29+
30+
// CHECK: #[[SP:.*]] = #llvm.di_subprogram<{{.*}}name = "test"{{.*}}>
31+
// CHECK: #[[SP1:.*]] = #llvm.di_subprogram<{{.*}}name = "__omp_offloading_{{.*}}_QQmain_l6"{{.*}}line = 6{{.*}}subprogramFlags = "LocalToUnit|Definition"{{.*}}>
32+
// CHECK: #llvm.di_local_variable<scope = #[[SP]], name = "x"{{.*}}line = 1, type = #[[TY:.*]]>
33+
// CHECK: #llvm.di_local_variable<scope = #[[SP]], name = "y"{{.*}}line = 3, type = #[[TY]]>
34+
// CHECK: #llvm.di_local_variable<scope = #[[SP1]], name = "x"{{.*}}line = 7, type = #[[TY]]>
35+
// CHECK: #llvm.di_local_variable<scope = #[[SP1]], name = "y"{{.*}}line = 8, type = #[[TY]]>
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// RUN: fir-opt --add-debug-info --mlir-print-debuginfo %s | FileCheck %s
2+
3+
module attributes {dlti.dl_spec = #dlti.dl_spec<>} {
4+
func.func @fn_(%arg0: !fir.ref<!fir.array<?x?xi32>> {fir.bindc_name = "b"}, %arg1: !fir.ref<i32> {fir.bindc_name = "c"}, %arg2: !fir.ref<i32> {fir.bindc_name = "d"}) {
5+
%c1 = arith.constant 1 : index
6+
%c0 = arith.constant 0 : index
7+
%0 = fir.alloca i32
8+
%1 = fir.alloca i32
9+
%2 = fir.undefined !fir.dscope
10+
%3 = fircg.ext_declare %arg1 dummy_scope %2 {uniq_name = "_QFfnEc"} : (!fir.ref<i32>, !fir.dscope) -> !fir.ref<i32> loc(#loc2)
11+
%4 = fircg.ext_declare %arg2 dummy_scope %2 {uniq_name = "_QFfnEd"} : (!fir.ref<i32>, !fir.dscope) -> !fir.ref<i32> loc(#loc3)
12+
%5 = fir.load %3 : !fir.ref<i32>
13+
%6 = fir.convert %5 : (i32) -> index
14+
%9 = fir.load %4 : !fir.ref<i32>
15+
%10 = fir.convert %9 : (i32) -> index
16+
%15 = fircg.ext_declare %arg0(%6, %10) dummy_scope %2 {uniq_name = "_QFfnEb"} : (!fir.ref<!fir.array<?x?xi32>>, index, index, !fir.dscope) -> !fir.ref<!fir.array<?x?xi32>> loc(#loc4)
17+
%16 = fircg.ext_embox %15(%6, %10) : (!fir.ref<!fir.array<?x?xi32>>, index, index) -> !fir.box<!fir.array<?x?xi32>>
18+
%17:3 = fir.box_dims %16, %c0 : (!fir.box<!fir.array<?x?xi32>>, index) -> (index, index, index)
19+
%18 = arith.subi %17#1, %c1 : index
20+
%19 = omp.map.bounds lower_bound(%c0 : index) upper_bound(%18 : index) extent(%17#1 : index) stride(%17#2 : index) start_idx(%c1 : index) {stride_in_bytes = true}
21+
%20 = arith.muli %17#2, %17#1 : index
22+
%21:3 = fir.box_dims %16, %c1 : (!fir.box<!fir.array<?x?xi32>>, index) -> (index, index, index)
23+
%22 = arith.subi %21#1, %c1 : index
24+
%23 = omp.map.bounds lower_bound(%c0 : index) upper_bound(%22 : index) extent(%21#1 : index) stride(%20 : index) start_idx(%c1 : index) {stride_in_bytes = true}
25+
%24 = omp.map.info var_ptr(%15 : !fir.ref<!fir.array<?x?xi32>>, i32) map_clauses(tofrom) capture(ByRef) bounds(%19, %23) -> !fir.ref<!fir.array<?x?xi32>> {name = "b"}
26+
%25 = omp.map.info var_ptr(%1 : !fir.ref<i32>, i32) map_clauses(implicit, exit_release_or_enter_alloc) capture(ByCopy) -> !fir.ref<i32> {name = ""}
27+
%26 = omp.map.info var_ptr(%0 : !fir.ref<i32>, i32) map_clauses(implicit, exit_release_or_enter_alloc) capture(ByCopy) -> !fir.ref<i32> {name = ""}
28+
omp.target map_entries(%24 -> %arg3, %25 -> %arg4, %26 -> %arg5 : !fir.ref<!fir.array<?x?xi32>>, !fir.ref<i32>, !fir.ref<i32>) {
29+
%27 = fir.load %arg5 : !fir.ref<i32>
30+
%28 = fir.load %arg4 : !fir.ref<i32>
31+
%29 = fir.convert %27 : (i32) -> index
32+
%31 = fir.convert %28 : (i32) -> index
33+
%37 = fircg.ext_declare %arg3(%29, %31) {uniq_name = "_QFfnEb"} : (!fir.ref<!fir.array<?x?xi32>>, index, index) -> !fir.ref<!fir.array<?x?xi32>> loc(#loc5)
34+
omp.terminator
35+
} loc(#loc6)
36+
return
37+
} loc(#loc7)
38+
}
39+
#loc1 = loc("test.f90":1:1)
40+
#loc2 = loc("test.f90":3:1)
41+
#loc3 = loc("test.f90":7:1)
42+
#loc4 = loc("test.f90":8:1)
43+
#loc5 = loc("test.f90":6:1)
44+
#loc6 = loc("test.f90":16:1)
45+
#loc7 = loc("test.f90":26:1)
46+
47+
48+
// Test that variable size arrays inside target regions get their own
49+
// compiler generated variables for size.
50+
51+
// CHECK: #[[SP:.*]] = #llvm.di_subprogram<{{.*}}name = "__omp_offloading_{{.*}}_fn__l16"{{.*}}>
52+
// CHECK: #llvm.di_local_variable<scope = #[[SP]], name = "._QFfnEb1"{{.*}}>
53+
// CHECK: #llvm.di_local_variable<scope = #[[SP]], name = "._QFfnEb2"{{.*}}>

0 commit comments

Comments
 (0)