[flang-commits] [flang] [flang][debug] Emit imported entities in source order (PR #215845)
via flang-commits
flang-commits at lists.llvm.org
Wed Aug 12 09:48:49 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-flang-fir-hlfir
Author: Abid Qadeer (abidh)
<details>
<summary>Changes</summary>
The entities a USE statement brings into a scope were gathered in a `DenseSet`, so they reached the subprogram's retained nodes in an order that follows pointer values. Compiling the same file twice therefore produced different debug information and a different object file, which is enough to break a reproducible build.
Use a `SetVector` instead. It keeps the deduplication the code relies on and hands the entities back in the order they were created, which is the order they appear in the source.
Fixes https://github.com/llvm/llvm-project/issues/215827
---
Full diff: https://github.com/llvm/llvm-project/pull/215845.diff
2 Files Affected:
- (modified) flang/lib/Optimizer/Transforms/AddDebugInfo.cpp (+11-10)
- (added) flang/test/Integration/debug-use-stmt-order.f90 (+47)
``````````diff
diff --git a/flang/lib/Optimizer/Transforms/AddDebugInfo.cpp b/flang/lib/Optimizer/Transforms/AddDebugInfo.cpp
index 82e9466c0a056..b9df44a92290b 100644
--- a/flang/lib/Optimizer/Transforms/AddDebugInfo.cpp
+++ b/flang/lib/Optimizer/Transforms/AddDebugInfo.cpp
@@ -31,6 +31,7 @@
#include "mlir/Pass/Pass.h"
#include "mlir/Transforms/DialectConversion.h"
#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
+#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/Support/FileSystem.h"
@@ -95,23 +96,23 @@ class AddDebugInfoPass : public fir::impl::AddDebugInfoBase<AddDebugInfoPass> {
void handleOnlyClause(
fir::UseStmtOp useOp, mlir::LLVM::DISubprogramAttr spAttr,
mlir::LLVM::DIFileAttr fileAttr, mlir::SymbolTable *symbolTable,
- llvm::DenseSet<mlir::LLVM::DIImportedEntityAttr> &importedModules);
+ llvm::SetVector<mlir::LLVM::DIImportedEntityAttr> &importedModules);
void handleRenamesWithoutOnly(
fir::UseStmtOp useOp, mlir::LLVM::DISubprogramAttr spAttr,
mlir::LLVM::DIModuleAttr modAttr, mlir::LLVM::DIFileAttr fileAttr,
mlir::SymbolTable *symbolTable,
- llvm::DenseSet<mlir::LLVM::DIImportedEntityAttr> &importedModules);
+ llvm::SetVector<mlir::LLVM::DIImportedEntityAttr> &importedModules);
void 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);
+ llvm::SetVector<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::SetVector<mlir::LLVM::DIImportedEntityAttr> &importedEntities,
llvm::StringSet<> &seenModuleNames);
std::optional<mlir::LLVM::DIImportedEntityAttr> createImportedDeclForGlobal(
llvm::StringRef symbolName, mlir::LLVM::DISubprogramAttr spAttr,
@@ -783,7 +784,7 @@ void AddDebugInfoPass::handleFuncOp(mlir::func::FuncOp funcOp,
subTypeAttr, /*retainedNodes=*/{}, /*annotations=*/{});
// Process USE statements (module globals are already processed)
- llvm::DenseSet<mlir::LLVM::DIImportedEntityAttr> importedEntities;
+ llvm::SetVector<mlir::LLVM::DIImportedEntityAttr> importedEntities;
handleUseStatements(funcOp, spAttr, fileAttr, cuAttr, symbolTable,
importedEntities);
@@ -857,7 +858,7 @@ AddDebugInfoPass::createImportedDeclForGlobal(
void AddDebugInfoPass::handleOnlyClause(
fir::UseStmtOp useOp, mlir::LLVM::DISubprogramAttr spAttr,
mlir::LLVM::DIFileAttr fileAttr, mlir::SymbolTable *symbolTable,
- llvm::DenseSet<mlir::LLVM::DIImportedEntityAttr> &importedModules) {
+ llvm::SetVector<mlir::LLVM::DIImportedEntityAttr> &importedModules) {
// Process ONLY symbols (without renames)
if (auto onlySymbols = useOp.getOnlySymbols()) {
for (mlir::Attribute attr : *onlySymbols) {
@@ -886,7 +887,7 @@ void AddDebugInfoPass::handleRenamesWithoutOnly(
fir::UseStmtOp useOp, mlir::LLVM::DISubprogramAttr spAttr,
mlir::LLVM::DIModuleAttr modAttr, mlir::LLVM::DIFileAttr fileAttr,
mlir::SymbolTable *symbolTable,
- llvm::DenseSet<mlir::LLVM::DIImportedEntityAttr> &importedModules) {
+ llvm::SetVector<mlir::LLVM::DIImportedEntityAttr> &importedModules) {
mlir::MLIRContext *context = &getContext();
llvm::SmallVector<mlir::LLVM::DINodeAttr> childDeclarations;
@@ -912,7 +913,7 @@ 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) {
+ llvm::SetVector<mlir::LLVM::DIImportedEntityAttr> &importedEntities) {
llvm::StringSet<> seenModuleNames;
funcOp.walk([&](fir::UseStmtOp useOp) {
expandUseStmtForDebug(useOp, spAttr, fileAttr, cuAttr, symbolTable,
@@ -931,7 +932,7 @@ 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::SetVector<mlir::LLVM::DIImportedEntityAttr> &importedEntities,
llvm::StringSet<> &seenModuleNames) {
std::string modName = useOp.getModuleName().str();
if (seenModuleNames.contains(modName))
@@ -942,7 +943,7 @@ void AddDebugInfoPass::expandUseStmtForDebug(
getOrCreateModuleAttr(modName, fileAttr, cuAttr, /*line=*/1,
/*decl=*/true);
- llvm::DenseSet<mlir::LLVM::DIImportedEntityAttr> importedModules;
+ llvm::SetVector<mlir::LLVM::DIImportedEntityAttr> importedModules;
if (useOp.hasOnlyClause() || useOp.getHasOnlyWithRenames())
handleOnlyClause(useOp, spAttr, fileAttr, symbolTable, importedModules);
else if (useOp.hasRenames())
diff --git a/flang/test/Integration/debug-use-stmt-order.f90 b/flang/test/Integration/debug-use-stmt-order.f90
new file mode 100644
index 0000000000000..83cb310089a3b
--- /dev/null
+++ b/flang/test/Integration/debug-use-stmt-order.f90
@@ -0,0 +1,47 @@
+! RUN: %flang_fc1 -emit-llvm -debug-info-kind=standalone %s -o - | FileCheck %s
+
+! The entities a USE statement imports into a scope are emitted in source order,
+! which keeps the debug information, and with it the object file, identical from
+! one run of the compiler to the next.
+
+module m1
+ integer :: a1 = 1, b1 = 2, c1 = 3
+end module m1
+
+module m2
+ integer :: a2 = 4
+end module m2
+
+module m3
+ integer :: a3 = 5, b3 = 6
+end module m3
+
+program test_order
+ use m1, only: a1, b1, z1 => c1
+ use m2
+ use m3, x3 => a3, y3 => b3
+ implicit none
+ print *, a1, b1, z1, a2, x3, y3
+end program
+
+! CHECK-DAG: [[A1:![0-9]+]] = distinct !DIGlobalVariable(name: "a1"
+! CHECK-DAG: [[B1:![0-9]+]] = distinct !DIGlobalVariable(name: "b1"
+! CHECK-DAG: [[C1:![0-9]+]] = distinct !DIGlobalVariable(name: "c1"
+! CHECK-DAG: [[M2:![0-9]+]] = !DIModule(scope: !{{.*}}, name: "m2"
+! CHECK-DAG: [[M3:![0-9]+]] = !DIModule(scope: !{{.*}}, name: "m3"
+! CHECK-DAG: [[A3:![0-9]+]] = distinct !DIGlobalVariable(name: "a3"
+! CHECK-DAG: [[B3:![0-9]+]] = distinct !DIGlobalVariable(name: "b3"
+! CHECK-DAG: [[SP:![0-9]+]] = distinct !DISubprogram(name: "TEST_ORDER"{{.*}}retainedNodes: [[NODES:![0-9]+]]
+
+! CHECK: [[NODES]] = !{[[E1:![0-9]+]], [[E2:![0-9]+]], [[E3:![0-9]+]], [[E4:![0-9]+]], [[E5:![0-9]+]]}
+! CHECK-NEXT: [[E1]] = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: [[SP]], entity: [[A1]],
+! CHECK-NEXT: [[E2]] = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: [[SP]], entity: [[B1]],
+! CHECK-NEXT: [[E3]] = !DIImportedEntity(tag: DW_TAG_imported_declaration, name: "z1", scope: [[SP]], entity: [[C1]],
+! CHECK-NEXT: [[E4]] = !DIImportedEntity(tag: DW_TAG_imported_module, scope: [[SP]], entity: [[M2]],
+
+! The renames a USE statement without ONLY brings in are the children of the
+! module import, and they keep source order too.
+! CHECK-NEXT: [[E5]] = !DIImportedEntity(tag: DW_TAG_imported_module, scope: [[SP]], entity: [[M3]],{{.*}}elements: [[ELEMENTS:![0-9]+]]
+! CHECK-NEXT: [[ELEMENTS]] = !{[[C1E:![0-9]+]], [[C2E:![0-9]+]]}
+! CHECK-NEXT: [[C1E]] = !DIImportedEntity(tag: DW_TAG_imported_declaration, name: "x3", scope: [[SP]], entity: [[A3]],
+! CHECK-NEXT: [[C2E]] = !DIImportedEntity(tag: DW_TAG_imported_declaration, name: "y3", scope: [[SP]], entity: [[B3]],
``````````
</details>
https://github.com/llvm/llvm-project/pull/215845
More information about the flang-commits
mailing list