[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