[flang-commits] [flang] 06e70f6 - [flang][debug] Handle USE statements inside modules (#186184)
via flang-commits
flang-commits at lists.llvm.org
Tue Apr 21 02:56:07 PDT 2026
Author: Abid Qadeer
Date: 2026-04-21T10:56:03+01:00
New Revision: 06e70f60c9cc25a4dc321c7c9cc3b813e0191e06
URL: https://github.com/llvm/llvm-project/commit/06e70f60c9cc25a4dc321c7c9cc3b813e0191e06
DIFF: https://github.com/llvm/llvm-project/commit/06e70f60c9cc25a4dc321c7c9cc3b813e0191e06.diff
LOG: [flang][debug] Handle USE statements inside modules (#186184)
Previously, we only generated `fir.use_stmt` when a USE statement was
inside a function. USE statements inside modules were not handled,
resulting in missing debug information for transitive module
dependencies.
The problem can be seen with the following testcase:
```
module mod_a
integer :: x = 10
end module mod_a
module mod_b
use mod_a
integer :: y = 20
end module mod_b
program main
use mod_b
print *, x, y
end program main
```
We only generate `fir.use_stmt` for mod_b and nothing for `mod_a`,
making symbols from `mod_a` (like `x`) inaccessible in the debugger.
This PR solves this problem by introducing ModuleDebugImportsOp
which represents a use statement in a module. It is then used to
generate di_imported_entity in AddDebugInfo. Note that we generate
flattened debug information (one di_imported_entity for each use statement
that we can transitively reach in each function). This is to side step a
GDB issue (https://sourceware.org/bugzilla/show_bug.cgi?id=34034).
Fixes https://github.com/llvm/llvm-project/issues/177040
Added:
flang/test/Lower/debug-use-stmt-module-multiple.f90
flang/test/Transforms/debug-module-use-imports.fir
Modified:
flang/include/flang/Lower/PFTBuilder.h
flang/include/flang/Optimizer/Dialect/FIROps.td
flang/lib/Lower/Bridge.cpp
flang/lib/Lower/PFTBuilder.cpp
flang/lib/Optimizer/CodeGen/CodeGen.cpp
flang/lib/Optimizer/Transforms/AddDebugInfo.cpp
flang/test/Fir/fir-ops.fir
Removed:
################################################################################
diff --git a/flang/include/flang/Lower/PFTBuilder.h b/flang/include/flang/Lower/PFTBuilder.h
index 9cff324692341..55a755acaaeb7 100644
--- a/flang/include/flang/Lower/PFTBuilder.h
+++ b/flang/include/flang/Lower/PFTBuilder.h
@@ -768,6 +768,8 @@ struct ModuleLikeUnit : public ProgramUnit {
ModuleStatement endStmt;
ContainedUnitList containedUnitList;
EvaluationList evaluationList;
+ /// Preserved USE statements for debug info generation
+ std::list<Fortran::semantics::PreservedUseStmt> preservedUseStmts;
};
/// Block data units contain the variables and data initializers for common
diff --git a/flang/include/flang/Optimizer/Dialect/FIROps.td b/flang/include/flang/Optimizer/Dialect/FIROps.td
index d6fe214e0e9a0..eba4ddda4b6ad 100644
--- a/flang/include/flang/Optimizer/Dialect/FIROps.td
+++ b/flang/include/flang/Optimizer/Dialect/FIROps.td
@@ -3311,6 +3311,29 @@ def fir_UseStmtOp
def ImplicitFirTerminator : SingleBlockImplicitTerminator<"FirEndOp">;
+def fir_ModuleDebugImportsOp : fir_Op<"module_debug_imports", [
+ MemoryEffects<[MemWrite<DebuggingResource>]>,
+ IsolatedFromAbove,
+ ImplicitFirTerminator
+ ]> {
+ let summary = "USE statements inside a Fortran module";
+ let description = [{
+ Records the Fortran `USE` statements that appear in a module's specification
+ part for debug metadata.
+
+ The region holds one `fir.use_stmt` per `USE`, in program order. This
+ operation has no runtime effect. It is emitted only when full debug
+ information is requested.
+ }];
+
+ let arguments = (ins StrAttr:$module_name);
+ let regions = (region SizedRegion<1>:$uses);
+
+ let assemblyFormat = [{
+ $module_name attr-dict-with-keyword $uses
+ }];
+}
+
def fir_TypeInfoOp : fir_Op<"type_info",
[IsolatedFromAbove, Symbol, ImplicitFirTerminator]> {
let summary = "Derived type information";
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index 8d8786a4c25bc..655edc8ffa7b3 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -223,77 +223,111 @@ static mlir::FlatSymbolRefAttr gatherComponentInit(
return mlir::FlatSymbolRefAttr::get(mlirContext, name);
}
-/// Emit fir.use_stmt operations for USE statements in the given function unit
-static void
-emitUseStatementsFromFunit(Fortran::lower::AbstractConverter &converter,
- mlir::OpBuilder &builder, mlir::Location loc,
- const Fortran::lower::pft::FunctionLikeUnit &funit) {
+/// Emit a single fir.use_stmt from preserved frontend USE metadata.
+static void emitUseStmtOp(Fortran::lower::AbstractConverter &converter,
+ mlir::OpBuilder &builder, mlir::Location loc,
+ const Fortran::semantics::PreservedUseStmt &stmt,
+ const Fortran::semantics::Scope &lookupScope) {
mlir::MLIRContext *context = builder.getContext();
- const Fortran::semantics::Scope &scope = funit.getScope();
+ mlir::StringAttr moduleNameAttr =
+ mlir::StringAttr::get(context, stmt.moduleName);
- for (const auto &preservedStmt : funit.preservedUseStmts) {
+ // Helper function to get mangled name for a symbol
+ auto getMangledName = [&](const std::string &localName) -> std::string {
+ Fortran::parser::CharBlock charBlock{localName.data(), localName.size()};
+ const auto *sym = lookupScope.FindSymbol(charBlock);
+ if (!sym)
+ return "";
- 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();
- const auto &ultimateSym = sym->GetUltimate();
+ // Skip cases which can cause mangleName to fail.
+ if (ultimateSym.has<Fortran::semantics::DerivedTypeDetails>())
+ return "";
- // Skip cases which can cause mangleName to fail.
- if (ultimateSym.has<Fortran::semantics::DerivedTypeDetails>())
- return "";
+ if (ultimateSym.has<Fortran::semantics::UseErrorDetails>())
+ return "";
- if (ultimateSym.has<Fortran::semantics::UseErrorDetails>())
+ if (const auto *generic =
+ ultimateSym.detailsIf<Fortran::semantics::GenericDetails>()) {
+ if (!generic->specific())
return "";
+ }
- if (const auto *generic =
- ultimateSym.detailsIf<Fortran::semantics::GenericDetails>()) {
- if (!generic->specific())
- return "";
- }
-
- return converter.mangleName(ultimateSym);
- };
+ return converter.mangleName(ultimateSym);
+ };
- mlir::StringAttr moduleNameAttr =
- mlir::StringAttr::get(context, preservedStmt.moduleName);
+ llvm::SmallVector<mlir::Attribute> onlySymbolAttrs;
+ llvm::SmallVector<mlir::Attribute> renameAttrs;
- llvm::SmallVector<mlir::Attribute> onlySymbolAttrs;
- llvm::SmallVector<mlir::Attribute> renameAttrs;
+ for (const auto &name : stmt.onlyNames) {
+ std::string mangledName = getMangledName(name);
+ if (!mangledName.empty())
+ onlySymbolAttrs.push_back(
+ mlir::FlatSymbolRefAttr::get(context, mangledName));
+ }
- // Handle only
- for (const auto &name : preservedStmt.onlyNames) {
- std::string mangledName = getMangledName(name);
- if (!mangledName.empty())
- onlySymbolAttrs.push_back(
- mlir::FlatSymbolRefAttr::get(context, mangledName));
+ for (const auto &local : stmt.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
- 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));
- }
- }
+ mlir::ArrayAttr onlySymbolsAttr =
+ onlySymbolAttrs.empty() ? mlir::ArrayAttr()
+ : mlir::ArrayAttr::get(context, onlySymbolAttrs);
+ mlir::ArrayAttr renamesAttr =
+ renameAttrs.empty() ? mlir::ArrayAttr()
+ : mlir::ArrayAttr::get(context, renameAttrs);
- // 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);
+}
- fir::UseStmtOp::create(builder, loc, moduleNameAttr, onlySymbolsAttr,
- renamesAttr);
- }
+/// Emit fir.module_debug_imports for USE statements in a module.
+static void
+emitModuleDebugImports(Fortran::lower::AbstractConverter &converter,
+ mlir::OpBuilder &builder, mlir::Location loc,
+ const Fortran::lower::pft::ModuleLikeUnit &mod) {
+ if (!converter.getLoweringOptions().getPreserveUseDebugInfo())
+ return;
+ if (mod.preservedUseStmts.empty())
+ return;
+
+ const Fortran::semantics::Scope &modScope = mod.getScope();
+ const Fortran::semantics::Symbol *modSym = modScope.symbol();
+ if (!modSym)
+ return;
+
+ mlir::ModuleOp mlirModule = converter.getModuleOp();
+ mlir::OpBuilder::InsertionGuard guard(builder);
+ builder.setInsertionPoint(mlirModule.getBody(), mlirModule.getBody()->end());
+
+ auto op = fir::ModuleDebugImportsOp::create(
+ builder, loc,
+ mlir::StringAttr::get(builder.getContext(), modSym->name().ToString()));
+ mlir::Region ®ion = op.getUses();
+ mlir::Block *block = new mlir::Block();
+ region.push_back(block);
+ builder.setInsertionPointToStart(block);
+ for (const auto &stmt : mod.preservedUseStmts)
+ emitUseStmtOp(converter, builder, loc, stmt, modScope);
+ fir::FirEndOp::create(builder, loc);
+}
+
+/// Emit `fir.use_stmt` operations for USE statements that appear in the given
+/// function unit.
+static void
+emitUseStatementsFromFunit(Fortran::lower::AbstractConverter &converter,
+ mlir::OpBuilder &builder, mlir::Location loc,
+ const Fortran::lower::pft::FunctionLikeUnit &funit) {
+ const Fortran::semantics::Scope &scope = funit.getScope();
+ for (const auto &preservedStmt : funit.preservedUseStmts)
+ emitUseStmtOp(converter, builder, loc, preservedStmt, scope);
}
/// Helper class to generate the runtime type info global data and the
@@ -6897,6 +6931,7 @@ class FirConverter : public Fortran::lower::AbstractConverter {
/// declarative construct.
void lowerModuleDeclScope(Fortran::lower::pft::ModuleLikeUnit &mod) {
setCurrentPosition(mod.getStartingSourceLoc());
+ emitModuleDebugImports(*this, *builder, toLocation(), mod);
auto &scopeVariableListMap =
Fortran::lower::pft::getScopeVariableListMap(mod);
for (const auto &var : Fortran::lower::pft::getScopeVariableList(
diff --git a/flang/lib/Lower/PFTBuilder.cpp b/flang/lib/Lower/PFTBuilder.cpp
index 7e9f8b5de0827..437e70265de94 100644
--- a/flang/lib/Lower/PFTBuilder.cpp
+++ b/flang/lib/Lower/PFTBuilder.cpp
@@ -215,17 +215,28 @@ class PFTBuilder {
/// Process USE statements for debug info generation.
/// Captures USE statement information and stores it in the current
- /// FunctionLikeUnit for later use.
+ /// FunctionLikeUnit or ModuleLikeUnit 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)
+ if (!currentFunctionUnit && !currentModuleUnit)
+ return;
+
+ // For function-like units, only process USE statements in specification
+ // part.
+ if (currentFunctionUnit && specificationPartLevel == 0)
return;
std::string moduleName{useStmt.moduleName.source.ToString()};
+ auto addUseStmt = [&](Fortran::semantics::PreservedUseStmt &&stmt) {
+ if (currentFunctionUnit)
+ currentFunctionUnit->preservedUseStmts.push_back(std::move(stmt));
+ else if (currentModuleUnit)
+ currentModuleUnit->preservedUseStmts.push_back(std::move(stmt));
+ };
+
if (const auto *onlyList{
std::get_if<std::list<parser::Only>>(&useStmt.u)}) {
// USE mod, ONLY: list
@@ -275,14 +286,14 @@ class PFTBuilder {
only.u);
}
- currentFunctionUnit->preservedUseStmts.push_back(std::move(stmt));
+ addUseStmt(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));
+ addUseStmt(std::move(stmt));
} else {
// USE mod, renames (import all with some renames)
Fortran::semantics::PreservedUseStmt stmt{moduleName};
@@ -302,7 +313,7 @@ class PFTBuilder {
rename.u);
}
- currentFunctionUnit->preservedUseStmts.push_back(std::move(stmt));
+ addUseStmt(std::move(stmt));
}
}
}
@@ -411,11 +422,13 @@ class PFTBuilder {
containedUnitList = &unit.containedUnitList;
pushEvaluationList(&unit.evaluationList);
pftParentStack.emplace_back(unit);
+ currentModuleUnit = &unit;
LLVM_DEBUG(dumpScope(&unit.getScope()));
return true;
}
void exitModule() {
+ currentModuleUnit = nullptr; // Clear when exiting module
containsStmtStack.pop_back();
if (!evaluationListStack.empty())
popEvaluationList();
@@ -1250,6 +1263,8 @@ class PFTBuilder {
lower::pft::Evaluation *lastLexicalEvaluation{};
/// Current function-like unit being processed (for USE statement tracking)
lower::pft::FunctionLikeUnit *currentFunctionUnit{nullptr};
+ /// Current module-like unit being processed (for USE statement tracking)
+ lower::pft::ModuleLikeUnit *currentModuleUnit{nullptr};
};
#ifndef NDEBUG
diff --git a/flang/lib/Optimizer/CodeGen/CodeGen.cpp b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
index b03b169e0af4f..4986a20ab3123 100644
--- a/flang/lib/Optimizer/CodeGen/CodeGen.cpp
+++ b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
@@ -3695,6 +3695,19 @@ struct UseStmtOpConversion : public fir::FIROpConversion<fir::UseStmtOp> {
}
};
+/// Erase `fir.module_debug_imports` during LLVM lowering (debug metadata only).
+struct ModuleDebugImportsOpConversion
+ : public fir::FIROpConversion<fir::ModuleDebugImportsOp> {
+ using FIROpConversion::FIROpConversion;
+
+ llvm::LogicalResult
+ matchAndRewrite(fir::ModuleDebugImportsOp op, OpAdaptor adaptor,
+ mlir::ConversionPatternRewriter &rewriter) const override {
+ rewriter.eraseOp(op);
+ return mlir::success();
+ }
+};
+
static void genCondBrOp(mlir::Location loc, mlir::Value cmp, mlir::Block *dest,
std::optional<mlir::ValueRange> destOps,
mlir::ConversionPatternRewriter &rewriter,
@@ -4813,8 +4826,9 @@ void fir::populateFIRToLLVMConversionPatterns(
StoreOpConversion, StringLitOpConversion, SubcOpConversion,
TypeDescOpConversion, TypeInfoOpConversion, UnboxCharOpConversion,
UnboxProcOpConversion, UndefOpConversion, UnreachableOpConversion,
- UseStmtOpConversion, XArrayCoorOpConversion, XEmboxOpConversion,
- XReboxOpConversion, ZeroOpConversion>(converter, options);
+ 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/Transforms/AddDebugInfo.cpp b/flang/lib/Optimizer/Transforms/AddDebugInfo.cpp
index 389581c376a6d..9a53e7ff11771 100644
--- a/flang/lib/Optimizer/Transforms/AddDebugInfo.cpp
+++ b/flang/lib/Optimizer/Transforms/AddDebugInfo.cpp
@@ -32,6 +32,7 @@
#include "mlir/Transforms/DialectConversion.h"
#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
#include "mlir/Transforms/RegionUtils.h"
+#include "llvm/ADT/StringSet.h"
#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/FileSystem.h"
@@ -73,6 +74,9 @@ class AddDebugInfoPass : public fir::impl::AddDebugInfoBase<AddDebugInfoPass> {
llvm::DenseMap<fir::GlobalOp, llvm::SmallVector<mlir::Attribute>>
globalToGlobalExprsMap;
+ /// Maps Fortran module name -> `fir.module_debug_imports`.
+ llvm::StringMap<fir::ModuleDebugImportsOp> moduleDebugImportsByName;
+
mlir::LLVM::DIModuleAttr getOrCreateModuleAttr(
const std::string &name, mlir::LLVM::DIFileAttr fileAttr,
mlir::LLVM::DIScopeAttr scope, unsigned line, bool decl);
@@ -104,6 +108,13 @@ class AddDebugInfoPass : public fir::impl::AddDebugInfoBase<AddDebugInfoPass> {
mlir::LLVM::DIFileAttr fileAttr, mlir::LLVM::DICompileUnitAttr cuAttr,
mlir::SymbolTable *symbolTable,
llvm::DenseSet<mlir::LLVM::DIImportedEntityAttr> &importedEntities);
+ void buildModuleDebugImportsMap(mlir::ModuleOp module);
+ void expandUseStmtForDebug(
+ fir::UseStmtOp useOp, mlir::LLVM::DISubprogramAttr spAttr,
+ mlir::LLVM::DIFileAttr fileAttr, mlir::LLVM::DICompileUnitAttr cuAttr,
+ mlir::SymbolTable *symbolTable,
+ llvm::DenseSet<mlir::LLVM::DIImportedEntityAttr> &importedEntities,
+ llvm::StringSet<> &seenModuleNames);
std::optional<mlir::LLVM::DIImportedEntityAttr> createImportedDeclForGlobal(
llvm::StringRef symbolName, mlir::LLVM::DISubprogramAttr spAttr,
mlir::LLVM::DIFileAttr fileAttr, mlir::StringAttr localNameAttr,
@@ -842,42 +853,75 @@ void AddDebugInfoPass::handleRenamesWithoutOnly(
importedModules.insert(moduleImport);
}
-// Process all USE statements in a function and collect imported entities
+// Process all USE statements in a function and collect imported entities.
void AddDebugInfoPass::handleUseStatements(
mlir::func::FuncOp funcOp, mlir::LLVM::DISubprogramAttr spAttr,
mlir::LLVM::DIFileAttr fileAttr, mlir::LLVM::DICompileUnitAttr cuAttr,
mlir::SymbolTable *symbolTable,
llvm::DenseSet<mlir::LLVM::DIImportedEntityAttr> &importedEntities) {
- mlir::MLIRContext *context = &getContext();
-
+ llvm::StringSet<> seenModuleNames;
funcOp.walk([&](fir::UseStmtOp useOp) {
- mlir::LLVM::DIModuleAttr modAttr = getOrCreateModuleAttr(
- useOp.getModuleName().str(), fileAttr, cuAttr, /*line=*/1,
- /*decl=*/true);
-
- llvm::DenseSet<mlir::LLVM::DIImportedEntityAttr> importedModules;
-
- if (useOp.hasOnlyClause())
- handleOnlyClause(useOp, spAttr, fileAttr, symbolTable, importedModules);
- else if (useOp.hasRenames())
- handleRenamesWithoutOnly(useOp, spAttr, modAttr, fileAttr, symbolTable,
- importedModules);
- else {
- // Simple module import
- auto importedEntity = mlir::LLVM::DIImportedEntityAttr::get(
- context, llvm::dwarf::DW_TAG_imported_module, spAttr, modAttr,
- fileAttr, /*line=*/1, /*name=*/nullptr, /*elements*/ {});
- importedModules.insert(importedEntity);
- }
+ expandUseStmtForDebug(useOp, spAttr, fileAttr, cuAttr, symbolTable,
+ importedEntities, seenModuleNames);
+ });
+}
- importedEntities.insert(importedModules.begin(), importedModules.end());
+void AddDebugInfoPass::buildModuleDebugImportsMap(mlir::ModuleOp module) {
+ moduleDebugImportsByName.clear();
+ module.walk([&](fir::ModuleDebugImportsOp op) {
+ moduleDebugImportsByName[op.getModuleName().str()] = op;
});
}
+void AddDebugInfoPass::expandUseStmtForDebug(
+ fir::UseStmtOp useOp, mlir::LLVM::DISubprogramAttr spAttr,
+ mlir::LLVM::DIFileAttr fileAttr, mlir::LLVM::DICompileUnitAttr cuAttr,
+ mlir::SymbolTable *symbolTable,
+ llvm::DenseSet<mlir::LLVM::DIImportedEntityAttr> &importedEntities,
+ llvm::StringSet<> &seenModuleNames) {
+ std::string modName = useOp.getModuleName().str();
+ if (seenModuleNames.contains(modName))
+ return;
+
+ mlir::MLIRContext *context = &getContext();
+ mlir::LLVM::DIModuleAttr modAttr =
+ getOrCreateModuleAttr(modName, fileAttr, cuAttr, /*line=*/1,
+ /*decl=*/true);
+
+ llvm::DenseSet<mlir::LLVM::DIImportedEntityAttr> importedModules;
+ if (useOp.hasOnlyClause())
+ handleOnlyClause(useOp, spAttr, fileAttr, symbolTable, importedModules);
+ else if (useOp.hasRenames())
+ handleRenamesWithoutOnly(useOp, spAttr, modAttr, fileAttr, symbolTable,
+ importedModules);
+ else {
+ auto importedEntity = mlir::LLVM::DIImportedEntityAttr::get(
+ context, llvm::dwarf::DW_TAG_imported_module, spAttr, modAttr, fileAttr,
+ /*line=*/1, /*name=*/nullptr, /*elements*/ {});
+ importedModules.insert(importedEntity);
+ }
+ importedEntities.insert(importedModules.begin(), importedModules.end());
+ seenModuleNames.insert(modName);
+
+ if (useOp.hasOnlyClause())
+ return;
+
+ auto it = moduleDebugImportsByName.find(modName);
+ if (it == moduleDebugImportsByName.end())
+ return;
+ fir::ModuleDebugImportsOp mdi = it->second;
+ if (mdi.getUses().empty())
+ return;
+ for (auto childUse : mdi.getUses().front().getOps<fir::UseStmtOp>())
+ expandUseStmtForDebug(childUse, spAttr, fileAttr, cuAttr, symbolTable,
+ importedEntities, seenModuleNames);
+}
+
void AddDebugInfoPass::runOnOperation() {
mlir::ModuleOp module = getOperation();
mlir::MLIRContext *context = &getContext();
mlir::SymbolTable symbolTable(module);
+ buildModuleDebugImportsMap(module);
llvm::StringRef fileName;
std::string filePath;
std::optional<mlir::DataLayout> dl =
diff --git a/flang/test/Fir/fir-ops.fir b/flang/test/Fir/fir-ops.fir
index 2dd220134a73d..79e25d286fb3f 100644
--- a/flang/test/Fir/fir-ops.fir
+++ b/flang/test/Fir/fir-ops.fir
@@ -1106,3 +1106,10 @@ func.func @test_logical_ops(%l1: !fir.logical<4>, %l2: !fir.logical<4>, %i1: i32
%7 = fir.neqv %i1, %i2 : i32
return
}
+
+// CHECK-LABEL: fir.module_debug_imports "debug_mod"
+// CHECK-NEXT: fir.use_stmt "used_mod"
+// CHECK-NEXT: }
+fir.module_debug_imports "debug_mod" {
+ fir.use_stmt "used_mod"
+}
diff --git a/flang/test/Lower/debug-use-stmt-module-multiple.f90 b/flang/test/Lower/debug-use-stmt-module-multiple.f90
new file mode 100644
index 0000000000000..fdcc079f8987c
--- /dev/null
+++ b/flang/test/Lower/debug-use-stmt-module-multiple.f90
@@ -0,0 +1,42 @@
+! 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 mod_ma
+ integer :: va = 1
+end module mod_ma
+
+module mod_mb
+ integer :: vb = 2
+end module mod_mb
+
+module mod_mc
+ integer :: vc = 3
+end module mod_mc
+
+! A module that uses three
diff erent modules in its specification part.
+module mod_multi
+ use mod_ma, only: va
+ use mod_mb
+ use mod_mc, renamed_vc => vc
+ integer :: vx = 0
+end module mod_multi
+
+subroutine test_sub_uses_multi
+ use mod_multi
+ implicit none
+ print *, vx
+end subroutine
+
+! WITH_DEBUG: fir.module_debug_imports "mod_multi" {
+! WITH_DEBUG-NEXT: fir.use_stmt "mod_ma" only_symbols{{\[}}[@_QMmod_maEva]]
+! WITH_DEBUG-NEXT: fir.use_stmt "mod_mb"{{$}}
+! WITH_DEBUG-NEXT: fir.use_stmt "mod_mc" renames{{\[}}[#fir.use_rename<"renamed_vc", @_QMmod_mcEvc>]]
+! WITH_DEBUG-NEXT: }
+
+! There should be no more fir.use_stmt for mod_ma, mod_mb and mod_mc.
+! WITH_DEBUG-NOT: fir.use_stmt "mod_ma"
+! WITH_DEBUG-NOT: fir.use_stmt "mod_mb"
+! WITH_DEBUG-NOT: fir.use_stmt "mod_mc"
+
+! NO_DEBUG-NOT: fir.module_debug_imports
diff --git a/flang/test/Transforms/debug-module-use-imports.fir b/flang/test/Transforms/debug-module-use-imports.fir
new file mode 100644
index 0000000000000..e3127c888e806
--- /dev/null
+++ b/flang/test/Transforms/debug-module-use-imports.fir
@@ -0,0 +1,31 @@
+// RUN: fir-opt --add-debug-info --mlir-print-debuginfo %s | FileCheck %s
+
+// `fir.module_debug_imports` supplies transitive USE metadata; AddDebugInfo
+// flattens it onto the subprogram.
+
+module {
+ fir.global @_QMchildEx : i32 {
+ %0 = fir.zero_bits i32
+ fir.has_value %0 : i32
+ }
+ fir.global @_QMparentEy : i32 {
+ %0 = fir.zero_bits i32
+ fir.has_value %0 : i32
+ }
+ fir.module_debug_imports "parent" {
+ fir.use_stmt "child"
+ }
+ func.func @_QQmain() attributes {fir.bindc_name = "MAIN_USE"} {
+ fir.use_stmt "parent"
+ return
+ } loc(#loc1)
+}
+#loc1 = loc("m.f90":1:1)
+
+// CHECK-DAG: #[[MOD_CHILD:.+]] = #llvm.di_module<{{.*}}name = "child"{{.*}}>
+// CHECK-DAG: #[[MOD_PARENT:.+]] = #llvm.di_module<{{.*}}name = "parent"{{.*}}>
+
+// CHECK-DAG: #[[SP_REC:.+]] = #llvm.di_subprogram<recId = distinct[[[RECID:[0-9]+]]]<>, isRecSelf = true{{.*}}name = "MAIN_USE"
+// CHECK-DAG: #llvm.di_imported_entity<tag = DW_TAG_imported_module, scope = #[[SP_REC]], entity = #[[MOD_CHILD]],{{.*}}>
+// CHECK-DAG: #llvm.di_imported_entity<tag = DW_TAG_imported_module, scope = #[[SP_REC]], entity = #[[MOD_PARENT]],{{.*}}>
+// CHECK-DAG: #llvm.di_subprogram<recId = distinct[[[RECID]]]<>{{.*}}name = "MAIN_USE"{{.*}}retainedNodes = {{.+}}>
More information about the flang-commits
mailing list