Skip to content

Commit 70b1452

Browse files
committed
[flang] Add initial support for local variables.
This commit extracts information about local variables from XDeclareOp and creates DILocalVariableAttr. These are attached to DeclareOp using FusedLoc approach. Codegen can use them to create DbgDeclareOp. I have added tests that checks the debug information in mlir from and also in llvm ir.
1 parent 971f021 commit 70b1452

File tree

4 files changed

+191
-4
lines changed

4 files changed

+191
-4
lines changed

flang/lib/Optimizer/CodeGen/CodeGen.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,14 @@ struct DeclareOpConversion : public fir::FIROpConversion<fir::cg::XDeclareOp> {
178178
matchAndRewrite(fir::cg::XDeclareOp declareOp, OpAdaptor adaptor,
179179
mlir::ConversionPatternRewriter &rewriter) const override {
180180
auto memRef = adaptor.getOperands()[0];
181+
if (auto fusedLoc = mlir::dyn_cast<mlir::FusedLoc>(declareOp.getLoc())) {
182+
if (auto varAttr =
183+
mlir::dyn_cast_or_null<mlir::LLVM::DILocalVariableAttr>(
184+
fusedLoc.getMetadata())) {
185+
rewriter.create<mlir::LLVM::DbgDeclareOp>(memRef.getLoc(), memRef,
186+
varAttr, nullptr);
187+
}
188+
}
181189
rewriter.replaceOp(declareOp, memRef);
182190
return mlir::success();
183191
}

flang/lib/Optimizer/Transforms/AddDebugInfo.cpp

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "flang/Common/Version.h"
1616
#include "flang/Optimizer/Builder/FIRBuilder.h"
1717
#include "flang/Optimizer/Builder/Todo.h"
18+
#include "flang/Optimizer/CodeGen/CGOps.h"
1819
#include "flang/Optimizer/Dialect/FIRDialect.h"
1920
#include "flang/Optimizer/Dialect/FIROps.h"
2021
#include "flang/Optimizer/Dialect/FIRType.h"
@@ -45,13 +46,44 @@ namespace fir {
4546
namespace {
4647

4748
class AddDebugInfoPass : public fir::impl::AddDebugInfoBase<AddDebugInfoPass> {
49+
void handleDeclareOp(fir::cg::XDeclareOp declOp,
50+
mlir::LLVM::DIFileAttr fileAttr,
51+
mlir::LLVM::DIScopeAttr scopeAttr,
52+
fir::DebugTypeGenerator &typeGen, uint32_t &argNo);
53+
4854
public:
4955
AddDebugInfoPass(fir::AddDebugInfoOptions options) : Base(options) {}
5056
void runOnOperation() override;
5157
};
5258

59+
static uint32_t getLineFromLoc(mlir::Location loc) {
60+
uint32_t line = 1;
61+
if (auto fileLoc = mlir::dyn_cast<mlir::FileLineColLoc>(loc))
62+
line = fileLoc.getLine();
63+
return line;
64+
}
65+
5366
} // namespace
5467

68+
void AddDebugInfoPass::handleDeclareOp(fir::cg::XDeclareOp declOp,
69+
mlir::LLVM::DIFileAttr fileAttr,
70+
mlir::LLVM::DIScopeAttr scopeAttr,
71+
fir::DebugTypeGenerator &typeGen,
72+
uint32_t &argNo) {
73+
mlir::MLIRContext *context = &getContext();
74+
mlir::OpBuilder builder(context);
75+
76+
bool isLocal = (declOp.getMemref().getDefiningOp() != nullptr);
77+
auto tyAttr = typeGen.convertType(fir::unwrapRefType(declOp.getType()),
78+
fileAttr, scopeAttr, declOp.getLoc());
79+
auto result = fir::NameUniquer::deconstruct(declOp.getUniqName());
80+
auto localVarAttr = mlir::LLVM::DILocalVariableAttr::get(
81+
context, scopeAttr, mlir::StringAttr::get(context, result.second.name),
82+
fileAttr, getLineFromLoc(declOp.getLoc()), isLocal ? 0 : argNo++,
83+
/* alignInBits*/ 0, tyAttr);
84+
declOp->setLoc(builder.getFusedLoc({declOp->getLoc()}, localVarAttr));
85+
}
86+
5587
void AddDebugInfoPass::runOnOperation() {
5688
mlir::ModuleOp module = getOperation();
5789
mlir::MLIRContext *context = &getContext();
@@ -144,14 +176,16 @@ void AddDebugInfoPass::runOnOperation() {
144176
subprogramFlags =
145177
subprogramFlags | mlir::LLVM::DISubprogramFlags::Definition;
146178
}
147-
unsigned line = 1;
148-
if (auto funcLoc = mlir::dyn_cast<mlir::FileLineColLoc>(l))
149-
line = funcLoc.getLine();
150-
179+
unsigned line = getLineFromLoc(l);
151180
auto spAttr = mlir::LLVM::DISubprogramAttr::get(
152181
context, id, compilationUnit, fileAttr, funcName, fullName,
153182
funcFileAttr, line, line, subprogramFlags, subTypeAttr);
154183
funcOp->setLoc(builder.getFusedLoc({funcOp->getLoc()}, spAttr));
184+
185+
uint32_t argNo = 1;
186+
funcOp.walk([&](fir::cg::XDeclareOp declOp) {
187+
handleDeclareOp(declOp, fileAttr, spAttr, typeGen, argNo);
188+
});
155189
});
156190
}
157191

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
! RUN: %flang_fc1 -emit-llvm -debug-info-kind=standalone %s -o - | FileCheck %s
2+
3+
! This tests checks the debug information for local variables in llvm IR.
4+
5+
! CHECK-LABEL: define void @_QQmain
6+
! CHECK-DAG: %[[AL11:.*]] = alloca i32
7+
! CHECK-DAG: %[[AL12:.*]] = alloca i64
8+
! CHECK-DAG: %[[AL13:.*]] = alloca i8
9+
! CHECK-DAG: %[[AL14:.*]] = alloca i32
10+
! CHECK-DAG: %[[AL15:.*]] = alloca float
11+
! CHECK-DAG: %[[AL16:.*]] = alloca double
12+
! CHECK-DAG: call void @llvm.dbg.declare(metadata ptr %[[AL11]], metadata ![[I4:.*]], metadata !DIExpression())
13+
! CHECK-DAG: call void @llvm.dbg.declare(metadata ptr %[[AL12]], metadata ![[I8:.*]], metadata !DIExpression())
14+
! CHECK-DAG: call void @llvm.dbg.declare(metadata ptr %[[AL13]], metadata ![[L1:.*]], metadata !DIExpression())
15+
! CHECK-DAG: call void @llvm.dbg.declare(metadata ptr %[[AL14]], metadata ![[L4:.*]], metadata !DIExpression())
16+
! CHECK-DAG: call void @llvm.dbg.declare(metadata ptr %[[AL15]], metadata ![[R4:.*]], metadata !DIExpression())
17+
! CHECK-DAG: call void @llvm.dbg.declare(metadata ptr %[[AL16]], metadata ![[R8:.*]], metadata !DIExpression())
18+
! CHECK-LABEL: }
19+
20+
! CHECK-LABEL: define {{.*}}i64 @_QFPfn1
21+
! CHECK-SAME: (ptr %[[ARG1:.*]], ptr %[[ARG2:.*]], ptr %[[ARG3:.*]])
22+
! CHECK-DAG: tail call void @llvm.dbg.declare(metadata ptr %[[ARG1]], metadata ![[A1:.*]], metadata !DIExpression())
23+
! CHECK-DAG: tail call void @llvm.dbg.declare(metadata ptr %[[ARG2]], metadata ![[B1:.*]], metadata !DIExpression())
24+
! CHECK-DAG: tail call void @llvm.dbg.declare(metadata ptr %[[ARG3]], metadata ![[C1:.*]], metadata !DIExpression())
25+
! CHECK-DAG: %[[AL2:.*]] = alloca i64
26+
! CHECK-DAG: tail call void @llvm.dbg.declare(metadata ptr %[[AL2]], metadata ![[RES1:.*]], metadata !DIExpression())
27+
! CHECK-LABEL: }
28+
29+
! CHECK-LABEL: define {{.*}}i32 @_QFPfn2
30+
! CHECK-SAME: (ptr %[[FN2ARG1:.*]], ptr %[[FN2ARG2:.*]], ptr %[[FN2ARG3:.*]])
31+
! CHECK-DAG: tail call void @llvm.dbg.declare(metadata ptr %[[FN2ARG1]], metadata ![[A2:.*]], metadata !DIExpression())
32+
! CHECK-DAG: tail call void @llvm.dbg.declare(metadata ptr %[[FN2ARG2]], metadata ![[B2:.*]], metadata !DIExpression())
33+
! CHECK-DAG: tail call void @llvm.dbg.declare(metadata ptr %[[FN2ARG3]], metadata ![[C2:.*]], metadata !DIExpression())
34+
! CHECK-DAG: %[[AL3:.*]] = alloca i32
35+
! CHECK-DAG: tail call void @llvm.dbg.declare(metadata ptr %[[AL3]], metadata ![[RES2:.*]], metadata !DIExpression())
36+
! CHECK-LABEL: }
37+
38+
program mn
39+
! CHECK-DAG: ![[MAIN:.*]] = distinct !DISubprogram(name: "_QQmain", {{.*}})
40+
41+
! CHECK-DAG: ![[TYI32:.*]] = !DIBasicType(name: "integer", size: 32, encoding: DW_ATE_signed)
42+
! CHECK-DAG: ![[TYI64:.*]] = !DIBasicType(name: "integer", size: 64, encoding: DW_ATE_signed)
43+
! CHECK-DAG: ![[TYL8:.*]] = !DIBasicType(name: "logical", size: 8, encoding: DW_ATE_boolean)
44+
! CHECK-DAG: ![[TYL32:.*]] = !DIBasicType(name: "logical", size: 32, encoding: DW_ATE_boolean)
45+
! CHECK-DAG: ![[TYR32:.*]] = !DIBasicType(name: "real", size: 32, encoding: DW_ATE_float)
46+
! CHECK-DAG: ![[TYR64:.*]] = !DIBasicType(name: "real", size: 64, encoding: DW_ATE_float)
47+
48+
! CHECK-DAG: ![[I4]] = !DILocalVariable(name: "i4", scope: ![[MAIN]], file: !{{.*}}, line: [[@LINE+6]], type: ![[TYI32]])
49+
! CHECK-DAG: ![[I8]] = !DILocalVariable(name: "i8", scope: ![[MAIN]], file: !{{.*}}, line: [[@LINE+6]], type: ![[TYI64]])
50+
! CHECK-DAG: ![[R4]] = !DILocalVariable(name: "r4", scope: ![[MAIN]], file: !{{.*}}, line: [[@LINE+6]], type: ![[TYR32]])
51+
! CHECK-DAG: ![[R8]] = !DILocalVariable(name: "r8", scope: ![[MAIN]], file: !{{.*}}, line: [[@LINE+6]], type: ![[TYR64]])
52+
! CHECK-DAG: ![[L1]] = !DILocalVariable(name: "l1", scope: ![[MAIN]], file: !{{.*}}, line: [[@LINE+6]], type: ![[TYL8]])
53+
! CHECK-DAG: ![[L4]] = !DILocalVariable(name: "l4", scope: ![[MAIN]], file: !{{.*}}, line: [[@LINE+6]], type: ![[TYL32]])
54+
integer(kind=4) :: i4
55+
integer(kind=8) :: i8
56+
real(kind=4) :: r4
57+
real(kind=8) :: r8
58+
logical(kind=1) :: l1
59+
logical(kind=4) :: l4
60+
61+
i8 = fn1(i4, r8, l1)
62+
i4 = fn2(i8, r4, l4)
63+
contains
64+
! CHECK-DAG: ![[FN1:.*]] = distinct !DISubprogram(name: "fn1", {{.*}})
65+
! CHECK-DAG: ![[A1]] = !DILocalVariable(name: "a1", arg: 1, scope: ![[FN1]], file: !{{.*}}, line: [[@LINE+5]], type: ![[TYI32]])
66+
! CHECK-DAG: ![[B1]] = !DILocalVariable(name: "b1", arg: 2, scope: ![[FN1]], file: !{{.*}}, line: [[@LINE+5]], type: ![[TYR64]])
67+
! CHECK-DAG: ![[C1]] = !DILocalVariable(name: "c1", arg: 3, scope: ![[FN1]], file: !{{.*}}, line: [[@LINE+5]], type: ![[TYL8]])
68+
! CHECK-DAG: ![[RES1]] = !DILocalVariable(name: "res1", scope: ![[FN1]], file: !{{.*}}, line: [[@LINE+5]], type: ![[TYI64]])
69+
function fn1(a1, b1, c1) result (res1)
70+
integer(kind=4), intent(in) :: a1
71+
real(kind=8), intent(in) :: b1
72+
logical(kind=1), intent(in) :: c1
73+
integer(kind=8) :: res1
74+
75+
res1 = a1 + b1
76+
end function
77+
78+
! CHECK-DAG: ![[FN2:.*]] = distinct !DISubprogram(name: "fn2", {{.*}})
79+
! CHECK-DAG: ![[A2]] = !DILocalVariable(name: "a2", arg: 1, scope: ![[FN2]], file: !{{.*}}, line: [[@LINE+5]], type: ![[TYI64]])
80+
! CHECK-DAG: ![[B2]] = !DILocalVariable(name: "b2", arg: 2, scope: ![[FN2]], file: !{{.*}}, line: [[@LINE+5]], type: ![[TYR32]])
81+
! CHECK-DAG: ![[C2]] = !DILocalVariable(name: "c2", arg: 3, scope: ![[FN2]], file: !{{.*}}, line: [[@LINE+5]], type: ![[TYL32]])
82+
! CHECK-DAG: ![[RES2]] = !DILocalVariable(name: "res2", scope: ![[FN2]], file: !{{.*}}, line: [[@LINE+5]], type: ![[TYI32]])
83+
function fn2(a2, b2, c2) result (res2)
84+
integer(kind=8), intent(in) :: a2
85+
real(kind=4), intent(in) :: b2
86+
logical(kind=4), intent(in) :: c2
87+
integer(kind=4) :: res2
88+
89+
res2 = a2 + b2
90+
end function
91+
end program
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
! RUN: %flang_fc1 -emit-fir -debug-info-kind=standalone -mmlir --mlir-print-debuginfo %s -o - | \
2+
! RUN: fir-opt --cg-rewrite --mlir-print-debuginfo | fir-opt --add-debug-info --mlir-print-debuginfo | FileCheck %s
3+
4+
! CHECK-DAG: #[[INT8:.*]] = #llvm.di_basic_type<tag = DW_TAG_base_type, name = "integer", sizeInBits = 64, encoding = DW_ATE_signed>
5+
! CHECK-DAG: #[[INT4:.*]] = #llvm.di_basic_type<tag = DW_TAG_base_type, name = "integer", sizeInBits = 32, encoding = DW_ATE_signed>
6+
! CHECK-DAG: #[[REAL8:.*]] = #llvm.di_basic_type<tag = DW_TAG_base_type, name = "real", sizeInBits = 64, encoding = DW_ATE_float>
7+
! CHECK-DAG: #[[LOG1:.*]] = #llvm.di_basic_type<tag = DW_TAG_base_type, name = "logical", sizeInBits = 8, encoding = DW_ATE_boolean>
8+
! CHECK-DAG: #[[REAL4:.*]] = #llvm.di_basic_type<tag = DW_TAG_base_type, name = "real", sizeInBits = 32, encoding = DW_ATE_float>
9+
! CHECK-DAG: #[[LOG4:.*]] = #llvm.di_basic_type<tag = DW_TAG_base_type, name = "logical", sizeInBits = 32, encoding = DW_ATE_boolean>
10+
! CHECK-DAG: #[[MAIN:.*]] = #llvm.di_subprogram<{{.*}}name = "_QQmain"{{.*}}>
11+
! CHECK-DAG: #[[FN1:.*]] = #llvm.di_subprogram<{{.*}}name = "fn1"{{.*}}>
12+
! CHECK-DAG: #[[FN2:.*]] = #llvm.di_subprogram<{{.*}}name = "fn2"{{.*}}>
13+
14+
program mn
15+
! CHECK-DAG: #[[I4:.*]] = #llvm.di_local_variable<scope = #[[MAIN]], name = "i4", file = #{{.*}}, line = [[@LINE+6]], type = #[[INT4]]>
16+
! CHECK-DAG: #[[I8:.*]] = #llvm.di_local_variable<scope = #[[MAIN]], name = "i8", file = #{{.*}}, line = [[@LINE+6]], type = #[[INT8]]>
17+
! CHECK-DAG: #[[R4:.*]] = #llvm.di_local_variable<scope = #[[MAIN]], name = "r4", file = #{{.*}}, line = [[@LINE+6]], type = #[[REAL4]]>
18+
! CHECK-DAG: #[[R8:.*]] = #llvm.di_local_variable<scope = #[[MAIN]], name = "r8", file = #{{.*}}, line = [[@LINE+6]], type = #[[REAL8]]>
19+
! CHECK-DAG: #[[L1:.*]] = #llvm.di_local_variable<scope = #[[MAIN]], name = "l1", file = #{{.*}}, line = [[@LINE+6]], type = #[[LOG1]]>
20+
! CHECK-DAG: #[[L4:.*]] = #llvm.di_local_variable<scope = #[[MAIN]], name = "l4", file = #{{.*}}, line = [[@LINE+6]], type = #[[LOG4]]>
21+
integer(kind=4) :: i4
22+
integer(kind=8) :: i8
23+
real(kind=4) :: r4
24+
real(kind=8) :: r8
25+
logical(kind=1) :: l1
26+
logical(kind=4) :: l4
27+
i8 = fn1(i4, r8, l1)
28+
i4 = fn2(i8, r4, l4)
29+
contains
30+
function fn1(a1, b1, c1) result (res1)
31+
! CHECK-DAG: #[[A1:.*]] = #llvm.di_local_variable<scope = #[[FN1]], name = "a1", file = #{{.*}}, line = [[@LINE+4]], arg = 1, type = #[[INT4]]>
32+
! CHECK-DAG: #[[B1:.*]] = #llvm.di_local_variable<scope = #[[FN1]], name = "b1", file = #{{.*}}, line = [[@LINE+4]], arg = 2, type = #[[REAL8]]>
33+
! CHECK-DAG: #[[C1:.*]] = #llvm.di_local_variable<scope = #[[FN1]], name = "c1", file = #{{.*}}, line = [[@LINE+4]], arg = 3, type = #[[LOG1]]>
34+
! CHECK-DAG: #[[RES1:.*]] = #llvm.di_local_variable<scope = #[[FN1]], name = "res1", file = #{{.*}}, line = [[@LINE+4]], type = #[[INT8]]>
35+
integer(kind=4), intent(in) :: a1
36+
real(kind=8), intent(in) :: b1
37+
logical(kind=1), intent(in) :: c1
38+
integer(kind=8) :: res1
39+
res1 = a1 + b1
40+
end function
41+
42+
function fn2(a2, b2, c2) result (res2)
43+
implicit none
44+
! CHECK-DAG: #[[A2:.*]] = #llvm.di_local_variable<scope = #[[FN2]], name = "a2", file = #{{.*}}, line = [[@LINE+4]], arg = 1, type = #[[INT8]]>
45+
! CHECK-DAG: #[[B2:.*]] = #llvm.di_local_variable<scope = #[[FN2]], name = "b2", file = #{{.*}}, line = [[@LINE+4]], arg = 2, type = #[[REAL4]]>
46+
! CHECK-DAG: #[[C2:.*]] = #llvm.di_local_variable<scope = #[[FN2]], name = "c2", file = #{{.*}}, line = [[@LINE+4]], arg = 3, type = #[[LOG4]]>
47+
! CHECK-DAG: #[[RES2:.*]] = #llvm.di_local_variable<scope = #[[FN2]], name = "res2", file = #{{.*}}, line = [[@LINE+4]], type = #[[INT4]]>
48+
integer(kind=8), intent(in) :: a2
49+
real(kind=4), intent(in) :: b2
50+
logical(kind=4), intent(in) :: c2
51+
integer(kind=4) :: res2
52+
res2 = a2 + b2
53+
end function
54+
end program

0 commit comments

Comments
 (0)