[flang-commits] [flang] Reland [flang][debug] Emit debug info for named constants- #213974 (PR #215369)

Abid Qadeer via flang-commits flang-commits at lists.llvm.org
Wed Aug 12 05:40:13 PDT 2026


================
@@ -510,8 +521,27 @@ void AddDebugInfoPass::handleGlobalOp(fir::GlobalOp globalOp,
   mlir::OpBuilder builder(context);
 
   std::pair result = fir::NameUniquer::deconstruct(globalOp.getSymName());
-  if (result.first != fir::NameUniquer::NameKind::VARIABLE)
+  switch (result.first) {
+  case fir::NameUniquer::NameKind::VARIABLE:
+    break;
+  case fir::NameUniquer::NameKind::CONSTANT:
+    // A constant local to a procedure is described while walking that
+    // procedure, where `scope` is its DISubprogramAttr. Reaching here with any
+    // other scope means the procedure is not in the IR, typically because it
+    // was never called and got removed while its constant survived. There is
+    // no procedure to attach the constant to, and describing it at compile
+    // unit scope would wrongly make it visible everywhere.
+    if (!isModuleLevelName(result.second) &&
+        !mlir::isa<mlir::LLVM::DISubprogramAttr>(scope))
+      return;
+    // Don't describe a constant for which we only have a declaration. It could
+    // leave an unresolved symbol in the debug information.
+    if (!globalOp.isInitialized())
----------------
abidh wrote:

Thanks for passing this on, the analysis is right, and both symptoms reproduce. Here is what was happening and what I have changed.

**The mechanism.** `decl = !globalOp.isInitialized()` answers *"does this unit initialize this global"*, and it was being used to answer *"is this unit where the module is defined"*. Those coincide for a module variable but not for a named constant, and since the walk now reaches constants, the first constant to arrive decided the question. Because `DIModuleAttr` is immutable and the map is first-writer-wins, that decision then stuck for every other member of the module. Using an intrinsic module materializes some of its constants locally — `_QMiso_fortran_envECcharacter_kinds` comes out `linkonce_odr` with an initializer — so one of those was enough to make `iso_fortran_env` look locally defined.

**The fix, in two parts.**

1. *Only describe a named constant this unit defines.* A constant imported from a module arrives as an uninitialized `fir.global`, i.e. a declaration, and describing it produced a location pointing at a symbol nothing defines. This is what created the undefined references, and it is already in the branch you reviewed.
2. *Decide module definedness before any `DIModuleAttr` is built.* Lowering emits one
`fir.module_debug_imports` for every module and submodule it compiles, so that set is exactly the set of modules defined here. It is now collected once up front and every `DIModuleAttr` is built from the same answer. No individual global gets a vote, so visit order cannot change the result.

These should fix the relocation and order sensitivity issue as describe above.

**A note on submodules.** A submodule has no `DIModuleAttr` of its own today; the entities it defines
hang off its ancestor module's `DIModuleAttr`. So for the submodule's own constant `sq` to be describable at all, the ancestor must be a definition in that object, a declaration carries no scope, and anything under it cannot reach the compile unit and is dropped, which is precisely what happens to `kid2` above. The new pass therefore also treats an ancestor as defined when a submodule of it is compiled here, and that is what makes the two objects agree. I have kept that deliberately narrow, because it is a stopgap rather than a model of what submodules are.  Describing submodules properly, with a DIModuleAttr of their own is follow-up work.



https://github.com/llvm/llvm-project/pull/215369


More information about the flang-commits mailing list