diff --git a/flang/include/flang/Optimizer/Transforms/Passes.td b/flang/include/flang/Optimizer/Transforms/Passes.td index d9072e7aab4f7..8c082fb073451 100644 --- a/flang/include/flang/Optimizer/Transforms/Passes.td +++ b/flang/include/flang/Optimizer/Transforms/Passes.td @@ -267,9 +267,9 @@ def AddDebugInfo : Pass<"add-debug-info", "mlir::ModuleOp"> { Option<"dwarfDebugFlags", "dwarf-debug-flags", "std::string", /*default=*/"std::string{}", "Command-line flags to append to DWARF producer">, - Option<"emitFakeUseForArguments", "emit-fake-use-for-arguments", + Option<"emitFakeUseForDebugVars", "emit-fake-use-for-debug-vars", "bool", /*default=*/"false", - "Emit fake use for function arguments to extend their lifetime"> + "Emit fake use for debug variables to extend their lifetime"> ]; } diff --git a/flang/lib/Optimizer/Passes/Pipelines.cpp b/flang/lib/Optimizer/Passes/Pipelines.cpp index 2b80da308a7d4..8c3528b588a18 100644 --- a/flang/lib/Optimizer/Passes/Pipelines.cpp +++ b/flang/lib/Optimizer/Passes/Pipelines.cpp @@ -113,7 +113,7 @@ void addDebugInfoPass(mlir::PassManager &pm, options.dwarfVersion = config.DwarfVersion; options.splitDwarfFile = config.SplitDwarfFile; options.dwarfDebugFlags = config.DwarfDebugFlags; - options.emitFakeUseForArguments = + options.emitFakeUseForDebugVars = (config.OptLevel == llvm::OptimizationLevel::O0) && !disableArgumentFakeUse; addPassConditionally(pm, disableDebugInfo, diff --git a/flang/lib/Optimizer/Transforms/AddDebugInfo.cpp b/flang/lib/Optimizer/Transforms/AddDebugInfo.cpp index e0b570a908521..5aaf66baf9c9d 100644 --- a/flang/lib/Optimizer/Transforms/AddDebugInfo.cpp +++ b/flang/lib/Optimizer/Transforms/AddDebugInfo.cpp @@ -307,7 +307,7 @@ void AddDebugInfoPass::handleLocalVariable(Op declOp, llvm::StringRef name, if (dummyScope && declOp.getDummyScope() == dummyScope) { if (auto argNoOpt = declOp.getDummyArgNo()) { argNo = *argNoOpt; - if (emitFakeUseForArguments) { + if (emitFakeUseForDebugVars) { if constexpr (std::is_same_v) { if (auto funcOp = declOp->template getParentOfType()) { @@ -331,6 +331,60 @@ void AddDebugInfoPass::handleLocalVariable(Op declOp, llvm::StringRef name, auto tyAttr = typeGen.convertType(typeToConvert, fileAttr, scopeAttr, typeGenDeclOp); + // Check if tyAttr represents a dynamic Fortran array. If so, create + // FakeUseOps to preserve the automatic variables that represent the lower + // bound, count, and stride. + if (emitFakeUseForDebugVars) { + if (auto arrayTy = + mlir::dyn_cast(tyAttr)) { + if (arrayTy.getTag() == llvm::dwarf::DW_TAG_array_type) { + bool isDynamic = llvm::any_of( + arrayTy.getElements(), [](mlir::Attribute element) { + auto subrange = + mlir::dyn_cast(element); + if (!subrange) + return false; + return static_cast( + mlir::dyn_cast_if_present< + mlir::LLVM::DILocalVariableAttr>(subrange.getCount()) || + mlir::dyn_cast_if_present< + mlir::LLVM::DILocalVariableAttr>( + subrange.getLowerBound()) || + mlir::dyn_cast_if_present< + mlir::LLVM::DILocalVariableAttr>( + subrange.getStride())); + }); + if (isDynamic) { + if constexpr (std::is_same_v) { + if (auto funcOp = + declOp->template getParentOfType()) { + if (declOp->getBlock() == &funcOp.getBody().front()) { + for (mlir::Block &block : funcOp.getBody()) { + if (auto returnOp = mlir::dyn_cast( + block.getTerminator())) { + mlir::OpBuilder::InsertionGuard guard(builder); + builder.setInsertionPoint(returnOp); + // Preserve count if it is a variable and not a constant. + // Count is represented by shape in the declOp. + for (auto val : declOp.getShape()) + if (!fir::getIntIfConstant(val)) + fir::FakeUseOp::create(builder, declOp.getLoc(), val); + // Preserve lower bound if it is a variable and not a + // constant. + // Lower bound is represented by shift in the declOp. + for (auto val : declOp.getShift()) + if (!fir::getIntIfConstant(val)) + fir::FakeUseOp::create(builder, declOp.getLoc(), val); + } + } + } + } + } + } + } + } + } + auto localVarAttr = mlir::LLVM::DILocalVariableAttr::get( context, scopeAttr, mlir::StringAttr::get(context, name), fileAttr, getLineFromLoc(declOp.getLoc()), argNo, /* alignInBits*/ 0, tyAttr, diff --git a/flang/test/Transforms/debug-fake-use.fir b/flang/test/Transforms/debug-fake-use.fir index d86cc9b3afee4..e0aea80adf845 100644 --- a/flang/test/Transforms/debug-fake-use.fir +++ b/flang/test/Transforms/debug-fake-use.fir @@ -1,5 +1,5 @@ -// RUN: fir-opt --add-debug-info="emit-fake-use-for-arguments=true" %s | FileCheck %s --check-prefix=FAKE-USE -// RUN: fir-opt --add-debug-info="emit-fake-use-for-arguments=false" %s | FileCheck %s --check-prefix=NO-FAKE-USE +// RUN: fir-opt --add-debug-info="emit-fake-use-for-debug-vars=true" %s | FileCheck %s --check-prefix=FAKE-USE +// RUN: fir-opt --add-debug-info="emit-fake-use-for-debug-vars=false" %s | FileCheck %s --check-prefix=NO-FAKE-USE // FAKE-USE-LABEL: func.func @test_ // FAKE-USE: %[[UNDEF:.*]] = fir.undefined !fir.dscope @@ -31,6 +31,19 @@ // NO-FAKE-USE-NOT: fir.fake_use // NO-FAKE-USE: return +// FAKE-USE-LABEL: func.func @test_dynamic_array +// FAKE-USE: fircg.ext_declare %arg0(%[[COUNT:.*]]) origin %[[LB:.*]] dummy_scope +// FAKE-USE: fir.call @foo() : () -> () +// FAKE-USE: fir.fake_use %[[COUNT]] +// FAKE-USE: fir.fake_use %[[LB]] +// FAKE-USE: return + +// NO-FAKE-USE-LABEL: func.func @test_dynamic_array +// NO-FAKE-USE: fircg.ext_declare %arg0(%{{.*}}) origin %{{.*}} dummy_scope +// NO-FAKE-USE: fir.call @foo() : () -> () +// NO-FAKE-USE-NOT: fir.fake_use +// NO-FAKE-USE: return + #loc1 = loc("debug-fake-use.f90":1:1) #loc3 = loc("debug-fake-use.f90":3:14) #loc4 = loc("debug-fake-use.f90":4:14) @@ -53,4 +66,20 @@ module attributes {dlti.dl_spec = #dlti.dl_spec : vector<2xi64 fir.call @foo() : () -> () return loc(#loc5) } loc(#loc1) + + func.func @test_dynamic_array(%arg0: !fir.ref> {fir.bindc_name = "arr"} loc("debug-fake-use.f90":1:1), %arg1: !fir.ref {fir.bindc_name = "n"} loc("debug-fake-use.f90":1:1), %arg2: !fir.ref {fir.bindc_name = "lb"} loc("debug-fake-use.f90":1:1)) attributes {fir.internal_name = "_QPtest_dynamic_array"} { + %c0 = arith.constant 0 : index + %0 = fir.undefined !fir.dscope loc(#loc1) + %1 = fircg.ext_declare %arg1 dummy_scope %0 arg 2 {uniq_name = "_QFtest_dynamic_arrayEn"} : (!fir.ref, !fir.dscope) -> !fir.ref loc(#loc3) + %2 = fircg.ext_declare %arg2 dummy_scope %0 arg 3 {uniq_name = "_QFtest_dynamic_arrayElb"} : (!fir.ref, !fir.dscope) -> !fir.ref loc(#loc4) + %3 = fir.load %1 : !fir.ref + %4 = fir.convert %3 : (i32) -> index + %5 = arith.cmpi sgt, %4, %c0 : index + %6 = arith.select %5, %4, %c0 : index + %7 = fir.load %2 : !fir.ref + %8 = fir.convert %7 : (i32) -> index + %9 = fircg.ext_declare %arg0(%6) origin %8 dummy_scope %0 arg 1 {uniq_name = "_QFtest_dynamic_arrayEarr"} : (!fir.ref>, index, index, !fir.dscope) -> !fir.ref> loc(#loc3) + fir.call @foo() : () -> () + return loc(#loc5) + } loc(#loc1) } loc(#loc)