[flang][debug] handle inlined dummy_scope after #167489 (#168039)

#167489 did not work properly when MLIR inlining is enabled
(experimental in flang, enabled with `-mllvm -inline-all`).

The reason is that inlining will cause several `fir.dummy_scope` to
coexist inside a same `func.func` (`fir.dummy_scope` of inlined
`func.func` are preserved in order to preserve the relationship between
arguments of the inlined call for better aliasing deductions).

After #167489, the debug info pass creates argument debug info for all
fir.declare with a fir.dummy_scope. This causes arguments from inlined
calls to appear as argument of the procedure where the call was inlined.

To avoid this, only consider that fir.declare are arguments of the
current function if their fir.dummy_scope is the first one created in
the function (fir.dummy_scope cannot be reorder because they have write
effects to the debug memory ressource, and the fir.dummy_scope of the
current functions is always emitted before any calls are lowered, so
before any fir.dummy_scope are inlined).
This commit is contained in:
jeanPerier 2025-11-14 12:57:05 +01:00 committed by GitHub
parent 5829bb9476
commit 778b85ae03
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 56 additions and 4 deletions

View File

@ -53,7 +53,7 @@ class AddDebugInfoPass : public fir::impl::AddDebugInfoBase<AddDebugInfoPass> {
mlir::LLVM::DIFileAttr fileAttr,
mlir::LLVM::DIScopeAttr scopeAttr,
fir::DebugTypeGenerator &typeGen,
mlir::SymbolTable *symbolTable);
mlir::SymbolTable *symbolTable, mlir::Value dummyScope);
public:
AddDebugInfoPass(fir::AddDebugInfoOptions options) : Base(options) {}
@ -206,7 +206,8 @@ void AddDebugInfoPass::handleDeclareOp(fir::cg::XDeclareOp declOp,
mlir::LLVM::DIFileAttr fileAttr,
mlir::LLVM::DIScopeAttr scopeAttr,
fir::DebugTypeGenerator &typeGen,
mlir::SymbolTable *symbolTable) {
mlir::SymbolTable *symbolTable,
mlir::Value dummyScope) {
mlir::MLIRContext *context = &getContext();
mlir::OpBuilder builder(context);
auto result = fir::NameUniquer::deconstruct(declOp.getUniqName());
@ -230,7 +231,7 @@ void AddDebugInfoPass::handleDeclareOp(fir::cg::XDeclareOp declOp,
// Get the dummy argument position from the explicit attribute.
unsigned argNo = 0;
if (declOp.getDummyScope()) {
if (dummyScope && declOp.getDummyScope() == dummyScope) {
if (auto argNoOpt = declOp.getDummyArgNo())
argNo = *argNoOpt;
}
@ -610,6 +611,21 @@ void AddDebugInfoPass::handleFuncOp(mlir::func::FuncOp funcOp,
funcOp->setLoc(builder.getFusedLoc({l}, spAttr));
addTargetOpDISP(/*lineTableOnly=*/false, entities);
// Find the first dummy_scope definition. This is the one of the current
// function. The other ones may come from inlined calls. The variables inside
// those inlined calls should not be identified as arguments of the current
// function.
mlir::Value dummyScope;
funcOp.walk([&](fir::UndefOp undef) -> mlir::WalkResult {
// TODO: delay fir.dummy_scope translation to undefined until
// codegeneration. This is nicer and safer to match.
if (llvm::isa<fir::DummyScopeType>(undef.getType())) {
dummyScope = undef;
return mlir::WalkResult::interrupt();
}
return mlir::WalkResult::advance();
});
funcOp.walk([&](fir::cg::XDeclareOp declOp) {
mlir::LLVM::DISubprogramAttr spTy = spAttr;
if (auto tOp = declOp->getParentOfType<mlir::omp::TargetOp>()) {
@ -619,7 +635,7 @@ void AddDebugInfoPass::handleFuncOp(mlir::func::FuncOp funcOp,
spTy = sp;
}
}
handleDeclareOp(declOp, fileAttr, spTy, typeGen, symbolTable);
handleDeclareOp(declOp, fileAttr, spTy, typeGen, symbolTable, dummyScope);
});
// commonBlockMap ensures that we don't create multiple DICommonBlockAttr of
// the same name in one function. But it is ok (rather required) to create

View File

@ -0,0 +1,36 @@
// Tests that inlined calls arguments are not mistaken for the arguments of the
// procedure where the calls were inlined.
// RUN: fir-opt --add-debug-info --mlir-print-debuginfo %s -o - | FileCheck %s
// CHECK: #di_local_variable = #llvm.di_local_variable<scope = #di_subprogram, name = "i", file = #di_file, line = 6, arg = 1, type = #di_basic_type>
// CHECK: #di_local_variable1 = #llvm.di_local_variable<scope = #di_subprogram, name = "i", file = #di_file, line = 1, type = #di_basic_type>
func.func @foo_(%arg0: !fir.ref<i32> {fir.bindc_name = "i"} loc("debug-dummy-argument-inline.f90":5:1)) attributes {fir.internal_name = "_QPfoo"} {
%0 = fir.undefined !fir.dscope loc(#loc5)
%1 = fircg.ext_declare %arg0 dummy_scope %0 arg 1 {uniq_name = "_QFfooEi"} : (!fir.ref<i32>, !fir.dscope) -> !fir.ref<i32> loc(#loc6)
%2 = fir.undefined !fir.dscope loc(#loc11)
%3 = fircg.ext_declare %1 dummy_scope %2 arg 1 {uniq_name = "_QFbarEi"} : (!fir.ref<i32>, !fir.dscope) -> !fir.ref<i32> loc(#loc12)
fir.call @buzz_(%3) fastmath<contract> : (!fir.ref<i32>) -> () loc(#loc13)
%4 = fir.undefined !fir.dscope loc(#loc14)
%5 = fircg.ext_declare %1 dummy_scope %4 arg 1 {uniq_name = "_QFbarEi"} : (!fir.ref<i32>, !fir.dscope) -> !fir.ref<i32> loc(#loc15)
fir.call @buzz_(%5) fastmath<contract> : (!fir.ref<i32>) -> () loc(#loc16)
return loc(#loc9)
} loc(#loc5)
func.func private @buzz_(!fir.ref<i32>) attributes {fir.internal_name = "_QPbuzz"} loc(#loc10)
#loc = loc("debug-dummy-argument-inline.f90":0:0)
#loc1 = loc("debug-dummy-argument-inline.f90":1:1)
#loc2 = loc("debug-dummy-argument-inline.f90":2:14)
#loc3 = loc("debug-dummy-argument-inline.f90":3:3)
#loc4 = loc("debug-dummy-argument-inline.f90":4:1)
#loc5 = loc("debug-dummy-argument-inline.f90":5:1)
#loc6 = loc("debug-dummy-argument-inline.f90":6:14)
#loc7 = loc("debug-dummy-argument-inline.f90":7:3)
#loc8 = loc("debug-dummy-argument-inline.f90":8:3)
#loc9 = loc("debug-dummy-argument-inline.f90":9:1)
#loc10 = loc("debug-dummy-argument-inline.f90":3:8)
#loc11 = loc(callsite(#loc1 at #loc7))
#loc12 = loc(callsite(#loc2 at #loc7))
#loc13 = loc(callsite(#loc3 at #loc7))
#loc14 = loc(callsite(#loc1 at #loc8))
#loc15 = loc(callsite(#loc2 at #loc8))
#loc16 = loc(callsite(#loc3 at #loc8))