[flang-commits] [flang] [flang][debug] generate llvm.fake.use for arguments at -g and O0 (PR #187044)
via flang-commits
flang-commits at lists.llvm.org
Thu Apr 23 06:54:05 PDT 2026
https://github.com/jeanPerier updated https://github.com/llvm/llvm-project/pull/187044
>From ce58a291b82c7cb120b9e8b84bc08f3f76d29444 Mon Sep 17 00:00:00 2001
From: Jean Perier <jperier at nvidia.com>
Date: Tue, 17 Mar 2026 07:54:10 -0700
Subject: [PATCH] [flang][debug] generate llvm.fake.use for arguments at -g and
O0
---
.../include/flang/Optimizer/Dialect/FIROps.td | 10 ++++
.../flang/Optimizer/Transforms/Passes.td | 6 +-
flang/lib/Optimizer/CodeGen/CodeGen.cpp | 41 +++++++++-----
flang/lib/Optimizer/Passes/Pipelines.cpp | 8 +++
.../lib/Optimizer/Transforms/AddDebugInfo.cpp | 21 ++++++-
flang/test/Fir/fake_use-codegen.fir | 12 ++++
flang/test/Transforms/debug-fake-use.fir | 56 +++++++++++++++++++
7 files changed, 137 insertions(+), 17 deletions(-)
create mode 100644 flang/test/Fir/fake_use-codegen.fir
create mode 100644 flang/test/Transforms/debug-fake-use.fir
diff --git a/flang/include/flang/Optimizer/Dialect/FIROps.td b/flang/include/flang/Optimizer/Dialect/FIROps.td
index eba4ddda4b6ad..1af30684db4f6 100644
--- a/flang/include/flang/Optimizer/Dialect/FIROps.td
+++ b/flang/include/flang/Optimizer/Dialect/FIROps.td
@@ -567,6 +567,16 @@ def fir_ZeroOp : fir_OneResultOp<"zero_bits", [Pure]> {
let assemblyFormat = "type($intype) attr-dict";
}
+def fir_FakeUseOp : fir_Op<"fake_use", []> {
+ let summary = "fake use of values to extend their lifetime";
+ let description = [{
+ This operation is used to keep values alive for debug information purposes.
+ }];
+
+ let arguments = (ins Variadic<AnyType>:$args);
+ let assemblyFormat = "$args attr-dict `:` type($args)";
+}
+
//===----------------------------------------------------------------------===//
// Terminator operations
//===----------------------------------------------------------------------===//
diff --git a/flang/include/flang/Optimizer/Transforms/Passes.td b/flang/include/flang/Optimizer/Transforms/Passes.td
index 9ace5756e417a..e107672adf907 100644
--- a/flang/include/flang/Optimizer/Transforms/Passes.td
+++ b/flang/include/flang/Optimizer/Transforms/Passes.td
@@ -266,8 +266,10 @@ def AddDebugInfo : Pass<"add-debug-info", "mlir::ModuleOp"> {
"Name of the split dwarf file">,
Option<"dwarfDebugFlags", "dwarf-debug-flags",
"std::string", /*default=*/"std::string{}",
- "Command-line flags to append to DWARF producer">
-
+ "Command-line flags to append to DWARF producer">,
+ Option<"emitFakeUseForArguments", "emit-fake-use-for-arguments",
+ "bool", /*default=*/"false",
+ "Emit fake use for function arguments to extend their lifetime">
];
}
diff --git a/flang/lib/Optimizer/CodeGen/CodeGen.cpp b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
index 4986a20ab3123..bfec1a6d81c81 100644
--- a/flang/lib/Optimizer/CodeGen/CodeGen.cpp
+++ b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
@@ -4129,6 +4129,18 @@ struct ZeroOpConversion : public fir::FIROpConversion<fir::ZeroOp> {
}
};
+/// convert to LLVM IR dialect `fake_use`
+struct FakeUseOpConversion : public fir::FIROpConversion<fir::FakeUseOp> {
+ using FIROpConversion::FIROpConversion;
+
+ llvm::LogicalResult
+ matchAndRewrite(fir::FakeUseOp op, OpAdaptor adaptor,
+ mlir::ConversionPatternRewriter &rewriter) const override {
+ rewriter.replaceOpWithNewOp<mlir::LLVM::FakeUseOp>(op, adaptor.getArgs());
+ return mlir::success();
+ }
+};
+
/// `fir.unreachable` --> `llvm.unreachable`
struct UnreachableOpConversion
: public fir::FIROpConversion<fir::UnreachableOp> {
@@ -4815,20 +4827,21 @@ void fir::populateFIRToLLVMConversionPatterns(
DoConcurrentSpecifierOpConversion<fir::DeclareReductionOp>,
DivcOpConversion, EmboxOpConversion, EmboxCharOpConversion,
EmboxProcOpConversion, EqvOpConversion, ExtractValueOpConversion,
- FieldIndexOpConversion, FirEndOpConversion, FreeMemOpConversion,
- GlobalLenOpConversion, GlobalOpConversion, InsertOnRangeOpConversion,
- IsPresentOpConversion, LenParamIndexOpConversion, LoadOpConversion,
- LogicalAndOpConversion, LogicalOrOpConversion, MulcOpConversion,
- NegcOpConversion, NeqvOpConversion, NoReassocOpConversion,
- PrefetchOpConversion, SelectCaseOpConversion, SelectOpConversion,
- SelectRankOpConversion, SelectTypeOpConversion, ShapeOpConversion,
- ShapeShiftOpConversion, ShiftOpConversion, SliceOpConversion,
- StoreOpConversion, StringLitOpConversion, SubcOpConversion,
- TypeDescOpConversion, TypeInfoOpConversion, UnboxCharOpConversion,
- UnboxProcOpConversion, UndefOpConversion, UnreachableOpConversion,
- UseStmtOpConversion, ModuleDebugImportsOpConversion,
- XArrayCoorOpConversion, XEmboxOpConversion, XReboxOpConversion,
- ZeroOpConversion>(converter, options);
+ FakeUseOpConversion, FieldIndexOpConversion, FirEndOpConversion,
+ FreeMemOpConversion, GlobalLenOpConversion, GlobalOpConversion,
+ InsertOnRangeOpConversion, IsPresentOpConversion,
+ LenParamIndexOpConversion, LoadOpConversion, LogicalAndOpConversion,
+ LogicalOrOpConversion, MulcOpConversion, NegcOpConversion,
+ NeqvOpConversion, NoReassocOpConversion, PrefetchOpConversion,
+ SelectCaseOpConversion, SelectOpConversion, SelectRankOpConversion,
+ SelectTypeOpConversion, ShapeOpConversion, ShapeShiftOpConversion,
+ ShiftOpConversion, SliceOpConversion, StoreOpConversion,
+ StringLitOpConversion, SubcOpConversion, TypeDescOpConversion,
+ TypeInfoOpConversion, UnboxCharOpConversion, UnboxProcOpConversion,
+ UndefOpConversion, UnreachableOpConversion, UseStmtOpConversion,
+ ModuleDebugImportsOpConversion, XArrayCoorOpConversion,
+ XEmboxOpConversion, XReboxOpConversion, ZeroOpConversion>(converter,
+ options);
// Patterns that are populated without a type converter do not trigger
// target materializations for the operands of the root op.
diff --git a/flang/lib/Optimizer/Passes/Pipelines.cpp b/flang/lib/Optimizer/Passes/Pipelines.cpp
index 4c48b7a75d83d..48d60a7265534 100644
--- a/flang/lib/Optimizer/Passes/Pipelines.cpp
+++ b/flang/lib/Optimizer/Passes/Pipelines.cpp
@@ -16,6 +16,11 @@
static llvm::cl::opt<bool> forceNoAlias("force-no-alias", llvm::cl::Hidden,
llvm::cl::init(true));
+/// Disable the use of fake use for arguments.
+static llvm::cl::opt<bool> disableArgumentFakeUse("disable-argument-fake-use",
+ llvm::cl::Hidden,
+ llvm::cl::init(false));
+
namespace fir {
template <typename F>
@@ -104,6 +109,9 @@ void addDebugInfoPass(mlir::PassManager &pm,
options.dwarfVersion = config.DwarfVersion;
options.splitDwarfFile = config.SplitDwarfFile;
options.dwarfDebugFlags = config.DwarfDebugFlags;
+ options.emitFakeUseForArguments =
+ (config.OptLevel == llvm::OptimizationLevel::O0) &&
+ !disableArgumentFakeUse;
addPassConditionally(pm, disableDebugInfo,
[&]() { return fir::createAddDebugInfoPass(options); });
}
diff --git a/flang/lib/Optimizer/Transforms/AddDebugInfo.cpp b/flang/lib/Optimizer/Transforms/AddDebugInfo.cpp
index 9a53e7ff11771..65c8508de9c9f 100644
--- a/flang/lib/Optimizer/Transforms/AddDebugInfo.cpp
+++ b/flang/lib/Optimizer/Transforms/AddDebugInfo.cpp
@@ -305,8 +305,27 @@ void AddDebugInfoPass::handleLocalVariable(Op declOp, llvm::StringRef name,
// Get the dummy argument position from the explicit attribute.
unsigned argNo = 0;
if (dummyScope && declOp.getDummyScope() == dummyScope) {
- if (auto argNoOpt = declOp.getDummyArgNo())
+ if (auto argNoOpt = declOp.getDummyArgNo()) {
argNo = *argNoOpt;
+ if (emitFakeUseForArguments) {
+ if constexpr (std::is_same_v<Op, fir::cg::XDeclareOp>) {
+ if (auto funcOp =
+ declOp->template getParentOfType<mlir::func::FuncOp>()) {
+ if (declOp->getBlock() == &funcOp.getBody().front()) {
+ for (mlir::Block &block : funcOp.getBody()) {
+ if (auto returnOp = mlir::dyn_cast<mlir::func::ReturnOp>(
+ block.getTerminator())) {
+ mlir::OpBuilder::InsertionGuard guard(builder);
+ builder.setInsertionPoint(returnOp);
+ fir::FakeUseOp::create(builder, declOp.getLoc(),
+ declOp.getMemref());
+ }
+ }
+ }
+ }
+ }
+ }
+ }
}
auto tyAttr =
diff --git a/flang/test/Fir/fake_use-codegen.fir b/flang/test/Fir/fake_use-codegen.fir
new file mode 100644
index 0000000000000..a1f50cf7b495d
--- /dev/null
+++ b/flang/test/Fir/fake_use-codegen.fir
@@ -0,0 +1,12 @@
+// RUN: fir-opt --fir-to-llvm-ir %s | FileCheck %s
+
+// CHECK-LABEL: llvm.func @test_fake_use(
+// CHECK-SAME: %[[ARG0:.*]]: !llvm.ptr) {
+// CHECK: llvm.intr.fake.use %[[ARG0]] : !llvm.ptr
+// CHECK: llvm.return
+// CHECK: }
+
+func.func @test_fake_use(%arg0: !fir.ref<i32>) {
+ fir.fake_use %arg0 : !fir.ref<i32>
+ return
+}
diff --git a/flang/test/Transforms/debug-fake-use.fir b/flang/test/Transforms/debug-fake-use.fir
new file mode 100644
index 0000000000000..d86cc9b3afee4
--- /dev/null
+++ b/flang/test/Transforms/debug-fake-use.fir
@@ -0,0 +1,56 @@
+// 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
+
+// FAKE-USE-LABEL: func.func @test_
+// FAKE-USE: %[[UNDEF:.*]] = fir.undefined !fir.dscope
+// FAKE-USE: %[[DECL:.*]] = fircg.ext_declare %arg0 dummy_scope %[[UNDEF]] arg 1
+// FAKE-USE: fir.call @foo() : () -> ()
+// FAKE-USE: fir.fake_use %arg0
+// FAKE-USE: return
+
+// NO-FAKE-USE-LABEL: func.func @test_
+// NO-FAKE-USE: %[[UNDEF:.*]] = fir.undefined !fir.dscope
+// NO-FAKE-USE: %[[DECL:.*]] = fircg.ext_declare %arg0 dummy_scope %[[UNDEF]] arg 1
+// NO-FAKE-USE: fir.call @foo() : () -> ()
+// NO-FAKE-USE-NOT: fir.fake_use
+// NO-FAKE-USE: return
+
+// FAKE-USE-LABEL: func.func @test_local
+// FAKE-USE: %[[UNDEF:.*]] = fir.undefined !fir.dscope
+// FAKE-USE: %[[ALLOCA:.*]] = fir.alloca i32
+// FAKE-USE: %[[DECL_LOCAL:.*]] = fircg.ext_declare %[[ALLOCA]] dummy_scope %[[UNDEF]] {uniq_name = "_QFtestElocal"}
+// FAKE-USE: fir.call @foo() : () -> ()
+// FAKE-USE-NOT: fir.fake_use
+// FAKE-USE: return
+
+// NO-FAKE-USE-LABEL: func.func @test_local
+// NO-FAKE-USE: %[[UNDEF:.*]] = fir.undefined !fir.dscope
+// NO-FAKE-USE: %[[ALLOCA:.*]] = fir.alloca i32
+// NO-FAKE-USE: %[[DECL_LOCAL:.*]] = fircg.ext_declare %[[ALLOCA]] dummy_scope %[[UNDEF]] {uniq_name = "_QFtestElocal"}
+// 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)
+#loc5 = loc("debug-fake-use.f90":5:1)
+#loc = loc("debug-fake-use.f90":0:0)
+
+module attributes {dlti.dl_spec = #dlti.dl_spec<i128 = dense<128> : vector<2xi64>, f80 = dense<128> : vector<2xi64>, i1 = dense<8> : vector<2xi64>, !llvm.ptr<271> = dense<32> : vector<4xi64>, !llvm.ptr = dense<64> : vector<4xi64>, i64 = dense<64> : vector<2xi64>, !llvm.ptr<272> = dense<64> : vector<4xi64>, i32 = dense<32> : vector<2xi64>, i16 = dense<16> : vector<2xi64>, !llvm.ptr<270> = dense<32> : vector<4xi64>, i8 = dense<8> : vector<2xi64>, f128 = dense<128> : vector<2xi64>, f16 = dense<16> : vector<2xi64>, f64 = dense<64> : vector<2xi64>, "dlti.stack_alignment" = 128 : i64, "dlti.mangling_mode" = "e", "dlti.endianness" = "little">, fir.defaultkind = "a1c4d8i4l4r4", fir.kindmap = "", fir.target_cpu = "x86-64", llvm.data_layout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128", llvm.ident = "flang", llvm.target_triple = "x86_64-unknown-linux-gnu"} {
+ func.func private @foo()
+ func.func @test_(%arg0: !fir.ref<i32> {fir.bindc_name = "expected"} loc("debug-fake-use.f90":1:1)) attributes {fir.internal_name = "_QPtest"} {
+ %0 = fir.undefined !fir.dscope loc(#loc1)
+ %1 = fircg.ext_declare %arg0 dummy_scope %0 arg 1 {uniq_name = "_QFtestEexpected"} : (!fir.ref<i32>, !fir.dscope) -> !fir.ref<i32> loc(#loc3)
+ fir.call @foo() : () -> ()
+ return loc(#loc5)
+ } loc(#loc1)
+
+ func.func @test_local() attributes {fir.internal_name = "_QPtest_local"} {
+ %0 = fir.undefined !fir.dscope loc(#loc1)
+ %1 = fir.alloca i32 loc(#loc4)
+ %2 = fircg.ext_declare %1 dummy_scope %0 {uniq_name = "_QFtestElocal"} : (!fir.ref<i32>, !fir.dscope) -> !fir.ref<i32> loc(#loc4)
+ fir.call @foo() : () -> ()
+ return loc(#loc5)
+ } loc(#loc1)
+} loc(#loc)
More information about the flang-commits
mailing list