[flang-commits] [flang] [flang] Represent use statement in fir. (PR #168106)

Abid Qadeer via flang-commits flang-commits at lists.llvm.org
Fri Jan 2 03:28:08 PST 2026


https://github.com/abidh updated https://github.com/llvm/llvm-project/pull/168106

>From 14889be61350da92f4e16f784d1b8f96e159abc8 Mon Sep 17 00:00:00 2001
From: Abid Qadeer <haqadeer at amd.com>
Date: Fri, 14 Nov 2025 17:46:50 +0000
Subject: [PATCH 1/4] [flang] Represent use statement in fir.

This patch adds infrastructure to emit Fortran USE statement
information in FIR, which will be used by subsequent patches to generate
DWARF debug information.

The information about use statement is collected during semantic
analysis and stored in PreservedUseStmt objects. During
lowering, fir.use_stmt operations are emitted for each PreservedUseStmt
object. The fir.use_stmt operation captures the module name, ONLY list
symbols, and any renames specified in the USE statement.
---
 .../flang/Optimizer/Dialect/FIRAttr.td        | 17 ++++
 .../include/flang/Optimizer/Dialect/FIROps.td | 52 +++++++++++
 flang/include/flang/Semantics/scope.h         | 25 +++++
 flang/lib/Lower/Bridge.cpp                    | 92 +++++++++++++++++++
 flang/lib/Optimizer/CodeGen/CodeGen.cpp       | 19 +++-
 flang/lib/Semantics/resolve-names.cpp         | 80 ++++++++++++++++
 .../test/Lower/debug-use-stmt-symbol-refs.f90 | 59 ++++++++++++
 7 files changed, 342 insertions(+), 2 deletions(-)
 create mode 100644 flang/test/Lower/debug-use-stmt-symbol-refs.f90

diff --git a/flang/include/flang/Optimizer/Dialect/FIRAttr.td b/flang/include/flang/Optimizer/Dialect/FIRAttr.td
index 5e3185480a7eb..2fbe195ee9f33 100644
--- a/flang/include/flang/Optimizer/Dialect/FIRAttr.td
+++ b/flang/include/flang/Optimizer/Dialect/FIRAttr.td
@@ -243,4 +243,21 @@ def fir_FortranInlineAttr
     : EnumAttr<FIROpsDialect, fir_FortranInlineEnum, "inline_attrs"> {
   let assemblyFormat = "`<` $value `>`";
 }
+
+// USE statement rename mapping: local_name => use_name
+def fir_UseRenameAttr : fir_Attr<"UseRename"> {
+  let mnemonic = "use_rename";
+  let summary = "Represents a rename in a Fortran USE statement";
+  let description = [{
+    This attribute stores the mapping for a renamed symbol in a USE statement.
+    For example, in "USE mod, local_var => module_var", this stores the
+    local name and a symbol reference to the module variable.
+  }];
+
+  let parameters = (ins "mlir::StringAttr":$local_name,
+      "mlir::FlatSymbolRefAttr":$symbol);
+
+  let assemblyFormat = "`<` $local_name `,` $symbol `>`";
+}
+
 #endif // FIR_DIALECT_FIR_ATTRS
diff --git a/flang/include/flang/Optimizer/Dialect/FIROps.td b/flang/include/flang/Optimizer/Dialect/FIROps.td
index 7bfb304b973f5..126de599336a9 100644
--- a/flang/include/flang/Optimizer/Dialect/FIROps.td
+++ b/flang/include/flang/Optimizer/Dialect/FIROps.td
@@ -3102,6 +3102,58 @@ def fir_GlobalLenOp : fir_Op<"global_len", []> {
   }];
 }
 
+def fir_UseStmtOp
+    : fir_Op<"use_stmt", [MemoryEffects<[MemWrite<DebuggingResource>]>]> {
+  let summary = "Represents a Fortran USE statement";
+  let description = [{
+    This operation records a Fortran USE statement with its associated only/rename
+    information. It has no runtime effect but preserves semantic information for
+    debug information generation.
+
+    The operation captures:
+    - The module being used (via module_name string)
+    - Symbol references to symbols imported via the ONLY clause (if present)
+    - Symbol renames (local_name and symbol reference)
+
+    Examples:
+    ```
+      // USE mod1
+      fir.use_stmt "mod1"
+
+      // USE mod1, ONLY: var2
+      fir.use_stmt "mod1" only_symbols [@_QMmod1Evar2]
+
+      // USE mod2, var4 => var3
+      fir.use_stmt "mod2" renames [#fir.use_rename<"var4", @_QMmod2Evar3>]
+
+      // USE mod2, ONLY: var1, renamed => original
+      fir.use_stmt "mod2" only_symbols [@_QMmod2Evar1]
+                   renames [#fir.use_rename<"renamed", @_QMmod2Eoriginal>]
+    ```
+  }];
+
+  let arguments = (ins StrAttr:$module_name,
+      OptionalAttr<ArrayAttr>:$only_symbols, OptionalAttr<ArrayAttr>:$renames);
+
+  let assemblyFormat = [{
+    $module_name
+    (`only_symbols` `[` $only_symbols^ `]`)?
+    (`renames` `[` $renames^ `]`)?
+    attr-dict
+  }];
+
+  let extraClassDeclaration = [{
+    /// Returns true if this is a USE with ONLY clause
+    bool hasOnlyClause() { return getOnlySymbols().has_value(); }
+
+    /// Returns true if this has any renames
+    bool hasRenames() { return getRenames().has_value(); }
+
+    /// Returns true if this imports the entire module (no ONLY clause)
+    bool importsAll() { return !hasOnlyClause(); }
+  }];
+}
+
 def ImplicitFirTerminator : SingleBlockImplicitTerminator<"FirEndOp">;
 
 def fir_TypeInfoOp : fir_Op<"type_info",
diff --git a/flang/include/flang/Semantics/scope.h b/flang/include/flang/Semantics/scope.h
index ecffdb468bf6c..586659781491b 100644
--- a/flang/include/flang/Semantics/scope.h
+++ b/flang/include/flang/Semantics/scope.h
@@ -55,6 +55,19 @@ struct EquivalenceObject {
 };
 using EquivalenceSet = std::vector<EquivalenceObject>;
 
+// Preserved USE statement information for debug info generation.
+struct PreservedUseStmt {
+  enum class Kind { UseOnly, UseRenames, UseAll };
+
+  std::string moduleName;
+  Kind kind;
+  std::vector<std::string> onlyNames; // For Kind::UseOnly
+  std::vector<std::string> renames; // local_name (resolved via GetUltimate)
+
+  PreservedUseStmt(std::string modName, Kind k)
+      : moduleName(std::move(modName)), kind(k) {}
+};
+
 class Scope {
   using mapType = std::map<SourceName, MutableSymbolRef>;
 
@@ -190,6 +203,17 @@ class Scope {
     return equivalenceSets_;
   }
   void add_equivalenceSet(EquivalenceSet &&);
+
+  // Access preserved USE statements for debug info generation
+  std::list<PreservedUseStmt> &preservedUseStmts() {
+    return preservedUseStmts_;
+  }
+  const std::list<PreservedUseStmt> &preservedUseStmts() const {
+    return preservedUseStmts_;
+  }
+  void add_preservedUseStmt(PreservedUseStmt &&stmt) {
+    preservedUseStmts_.push_back(std::move(stmt));
+  }
   // Cray pointers are saved as map of pointee name -> pointer symbol
   const mapType &crayPointers() const { return crayPointers_; }
   void add_crayPointer(const SourceName &, Symbol &);
@@ -301,6 +325,7 @@ class Scope {
   mapType commonBlocks_;
   mapType commonBlockUses_; // USE-assocated COMMON blocks
   std::list<EquivalenceSet> equivalenceSets_;
+  std::list<PreservedUseStmt> preservedUseStmts_;
   mapType crayPointers_;
   std::map<SourceName, common::Reference<Scope>> submodules_;
   std::list<DeclTypeSpec> declTypeSpecs_;
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index d175e2a8a73cb..b7a5d7bc51426 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -222,6 +222,95 @@ static mlir::FlatSymbolRefAttr gatherComponentInit(
   return mlir::FlatSymbolRefAttr::get(mlirContext, name);
 }
 
+/// Emit fir.use_stmt operations for USE statements in the given scope
+static void
+emitUseStatementsFromScope(Fortran::lower::AbstractConverter &converter,
+                           mlir::OpBuilder &builder, mlir::Location loc,
+                           const Fortran::semantics::Scope &scope) {
+  mlir::MLIRContext *context = builder.getContext();
+
+  for (const auto &preservedStmt : scope.preservedUseStmts()) {
+
+    auto getMangledName = [&](const std::string &localName) -> std::string {
+      Fortran::parser::CharBlock charBlock{localName.data(), localName.size()};
+      const auto *sym = scope.FindSymbol(charBlock);
+      if (!sym)
+        return "";
+
+      const auto &ultimateSym = sym->GetUltimate();
+
+      // Skip cases which can cause mangleName to fail.
+      if (ultimateSym.has<Fortran::semantics::DerivedTypeDetails>())
+        return "";
+
+      if (const auto *generic =
+              ultimateSym.detailsIf<Fortran::semantics::GenericDetails>()) {
+        if (!generic->specific())
+          return "";
+      }
+
+      return converter.mangleName(ultimateSym);
+    };
+
+    mlir::StringAttr moduleNameAttr =
+        mlir::StringAttr::get(context, preservedStmt.moduleName);
+
+    llvm::SmallVector<mlir::Attribute> onlySymbolAttrs;
+    llvm::SmallVector<mlir::Attribute> renameAttrs;
+
+    switch (preservedStmt.kind) {
+    case Fortran::semantics::PreservedUseStmt::Kind::UseOnly:
+      // USE mod, ONLY: list
+      for (const auto &name : preservedStmt.onlyNames) {
+        std::string mangledName = getMangledName(name);
+        if (!mangledName.empty())
+          onlySymbolAttrs.push_back(
+              mlir::FlatSymbolRefAttr::get(context, mangledName));
+      }
+      // Handle renames within ONLY clause
+      for (const auto &local : preservedStmt.renames) {
+        std::string mangledName = getMangledName(local);
+        if (!mangledName.empty()) {
+          auto localAttr = mlir::StringAttr::get(context, local);
+          auto symbolRef = mlir::FlatSymbolRefAttr::get(context, mangledName);
+          renameAttrs.push_back(
+              fir::UseRenameAttr::get(context, localAttr, symbolRef));
+        }
+      }
+      break;
+
+    case Fortran::semantics::PreservedUseStmt::Kind::UseRenames:
+      // USE mod, renames (import all with some renames)
+      for (const auto &local : preservedStmt.renames) {
+        std::string mangledName = getMangledName(local);
+        if (!mangledName.empty()) {
+          auto localAttr = mlir::StringAttr::get(context, local);
+          auto symbolRef = mlir::FlatSymbolRefAttr::get(context, mangledName);
+          renameAttrs.push_back(
+              fir::UseRenameAttr::get(context, localAttr, symbolRef));
+        }
+      }
+      break;
+
+    case Fortran::semantics::PreservedUseStmt::Kind::UseAll:
+      // USE mod (import all, no renames)
+      break;
+    }
+
+    // Create optional array attributes
+    mlir::ArrayAttr onlySymbolsAttr =
+        onlySymbolAttrs.empty()
+            ? mlir::ArrayAttr()
+            : mlir::ArrayAttr::get(context, onlySymbolAttrs);
+    mlir::ArrayAttr renamesAttr =
+        renameAttrs.empty() ? mlir::ArrayAttr()
+                            : mlir::ArrayAttr::get(context, renameAttrs);
+
+    fir::UseStmtOp::create(builder, loc, moduleNameAttr, onlySymbolsAttr,
+                           renamesAttr);
+  }
+}
+
 /// Helper class to generate the runtime type info global data and the
 /// fir.type_info operations that contain the dipatch tables (if any).
 /// The type info global data is required to describe the derived type to the
@@ -6285,6 +6374,9 @@ class FirConverter : public Fortran::lower::AbstractConverter {
 
     mapDummiesAndResults(funit, callee);
 
+    // Emit USE statement operations for debug info generation
+    emitUseStatementsFromScope(*this, *builder, toLocation(), funit.getScope());
+
     // Map host associated symbols from parent procedure if any.
     if (funit.parentHasHostAssoc())
       funit.parentHostAssoc().internalProcedureBindings(*this, localSymbols);
diff --git a/flang/lib/Optimizer/CodeGen/CodeGen.cpp b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
index 55e885d46d351..6dff368b68254 100644
--- a/flang/lib/Optimizer/CodeGen/CodeGen.cpp
+++ b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
@@ -3462,6 +3462,20 @@ struct NoReassocOpConversion : public fir::FIROpConversion<fir::NoReassocOp> {
   }
 };
 
+/// Erase `fir.use_stmt` operations during LLVM lowering.
+/// These operations are only used for debug info generation by the
+/// AddDebugInfo pass and have no runtime representation.
+struct UseStmtOpConversion : public fir::FIROpConversion<fir::UseStmtOp> {
+  using FIROpConversion::FIROpConversion;
+
+  llvm::LogicalResult
+  matchAndRewrite(fir::UseStmtOp useStmt, OpAdaptor adaptor,
+                  mlir::ConversionPatternRewriter &rewriter) const override {
+    rewriter.eraseOp(useStmt);
+    return mlir::success();
+  }
+};
+
 static void genCondBrOp(mlir::Location loc, mlir::Value cmp, mlir::Block *dest,
                         std::optional<mlir::ValueRange> destOps,
                         mlir::ConversionPatternRewriter &rewriter,
@@ -4449,8 +4463,9 @@ void fir::populateFIRToLLVMConversionPatterns(
       SliceOpConversion, StoreOpConversion, StringLitOpConversion,
       SubcOpConversion, TypeDescOpConversion, TypeInfoOpConversion,
       UnboxCharOpConversion, UnboxProcOpConversion, UndefOpConversion,
-      UnreachableOpConversion, XArrayCoorOpConversion, XEmboxOpConversion,
-      XReboxOpConversion, ZeroOpConversion>(converter, options);
+      UnreachableOpConversion, UseStmtOpConversion, 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/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index ea2a3e4fb053d..7a1cac98e90af 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -3663,6 +3663,86 @@ void ModuleVisitor::Post(const parser::UseStmt &x) {
   for (const auto &[name, symbol] : useModuleScope_->commonBlockUses()) {
     currScope().AddCommonBlockUse(name, symbol->attrs(), symbol->GetUltimate());
   }
+
+  // Preserve USE statement information for debug info generation
+  std::string moduleName{x.moduleName.source.ToString()};
+
+  if (const auto *onlyList{std::get_if<std::list<parser::Only>>(&x.u)}) {
+    // USE mod, ONLY: list
+    PreservedUseStmt stmt{moduleName, PreservedUseStmt::Kind::UseOnly};
+
+    for (const auto &only : *onlyList) {
+      common::visit(
+          common::visitors{
+              [&](const parser::Rename &rename) {
+                // ONLY with rename: ONLY: local => use
+                common::visit(common::visitors{
+                                  [&](const parser::Rename::Names &names) {
+                                    std::string localName{
+                                        std::get<0>(names.t).source.ToString()};
+                                    stmt.renames.push_back(localName);
+                                  },
+                                  [&](const parser::Rename::Operators &) {
+                                    // Operator renames - not commonly needed
+                                    // for debug info
+                                  },
+                              },
+                    rename.u);
+              },
+              [&](const parser::Name &name) {
+                // ONLY without rename: ONLY: name
+                stmt.onlyNames.push_back(name.source.ToString());
+              },
+              [&](const common::Indirection<parser::GenericSpec> &genericSpec) {
+                // Generic spec can contain a Name (for regular symbols) or
+                // operators
+                common::visit(common::visitors{
+                                  [&](const parser::Name &name) {
+                                    stmt.onlyNames.push_back(
+                                        name.source.ToString());
+                                  },
+                                  [&](const auto &) {
+                                    // Operators and special forms - not
+                                    // commonly needed for variable debug info
+                                  },
+                              },
+                    genericSpec.value().u);
+              },
+          },
+          only.u);
+    }
+
+    currScope().add_preservedUseStmt(std::move(stmt));
+  } else if (const auto *renameList{
+                 std::get_if<std::list<parser::Rename>>(&x.u)}) {
+    // USE mod with optional renames (not ONLY)
+    if (renameList->empty()) {
+      // USE mod (import all, no renames)
+      PreservedUseStmt stmt{moduleName, PreservedUseStmt::Kind::UseAll};
+      currScope().add_preservedUseStmt(std::move(stmt));
+    } else {
+      // USE mod, renames (import all with some renames)
+      PreservedUseStmt stmt{moduleName, PreservedUseStmt::Kind::UseRenames};
+
+      for (const auto &rename : *renameList) {
+        common::visit(common::visitors{
+                          [&](const parser::Rename::Names &names) {
+                            std::string localName{
+                                std::get<0>(names.t).source.ToString()};
+                            stmt.renames.push_back(localName);
+                          },
+                          [&](const parser::Rename::Operators &) {
+                            // Operator renames - not commonly needed for debug
+                            // info
+                          },
+                      },
+            rename.u);
+      }
+
+      currScope().add_preservedUseStmt(std::move(stmt));
+    }
+  }
+
   useModuleScope_ = nullptr;
 }
 
diff --git a/flang/test/Lower/debug-use-stmt-symbol-refs.f90 b/flang/test/Lower/debug-use-stmt-symbol-refs.f90
new file mode 100644
index 0000000000000..0f2c1db58740b
--- /dev/null
+++ b/flang/test/Lower/debug-use-stmt-symbol-refs.f90
@@ -0,0 +1,59 @@
+! RUN: bbc -emit-fir %s -o - | FileCheck %s
+
+! Test USE statement lowering to fir.use_stmt operations
+! Covers: USE ONLY, USE with renames, and USE (all)
+
+module mod1
+  integer :: a = 10, b = 20, c = 30
+end module mod1
+
+module mod2
+  real :: x = 1.0, y = 2.0, z = 3.0
+end module mod2
+
+module mod3
+  logical :: flag = .true.
+end module mod3
+
+! Test 1: Program with USE ONLY and USE with renames
+program test_main
+  use mod1, only: b, c
+  use mod2, renamed_y => y
+  implicit none
+  print *, b, c, renamed_y
+end program
+
+! Test 2: Subroutine with USE (all) and different renames
+subroutine test_sub()
+  use mod1
+  use mod2, only: x
+  use mod3, my_flag => flag
+  implicit none
+  print *, a, b, c, x, my_flag
+end subroutine
+
+! Test 3: Function with multiple USE patterns
+function test_func() result(res)
+  use mod1, only: a
+  use mod2, renamed_x => x, renamed_z => z
+  use mod3
+  implicit none
+  integer :: res
+  res = a
+end function
+
+! CHECK-LABEL: func.func @_QQmain()
+! CHECK-DAG: fir.use_stmt "mod1" only_symbols{{\[}}[@_QMmod1Eb, @_QMmod1Ec]]
+! CHECK-DAG: fir.use_stmt "mod2" renames{{\[}}[#fir.use_rename<"renamed_y", @_QMmod2Ey>]]
+
+! CHECK-LABEL: func.func @_QPtest_sub()
+! CHECK-DAG: fir.use_stmt "mod1"{{$}}
+! CHECK-DAG: fir.use_stmt "mod2" only_symbols{{\[}}[@_QMmod2Ex]]
+! CHECK-DAG: fir.use_stmt "mod3" renames{{\[}}[#fir.use_rename<"my_flag", @_QMmod3Eflag>]]
+
+! CHECK-LABEL: func.func @_QPtest_func()
+! CHECK-DAG: fir.use_stmt "mod1" only_symbols{{\[}}[@_QMmod1Ea]]
+! CHECK-DAG: fir.use_stmt "mod2" renames{{\[}}[#fir.use_rename<"renamed_x", @_QMmod2Ex>, #fir.use_rename<"renamed_z", @_QMmod2Ez>]]
+! CHECK-DAG: fir.use_stmt "mod3"{{$}}
+
+

>From 751a06e9a78dcd6564c6c10425747c600edecf2c Mon Sep 17 00:00:00 2001
From: Abid Qadeer <haqadeer at amd.com>
Date: Mon, 15 Dec 2025 14:23:58 +0000
Subject: [PATCH 2/4] Handle review comments.

1. Removed enum Kind.
2. Simplified the code accordingly.
3. Removed some extra empty lines from the testcase.
---
 flang/include/flang/Semantics/scope.h         |  6 +--
 flang/lib/Lower/Bridge.cpp                    | 52 ++++++-------------
 flang/lib/Semantics/resolve-names.cpp         |  6 +--
 .../test/Lower/debug-use-stmt-symbol-refs.f90 |  2 -
 4 files changed, 20 insertions(+), 46 deletions(-)

diff --git a/flang/include/flang/Semantics/scope.h b/flang/include/flang/Semantics/scope.h
index 586659781491b..1ad5e71a8e9c7 100644
--- a/flang/include/flang/Semantics/scope.h
+++ b/flang/include/flang/Semantics/scope.h
@@ -57,15 +57,11 @@ using EquivalenceSet = std::vector<EquivalenceObject>;
 
 // Preserved USE statement information for debug info generation.
 struct PreservedUseStmt {
-  enum class Kind { UseOnly, UseRenames, UseAll };
-
   std::string moduleName;
-  Kind kind;
   std::vector<std::string> onlyNames; // For Kind::UseOnly
   std::vector<std::string> renames; // local_name (resolved via GetUltimate)
 
-  PreservedUseStmt(std::string modName, Kind k)
-      : moduleName(std::move(modName)), kind(k) {}
+  PreservedUseStmt(std::string modName) : moduleName(std::move(modName)) {}
 };
 
 class Scope {
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index b7a5d7bc51426..b78be6d1aa504 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -258,43 +258,23 @@ emitUseStatementsFromScope(Fortran::lower::AbstractConverter &converter,
     llvm::SmallVector<mlir::Attribute> onlySymbolAttrs;
     llvm::SmallVector<mlir::Attribute> renameAttrs;
 
-    switch (preservedStmt.kind) {
-    case Fortran::semantics::PreservedUseStmt::Kind::UseOnly:
-      // USE mod, ONLY: list
-      for (const auto &name : preservedStmt.onlyNames) {
-        std::string mangledName = getMangledName(name);
-        if (!mangledName.empty())
-          onlySymbolAttrs.push_back(
-              mlir::FlatSymbolRefAttr::get(context, mangledName));
+    // USE mod, ONLY: list
+    for (const auto &name : preservedStmt.onlyNames) {
+      std::string mangledName = getMangledName(name);
+      if (!mangledName.empty())
+        onlySymbolAttrs.push_back(
+            mlir::FlatSymbolRefAttr::get(context, mangledName));
+    }
+
+    // Handle renames
+    for (const auto &local : preservedStmt.renames) {
+      std::string mangledName = getMangledName(local);
+      if (!mangledName.empty()) {
+        auto localAttr = mlir::StringAttr::get(context, local);
+        auto symbolRef = mlir::FlatSymbolRefAttr::get(context, mangledName);
+        renameAttrs.push_back(
+            fir::UseRenameAttr::get(context, localAttr, symbolRef));
       }
-      // Handle renames within ONLY clause
-      for (const auto &local : preservedStmt.renames) {
-        std::string mangledName = getMangledName(local);
-        if (!mangledName.empty()) {
-          auto localAttr = mlir::StringAttr::get(context, local);
-          auto symbolRef = mlir::FlatSymbolRefAttr::get(context, mangledName);
-          renameAttrs.push_back(
-              fir::UseRenameAttr::get(context, localAttr, symbolRef));
-        }
-      }
-      break;
-
-    case Fortran::semantics::PreservedUseStmt::Kind::UseRenames:
-      // USE mod, renames (import all with some renames)
-      for (const auto &local : preservedStmt.renames) {
-        std::string mangledName = getMangledName(local);
-        if (!mangledName.empty()) {
-          auto localAttr = mlir::StringAttr::get(context, local);
-          auto symbolRef = mlir::FlatSymbolRefAttr::get(context, mangledName);
-          renameAttrs.push_back(
-              fir::UseRenameAttr::get(context, localAttr, symbolRef));
-        }
-      }
-      break;
-
-    case Fortran::semantics::PreservedUseStmt::Kind::UseAll:
-      // USE mod (import all, no renames)
-      break;
     }
 
     // Create optional array attributes
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index 7a1cac98e90af..77b444c1a3898 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -3669,7 +3669,7 @@ void ModuleVisitor::Post(const parser::UseStmt &x) {
 
   if (const auto *onlyList{std::get_if<std::list<parser::Only>>(&x.u)}) {
     // USE mod, ONLY: list
-    PreservedUseStmt stmt{moduleName, PreservedUseStmt::Kind::UseOnly};
+    PreservedUseStmt stmt{moduleName};
 
     for (const auto &only : *onlyList) {
       common::visit(
@@ -3718,11 +3718,11 @@ void ModuleVisitor::Post(const parser::UseStmt &x) {
     // USE mod with optional renames (not ONLY)
     if (renameList->empty()) {
       // USE mod (import all, no renames)
-      PreservedUseStmt stmt{moduleName, PreservedUseStmt::Kind::UseAll};
+      PreservedUseStmt stmt{moduleName};
       currScope().add_preservedUseStmt(std::move(stmt));
     } else {
       // USE mod, renames (import all with some renames)
-      PreservedUseStmt stmt{moduleName, PreservedUseStmt::Kind::UseRenames};
+      PreservedUseStmt stmt{moduleName};
 
       for (const auto &rename : *renameList) {
         common::visit(common::visitors{
diff --git a/flang/test/Lower/debug-use-stmt-symbol-refs.f90 b/flang/test/Lower/debug-use-stmt-symbol-refs.f90
index 0f2c1db58740b..52a6f12804d20 100644
--- a/flang/test/Lower/debug-use-stmt-symbol-refs.f90
+++ b/flang/test/Lower/debug-use-stmt-symbol-refs.f90
@@ -55,5 +55,3 @@ function test_func() result(res)
 ! CHECK-DAG: fir.use_stmt "mod1" only_symbols{{\[}}[@_QMmod1Ea]]
 ! CHECK-DAG: fir.use_stmt "mod2" renames{{\[}}[#fir.use_rename<"renamed_x", @_QMmod2Ex>, #fir.use_rename<"renamed_z", @_QMmod2Ez>]]
 ! CHECK-DAG: fir.use_stmt "mod3"{{$}}
-
-

>From 5af362d0772a580f2d9212a1ce91978780c9310a Mon Sep 17 00:00:00 2001
From: Abid Qadeer <haqadeer at amd.com>
Date: Fri, 19 Dec 2025 18:10:40 +0000
Subject: [PATCH 3/4] Handle review comments (2).

Move handling of parser::UseStmt in PFTBuild and make this processing
conditional on debug information being actually required.

Updated testcase to have only with renames test. Also renamed testcase.
---
 flang/include/flang/Lower/LoweringOptions.def |   5 +
 flang/include/flang/Lower/PFTBuilder.h        |   7 +-
 flang/include/flang/Semantics/scope.h         |  15 +--
 flang/lib/Frontend/CompilerInvocation.cpp     |   8 ++
 flang/lib/Frontend/ParserActions.cpp          |   5 +-
 flang/lib/Lower/Bridge.cpp                    |  15 +--
 flang/lib/Lower/PFTBuilder.cpp                | 114 +++++++++++++++++-
 flang/lib/Semantics/resolve-names.cpp         |  79 ------------
 .../test/Lower/debug-use-stmt-symbol-refs.f90 |  57 ---------
 flang/test/Lower/debug-use-stmt.f90           |  80 ++++++++++++
 flang/tools/bbc/bbc.cpp                       |   5 +-
 11 files changed, 227 insertions(+), 163 deletions(-)
 delete mode 100644 flang/test/Lower/debug-use-stmt-symbol-refs.f90
 create mode 100644 flang/test/Lower/debug-use-stmt.f90

diff --git a/flang/include/flang/Lower/LoweringOptions.def b/flang/include/flang/Lower/LoweringOptions.def
index 39f197d8d35c8..9f76dcc15a2a3 100644
--- a/flang/include/flang/Lower/LoweringOptions.def
+++ b/flang/include/flang/Lower/LoweringOptions.def
@@ -79,5 +79,10 @@ ENUM_LOWERINGOPT(ComplexDivisionToRuntime, unsigned, 1, 1)
 /// of the lowering pipeline.
 ENUM_LOWERINGOPT(RegisterMLIRDiagnosticsHandler, unsigned, 1, 1)
 
+/// If true, preserve USE statement information during PFT building for
+/// debug info generation. Should be enabled when debug info level is above
+/// LineTablesOnly. Off by default.
+ENUM_LOWERINGOPT(PreserveUseDebugInfo, unsigned, 1, 0)
+
 #undef LOWERINGOPT
 #undef ENUM_LOWERINGOPT
diff --git a/flang/include/flang/Lower/PFTBuilder.h b/flang/include/flang/Lower/PFTBuilder.h
index ac87fcd7c3b9b..9cff324692341 100644
--- a/flang/include/flang/Lower/PFTBuilder.h
+++ b/flang/include/flang/Lower/PFTBuilder.h
@@ -737,6 +737,8 @@ struct FunctionLikeUnit : public ProgramUnit {
   /// Terminal basic block (if any)
   mlir::Block *finalBlock{};
   HostAssociations hostAssociations;
+  /// Preserved USE statements for debug info generation
+  std::list<Fortran::semantics::PreservedUseStmt> preservedUseStmts;
 };
 
 /// Module-like units contain a list of function-like units.
@@ -866,6 +868,8 @@ void visitAllSymbols(const Evaluation &eval,
 } // namespace Fortran::lower::pft
 
 namespace Fortran::lower {
+class LoweringOptions;
+
 /// Create a PFT (Pre-FIR Tree) from the parse tree.
 ///
 /// A PFT is a light weight tree over the parse tree that is used to create FIR.
@@ -876,7 +880,8 @@ namespace Fortran::lower {
 /// either a statement, or a construct with a nested list of evaluations.
 std::unique_ptr<pft::Program>
 createPFT(const parser::Program &root,
-          const Fortran::semantics::SemanticsContext &semanticsContext);
+          const Fortran::semantics::SemanticsContext &semanticsContext,
+          const LoweringOptions &loweringOptions);
 
 /// Dumper for displaying a PFT.
 void dumpPFT(llvm::raw_ostream &outputStream, const pft::Program &pft);
diff --git a/flang/include/flang/Semantics/scope.h b/flang/include/flang/Semantics/scope.h
index 1ad5e71a8e9c7..6d25bb087fbbf 100644
--- a/flang/include/flang/Semantics/scope.h
+++ b/flang/include/flang/Semantics/scope.h
@@ -56,9 +56,10 @@ struct EquivalenceObject {
 using EquivalenceSet = std::vector<EquivalenceObject>;
 
 // Preserved USE statement information for debug info generation.
+// Preserved USE statement information for debug info generation
 struct PreservedUseStmt {
   std::string moduleName;
-  std::vector<std::string> onlyNames; // For Kind::UseOnly
+  std::vector<std::string> onlyNames; // For USE ONLY
   std::vector<std::string> renames; // local_name (resolved via GetUltimate)
 
   PreservedUseStmt(std::string modName) : moduleName(std::move(modName)) {}
@@ -199,17 +200,6 @@ class Scope {
     return equivalenceSets_;
   }
   void add_equivalenceSet(EquivalenceSet &&);
-
-  // Access preserved USE statements for debug info generation
-  std::list<PreservedUseStmt> &preservedUseStmts() {
-    return preservedUseStmts_;
-  }
-  const std::list<PreservedUseStmt> &preservedUseStmts() const {
-    return preservedUseStmts_;
-  }
-  void add_preservedUseStmt(PreservedUseStmt &&stmt) {
-    preservedUseStmts_.push_back(std::move(stmt));
-  }
   // Cray pointers are saved as map of pointee name -> pointer symbol
   const mapType &crayPointers() const { return crayPointers_; }
   void add_crayPointer(const SourceName &, Symbol &);
@@ -321,7 +311,6 @@ class Scope {
   mapType commonBlocks_;
   mapType commonBlockUses_; // USE-assocated COMMON blocks
   std::list<EquivalenceSet> equivalenceSets_;
-  std::list<PreservedUseStmt> preservedUseStmts_;
   mapType crayPointers_;
   std::map<SourceName, common::Reference<Scope>> submodules_;
   std::list<DeclTypeSpec> declTypeSpecs_;
diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp
index dd25cdc0e5707..4fb1d91306745 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -1657,6 +1657,14 @@ bool CompilerInvocation::createFromArgs(
   parsePreprocessorArgs(invoc.getPreprocessorOpts(), args);
   parseCodeGenArgs(invoc.getCodeGenOpts(), args, diags);
   success &= parseDebugArgs(invoc.getCodeGenOpts(), args, diags);
+
+  // Enable USE statement preservation for debug info if debug level is above
+  // LineTablesOnly.
+  using DebugInfoKind = llvm::codegenoptions::DebugInfoKind;
+  DebugInfoKind debugLevel = invoc.getCodeGenOpts().getDebugInfo();
+  invoc.loweringOpts.setPreserveUseDebugInfo(
+      debugLevel > DebugInfoKind::DebugLineTablesOnly);
+
   success &= parseVectorLibArg(invoc.getCodeGenOpts(), args, diags);
   success &= parseSemaArgs(invoc, args, diags);
   success &= parseDialectArgs(invoc, args, diags);
diff --git a/flang/lib/Frontend/ParserActions.cpp b/flang/lib/Frontend/ParserActions.cpp
index 4fe575b06d29f..1722c7a9cf9d0 100644
--- a/flang/lib/Frontend/ParserActions.cpp
+++ b/flang/lib/Frontend/ParserActions.cpp
@@ -48,7 +48,10 @@ void dumpProvenance(CompilerInstance &ci) {
 void dumpPreFIRTree(CompilerInstance &ci) {
   auto &parseTree{*ci.getParsing().parseTree()};
 
-  if (auto ast{lower::createPFT(parseTree, ci.getSemanticsContext())}) {
+  // Use default lowering options for PFT dump
+  lower::LoweringOptions loweringOptions{};
+  if (auto ast{lower::createPFT(parseTree, ci.getSemanticsContext(),
+                                loweringOptions)}) {
     lower::dumpPFT(llvm::outs(), *ast);
   } else {
     unsigned diagID = ci.getDiagnostics().getCustomDiagID(
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index b78be6d1aa504..a322e46ed0b9b 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -222,14 +222,15 @@ static mlir::FlatSymbolRefAttr gatherComponentInit(
   return mlir::FlatSymbolRefAttr::get(mlirContext, name);
 }
 
-/// Emit fir.use_stmt operations for USE statements in the given scope
+/// Emit fir.use_stmt operations for USE statements in the given function unit
 static void
-emitUseStatementsFromScope(Fortran::lower::AbstractConverter &converter,
+emitUseStatementsFromFunit(Fortran::lower::AbstractConverter &converter,
                            mlir::OpBuilder &builder, mlir::Location loc,
-                           const Fortran::semantics::Scope &scope) {
+                           const Fortran::lower::pft::FunctionLikeUnit &funit) {
   mlir::MLIRContext *context = builder.getContext();
+  const Fortran::semantics::Scope &scope = funit.getScope();
 
-  for (const auto &preservedStmt : scope.preservedUseStmts()) {
+  for (const auto &preservedStmt : funit.preservedUseStmts) {
 
     auto getMangledName = [&](const std::string &localName) -> std::string {
       Fortran::parser::CharBlock charBlock{localName.data(), localName.size()};
@@ -258,7 +259,7 @@ emitUseStatementsFromScope(Fortran::lower::AbstractConverter &converter,
     llvm::SmallVector<mlir::Attribute> onlySymbolAttrs;
     llvm::SmallVector<mlir::Attribute> renameAttrs;
 
-    // USE mod, ONLY: list
+    // Handle only
     for (const auto &name : preservedStmt.onlyNames) {
       std::string mangledName = getMangledName(name);
       if (!mangledName.empty())
@@ -6355,7 +6356,7 @@ class FirConverter : public Fortran::lower::AbstractConverter {
     mapDummiesAndResults(funit, callee);
 
     // Emit USE statement operations for debug info generation
-    emitUseStatementsFromScope(*this, *builder, toLocation(), funit.getScope());
+    emitUseStatementsFromFunit(*this, *builder, toLocation(), funit);
 
     // Map host associated symbols from parent procedure if any.
     if (funit.parentHasHostAssoc())
@@ -7186,7 +7187,7 @@ void Fortran::lower::LoweringBridge::lower(
     const Fortran::parser::Program &prg,
     const Fortran::semantics::SemanticsContext &semanticsContext) {
   std::unique_ptr<Fortran::lower::pft::Program> pft =
-      Fortran::lower::createPFT(prg, semanticsContext);
+      Fortran::lower::createPFT(prg, semanticsContext, getLoweringOptions());
   FirConverter converter{*this};
   converter.run(*pft);
 }
diff --git a/flang/lib/Lower/PFTBuilder.cpp b/flang/lib/Lower/PFTBuilder.cpp
index 80f31c2be91a8..2dc7032b85f42 100644
--- a/flang/lib/Lower/PFTBuilder.cpp
+++ b/flang/lib/Lower/PFTBuilder.cpp
@@ -8,6 +8,7 @@
 
 #include "flang/Lower/PFTBuilder.h"
 #include "flang/Lower/IntervalSet.h"
+#include "flang/Lower/LoweringOptions.h"
 #include "flang/Lower/Support/Utils.h"
 #include "flang/Parser/dump-parse-tree.h"
 #include "flang/Parser/parse-tree-visitor.h"
@@ -73,10 +74,11 @@ void dumpScope(const semantics::Scope *scope, int depth = -1);
 /// limit the bridge to one such instantiation.
 class PFTBuilder {
 public:
-  PFTBuilder(const semantics::SemanticsContext &semanticsContext)
+  PFTBuilder(const semantics::SemanticsContext &semanticsContext,
+             const lower::LoweringOptions &loweringOptions)
       : pgm{std::make_unique<lower::pft::Program>(
             semanticsContext.GetCommonBlocks())},
-        semanticsContext{semanticsContext} {
+        semanticsContext{semanticsContext}, loweringOptions{loweringOptions} {
     lower::pft::PftNode pftRoot{*pgm.get()};
     pftParentStack.push_back(pftRoot);
   }
@@ -124,6 +126,10 @@ class PFTBuilder {
                 },
             },
             stmt.unwrapped.u);
+      } else if constexpr (std::is_same_v<T, parser::UseStmt>) {
+        // Process USE statement for debug info generation
+        processUseStmt(stmt.unwrapped);
+        return false;
       }
     }
     return true;
@@ -202,6 +208,100 @@ class PFTBuilder {
     exitConstructOrDirective();
   }
 
+  /// Process USE statements for debug info generation.
+  /// Captures USE statement information and stores it in the current
+  /// FunctionLikeUnit for later use.
+  void processUseStmt(const parser::UseStmt &useStmt) {
+    if (!loweringOptions.getPreserveUseDebugInfo())
+      return;
+
+    // Only process USE statements in specification part of function-like units
+    if (specificationPartLevel == 0 || !currentFunctionUnit)
+      return;
+
+    std::string moduleName{useStmt.moduleName.source.ToString()};
+
+    if (const auto *onlyList{
+            std::get_if<std::list<parser::Only>>(&useStmt.u)}) {
+      // USE mod, ONLY: list
+      Fortran::semantics::PreservedUseStmt stmt{moduleName};
+
+      for (const auto &only : *onlyList) {
+        Fortran::common::visit(
+            Fortran::common::visitors{
+                [&](const parser::Rename &rename) {
+                  // ONLY with rename: ONLY: local => use
+                  Fortran::common::visit(
+                      Fortran::common::visitors{
+                          [&](const parser::Rename::Names &names) {
+                            std::string localName{
+                                std::get<0>(names.t).source.ToString()};
+                            stmt.renames.push_back(localName);
+                          },
+                          [&](const parser::Rename::Operators &) {
+                            // Operator renames - not commonly needed for debug
+                            // info
+                          },
+                      },
+                      rename.u);
+                },
+                [&](const parser::Name &name) {
+                  // ONLY without rename: ONLY: name
+                  stmt.onlyNames.push_back(name.source.ToString());
+                },
+                [&](const Fortran::common::Indirection<parser::GenericSpec>
+                        &genericSpec) {
+                  // Generic spec can contain a Name (for regular symbols) or
+                  // operators
+                  Fortran::common::visit(Fortran::common::visitors{
+                                             [&](const parser::Name &name) {
+                                               stmt.onlyNames.push_back(
+                                                   name.source.ToString());
+                                             },
+                                             [&](const auto &) {
+                                               // Operators and special forms -
+                                               // not commonly needed for
+                                               // variable debug info
+                                             },
+                                         },
+                                         genericSpec.value().u);
+                },
+            },
+            only.u);
+      }
+
+      currentFunctionUnit->preservedUseStmts.push_back(std::move(stmt));
+    } else if (const auto *renameList{
+                   std::get_if<std::list<parser::Rename>>(&useStmt.u)}) {
+      // USE mod with optional renames (not ONLY)
+      if (renameList->empty()) {
+        // USE mod (import all, no renames)
+        Fortran::semantics::PreservedUseStmt stmt{moduleName};
+        currentFunctionUnit->preservedUseStmts.push_back(std::move(stmt));
+      } else {
+        // USE mod, renames (import all with some renames)
+        Fortran::semantics::PreservedUseStmt stmt{moduleName};
+
+        for (const auto &rename : *renameList) {
+          Fortran::common::visit(
+              Fortran::common::visitors{
+                  [&](const parser::Rename::Names &names) {
+                    std::string localName{
+                        std::get<0>(names.t).source.ToString()};
+                    stmt.renames.push_back(localName);
+                  },
+                  [&](const parser::Rename::Operators &) {
+                    // Operator renames - not commonly needed for debug info
+                  },
+              },
+              rename.u);
+        }
+
+        currentFunctionUnit->preservedUseStmts.push_back(std::move(stmt));
+      }
+    }
+  }
+
   template <typename A>
   constexpr void Post(const A &) {
     if constexpr (lower::pft::isFunctionLike<A>) {
@@ -378,11 +478,13 @@ class PFTBuilder {
     containedUnitList = &unit.containedUnitList;
     pushEvaluationList(&unit.evaluationList);
     pftParentStack.emplace_back(unit);
+    currentFunctionUnit = &unit;
     LLVM_DEBUG(dumpScope(&unit.getScope()));
     return true;
   }
 
   void exitFunction() {
+    currentFunctionUnit = nullptr; // Clear when exiting function
     rewriteIfGotos();
     endFunctionBody();
     analyzeBranches(nullptr, *evaluationListStack.back()); // add branch links
@@ -1124,6 +1226,7 @@ class PFTBuilder {
   std::unique_ptr<lower::pft::Program> pgm;
   std::vector<lower::pft::PftNode> pftParentStack;
   const semantics::SemanticsContext &semanticsContext;
+  const lower::LoweringOptions &loweringOptions;
 
   llvm::SmallVector<bool> containsStmtStack{};
   lower::pft::ContainedUnitList *containedUnitList{};
@@ -1136,6 +1239,8 @@ class PFTBuilder {
   std::map<std::string, lower::pft::Evaluation *> constructNameMap{};
   int specificationPartLevel{};
   lower::pft::Evaluation *lastLexicalEvaluation{};
+  /// Current function-like unit being processed (for USE statement tracking)
+  lower::pft::FunctionLikeUnit *currentFunctionUnit{nullptr};
 };
 
 #ifndef NDEBUG
@@ -1894,8 +1999,9 @@ bool Fortran::lower::pft::Variable::isRuntimeTypeInfoData() const {
 
 std::unique_ptr<lower::pft::Program>
 Fortran::lower::createPFT(const parser::Program &root,
-                          const semantics::SemanticsContext &semanticsContext) {
-  PFTBuilder walker(semanticsContext);
+                          const semantics::SemanticsContext &semanticsContext,
+                          const LoweringOptions &loweringOptions) {
+  PFTBuilder walker(semanticsContext, loweringOptions);
   Walk(root, walker);
   return walker.result();
 }
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index 77b444c1a3898..a4781c129f04d 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -3664,85 +3664,6 @@ void ModuleVisitor::Post(const parser::UseStmt &x) {
     currScope().AddCommonBlockUse(name, symbol->attrs(), symbol->GetUltimate());
   }
 
-  // Preserve USE statement information for debug info generation
-  std::string moduleName{x.moduleName.source.ToString()};
-
-  if (const auto *onlyList{std::get_if<std::list<parser::Only>>(&x.u)}) {
-    // USE mod, ONLY: list
-    PreservedUseStmt stmt{moduleName};
-
-    for (const auto &only : *onlyList) {
-      common::visit(
-          common::visitors{
-              [&](const parser::Rename &rename) {
-                // ONLY with rename: ONLY: local => use
-                common::visit(common::visitors{
-                                  [&](const parser::Rename::Names &names) {
-                                    std::string localName{
-                                        std::get<0>(names.t).source.ToString()};
-                                    stmt.renames.push_back(localName);
-                                  },
-                                  [&](const parser::Rename::Operators &) {
-                                    // Operator renames - not commonly needed
-                                    // for debug info
-                                  },
-                              },
-                    rename.u);
-              },
-              [&](const parser::Name &name) {
-                // ONLY without rename: ONLY: name
-                stmt.onlyNames.push_back(name.source.ToString());
-              },
-              [&](const common::Indirection<parser::GenericSpec> &genericSpec) {
-                // Generic spec can contain a Name (for regular symbols) or
-                // operators
-                common::visit(common::visitors{
-                                  [&](const parser::Name &name) {
-                                    stmt.onlyNames.push_back(
-                                        name.source.ToString());
-                                  },
-                                  [&](const auto &) {
-                                    // Operators and special forms - not
-                                    // commonly needed for variable debug info
-                                  },
-                              },
-                    genericSpec.value().u);
-              },
-          },
-          only.u);
-    }
-
-    currScope().add_preservedUseStmt(std::move(stmt));
-  } else if (const auto *renameList{
-                 std::get_if<std::list<parser::Rename>>(&x.u)}) {
-    // USE mod with optional renames (not ONLY)
-    if (renameList->empty()) {
-      // USE mod (import all, no renames)
-      PreservedUseStmt stmt{moduleName};
-      currScope().add_preservedUseStmt(std::move(stmt));
-    } else {
-      // USE mod, renames (import all with some renames)
-      PreservedUseStmt stmt{moduleName};
-
-      for (const auto &rename : *renameList) {
-        common::visit(common::visitors{
-                          [&](const parser::Rename::Names &names) {
-                            std::string localName{
-                                std::get<0>(names.t).source.ToString()};
-                            stmt.renames.push_back(localName);
-                          },
-                          [&](const parser::Rename::Operators &) {
-                            // Operator renames - not commonly needed for debug
-                            // info
-                          },
-                      },
-            rename.u);
-      }
-
-      currScope().add_preservedUseStmt(std::move(stmt));
-    }
-  }
-
   useModuleScope_ = nullptr;
 }
 
diff --git a/flang/test/Lower/debug-use-stmt-symbol-refs.f90 b/flang/test/Lower/debug-use-stmt-symbol-refs.f90
deleted file mode 100644
index 52a6f12804d20..0000000000000
--- a/flang/test/Lower/debug-use-stmt-symbol-refs.f90
+++ /dev/null
@@ -1,57 +0,0 @@
-! RUN: bbc -emit-fir %s -o - | FileCheck %s
-
-! Test USE statement lowering to fir.use_stmt operations
-! Covers: USE ONLY, USE with renames, and USE (all)
-
-module mod1
-  integer :: a = 10, b = 20, c = 30
-end module mod1
-
-module mod2
-  real :: x = 1.0, y = 2.0, z = 3.0
-end module mod2
-
-module mod3
-  logical :: flag = .true.
-end module mod3
-
-! Test 1: Program with USE ONLY and USE with renames
-program test_main
-  use mod1, only: b, c
-  use mod2, renamed_y => y
-  implicit none
-  print *, b, c, renamed_y
-end program
-
-! Test 2: Subroutine with USE (all) and different renames
-subroutine test_sub()
-  use mod1
-  use mod2, only: x
-  use mod3, my_flag => flag
-  implicit none
-  print *, a, b, c, x, my_flag
-end subroutine
-
-! Test 3: Function with multiple USE patterns
-function test_func() result(res)
-  use mod1, only: a
-  use mod2, renamed_x => x, renamed_z => z
-  use mod3
-  implicit none
-  integer :: res
-  res = a
-end function
-
-! CHECK-LABEL: func.func @_QQmain()
-! CHECK-DAG: fir.use_stmt "mod1" only_symbols{{\[}}[@_QMmod1Eb, @_QMmod1Ec]]
-! CHECK-DAG: fir.use_stmt "mod2" renames{{\[}}[#fir.use_rename<"renamed_y", @_QMmod2Ey>]]
-
-! CHECK-LABEL: func.func @_QPtest_sub()
-! CHECK-DAG: fir.use_stmt "mod1"{{$}}
-! CHECK-DAG: fir.use_stmt "mod2" only_symbols{{\[}}[@_QMmod2Ex]]
-! CHECK-DAG: fir.use_stmt "mod3" renames{{\[}}[#fir.use_rename<"my_flag", @_QMmod3Eflag>]]
-
-! CHECK-LABEL: func.func @_QPtest_func()
-! CHECK-DAG: fir.use_stmt "mod1" only_symbols{{\[}}[@_QMmod1Ea]]
-! CHECK-DAG: fir.use_stmt "mod2" renames{{\[}}[#fir.use_rename<"renamed_x", @_QMmod2Ex>, #fir.use_rename<"renamed_z", @_QMmod2Ez>]]
-! CHECK-DAG: fir.use_stmt "mod3"{{$}}
diff --git a/flang/test/Lower/debug-use-stmt.f90 b/flang/test/Lower/debug-use-stmt.f90
new file mode 100644
index 0000000000000..6752a6f57507a
--- /dev/null
+++ b/flang/test/Lower/debug-use-stmt.f90
@@ -0,0 +1,80 @@
+! RUN: %flang_fc1 -emit-fir -debug-info-kind=standalone %s -o - | FileCheck %s --check-prefix=WITH_DEBUG
+! RUN: %flang_fc1 -emit-fir %s -o - | FileCheck %s --check-prefix=NO_DEBUG
+! RUN: %flang_fc1 -emit-fir -debug-info-kind=line-tables-only %s -o - | FileCheck %s --check-prefix=NO_DEBUG
+
+module mod1
+  integer :: a = 10, b = 20, c = 30
+end module mod1
+
+module mod2
+  real :: x = 1.0, y = 2.0, z = 3.0
+end module mod2
+
+module mod3
+  logical :: flag = .true.
+end module mod3
+
+! Test 1: Program with USE ONLY and USE with renames
+program test_main
+  use mod1, only: b, c
+  use mod2, renamed_y => y
+  implicit none
+  print *, b, c, renamed_y
+end program
+
+! Test 2: Subroutine with USE (all) and different renames
+subroutine test_sub()
+  use mod1
+  use mod2, only: x
+  use mod3, my_flag => flag
+  implicit none
+  print *, a, b, c, x, my_flag
+end subroutine
+
+! Test 3: Function with multiple USE patterns
+function test_func() result(res)
+  use mod1, only: a
+  use mod2, renamed_x => x, renamed_z => z
+  use mod3
+  implicit none
+  integer :: res
+  res = a
+end function
+
+! Test 4: Subroutine with USE ONLY with renames (combined)
+subroutine test_only_rename()
+  use mod1, only: local_b => b, c, local_a => a
+  use mod2, only: local_x => x, local_y => y
+  implicit none
+  print *, local_a, local_b, c, local_x, local_y
+end subroutine
+
+! WITH_DEBUG-LABEL: func.func @_QQmain()
+! WITH_DEBUG-DAG: fir.use_stmt "mod1" only_symbols{{\[}}[@_QMmod1Eb, @_QMmod1Ec]]
+! WITH_DEBUG-DAG: fir.use_stmt "mod2" renames{{\[}}[#fir.use_rename<"renamed_y", @_QMmod2Ey>]]
+
+! NO_DEBUG-LABEL: func.func @_QQmain()
+! NO_DEBUG-NOT: fir.use_stmt
+
+! WITH_DEBUG-LABEL: func.func @_QPtest_sub()
+! WITH_DEBUG-DAG: fir.use_stmt "mod1"{{$}}
+! WITH_DEBUG-DAG: fir.use_stmt "mod2" only_symbols{{\[}}[@_QMmod2Ex]]
+! WITH_DEBUG-DAG: fir.use_stmt "mod3" renames{{\[}}[#fir.use_rename<"my_flag", @_QMmod3Eflag>]]
+
+! NO_DEBUG-LABEL: func.func @_QPtest_sub()
+! NO_DEBUG-NOT: fir.use_stmt
+
+! WITH_DEBUG-LABEL: func.func @_QPtest_func()
+! WITH_DEBUG-DAG: fir.use_stmt "mod1" only_symbols{{\[}}[@_QMmod1Ea]]
+! WITH_DEBUG-DAG: fir.use_stmt "mod2" renames{{\[}}[#fir.use_rename<"renamed_x", @_QMmod2Ex>, #fir.use_rename<"renamed_z", @_QMmod2Ez>]]
+! WITH_DEBUG-DAG: fir.use_stmt "mod3"{{$}}
+
+! NO_DEBUG-LABEL: func.func @_QPtest_func()
+! NO_DEBUG-NOT: fir.use_stmt
+
+! WITH_DEBUG-LABEL: func.func @_QPtest_only_rename()
+! WITH_DEBUG-DAG: fir.use_stmt "mod1" only_symbols{{\[}}[@_QMmod1Ec]] renames{{\[}}[#fir.use_rename<"local_b", @_QMmod1Eb>, #fir.use_rename<"local_a", @_QMmod1Ea>]]
+! WITH_DEBUG-DAG: fir.use_stmt "mod2" renames{{\[}}[#fir.use_rename<"local_x", @_QMmod2Ex>, #fir.use_rename<"local_y", @_QMmod2Ey>]]
+
+! NO_DEBUG-LABEL: func.func @_QPtest_only_rename()
+! NO_DEBUG-NOT: fir.use_stmt
diff --git a/flang/tools/bbc/bbc.cpp b/flang/tools/bbc/bbc.cpp
index 8b12da3a7b50a..607ac5c82cec4 100644
--- a/flang/tools/bbc/bbc.cpp
+++ b/flang/tools/bbc/bbc.cpp
@@ -418,7 +418,10 @@ static llvm::LogicalResult convertFortranSourceToMLIR(
   }
 
   if (pftDumpTest) {
-    if (auto ast = Fortran::lower::createPFT(parseTree, semanticsContext)) {
+    // Use default lowering options for PFT dump test
+    Fortran::lower::LoweringOptions loweringOptions{};
+    if (auto ast = Fortran::lower::createPFT(parseTree, semanticsContext,
+                                             loweringOptions)) {
       Fortran::lower::dumpPFT(llvm::outs(), *ast);
       return mlir::success();
     }

>From 556993fc973b74f4b7e00be1d15c65e57f1708cf Mon Sep 17 00:00:00 2001
From: Abid Qadeer <haqadeer at amd.com>
Date: Fri, 2 Jan 2026 11:26:50 +0000
Subject: [PATCH 4/4] Handle review comments (3).

1. Remove a redundant comment.
2. Use -emit-hlfir instead of -emit-fir.
---
 flang/include/flang/Semantics/scope.h | 1 -
 flang/test/Lower/debug-use-stmt.f90   | 6 +++---
 2 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/flang/include/flang/Semantics/scope.h b/flang/include/flang/Semantics/scope.h
index 6d25bb087fbbf..16553fe692e79 100644
--- a/flang/include/flang/Semantics/scope.h
+++ b/flang/include/flang/Semantics/scope.h
@@ -56,7 +56,6 @@ struct EquivalenceObject {
 using EquivalenceSet = std::vector<EquivalenceObject>;
 
 // Preserved USE statement information for debug info generation.
-// Preserved USE statement information for debug info generation
 struct PreservedUseStmt {
   std::string moduleName;
   std::vector<std::string> onlyNames; // For USE ONLY
diff --git a/flang/test/Lower/debug-use-stmt.f90 b/flang/test/Lower/debug-use-stmt.f90
index 6752a6f57507a..293b38c78e17a 100644
--- a/flang/test/Lower/debug-use-stmt.f90
+++ b/flang/test/Lower/debug-use-stmt.f90
@@ -1,6 +1,6 @@
-! RUN: %flang_fc1 -emit-fir -debug-info-kind=standalone %s -o - | FileCheck %s --check-prefix=WITH_DEBUG
-! RUN: %flang_fc1 -emit-fir %s -o - | FileCheck %s --check-prefix=NO_DEBUG
-! RUN: %flang_fc1 -emit-fir -debug-info-kind=line-tables-only %s -o - | FileCheck %s --check-prefix=NO_DEBUG
+! RUN: %flang_fc1 -emit-hlfir -debug-info-kind=standalone %s -o - | FileCheck %s --check-prefix=WITH_DEBUG
+! RUN: %flang_fc1 -emit-hlfir %s -o - | FileCheck %s --check-prefix=NO_DEBUG
+! RUN: %flang_fc1 -emit-hlfir -debug-info-kind=line-tables-only %s -o - | FileCheck %s --check-prefix=NO_DEBUG
 
 module mod1
   integer :: a = 10, b = 20, c = 30



More information about the flang-commits mailing list