@@ -72,6 +72,10 @@ class AddDebugInfoPass : public fir::impl::AddDebugInfoBase<AddDebugInfoPass> {
7272 mlir::LLVM::DICompileUnitAttr cuAttr,
7373 fir::DebugTypeGenerator &typeGen,
7474 mlir::SymbolTable *symbolTable);
75+ std::optional<mlir::LLVM::DIModuleAttr>
76+ getModuleAttrFromGlobalOp (fir::GlobalOp globalOp,
77+ mlir::LLVM::DIFileAttr fileAttr,
78+ mlir::LLVM::DIScopeAttr scope);
7579};
7680
7781bool debugInfoIsAlreadySet (mlir::Location loc) {
@@ -152,6 +156,45 @@ mlir::LLVM::DIModuleAttr AddDebugInfoPass::getOrCreateModuleAttr(
152156 return modAttr;
153157}
154158
159+ // / If globalOp represents a module variable, return a ModuleAttr that
160+ // / represents that module.
161+ std::optional<mlir::LLVM::DIModuleAttr>
162+ AddDebugInfoPass::getModuleAttrFromGlobalOp (fir::GlobalOp globalOp,
163+ mlir::LLVM::DIFileAttr fileAttr,
164+ mlir::LLVM::DIScopeAttr scope) {
165+ mlir::MLIRContext *context = &getContext ();
166+ mlir::OpBuilder builder (context);
167+
168+ std::pair result = fir::NameUniquer::deconstruct (globalOp.getSymName ());
169+ // Only look for module if this variable is not part of a function.
170+ if (!result.second .procs .empty () || result.second .modules .empty ())
171+ return std::nullopt ;
172+
173+ // DWARF5 says following about the fortran modules:
174+ // A Fortran 90 module may also be represented by a module entry
175+ // (but no declaration attribute is warranted because Fortran has no concept
176+ // of a corresponding module body).
177+ // But in practice, compilers use declaration attribute with a module in cases
178+ // where module was defined in another source file (only being used in this
179+ // one). The isInitialized() seems to provide the right information
180+ // but inverted. It is true where module is actually defined but false where
181+ // it is used.
182+ // FIXME: Currently we don't have the line number on which a module was
183+ // declared. We are using a best guess of line - 1 where line is the source
184+ // line of the first member of the module that we encounter.
185+ unsigned line = getLineFromLoc (globalOp.getLoc ());
186+
187+ mlir::LLVM::DISubprogramAttr sp =
188+ mlir::dyn_cast_if_present<mlir::LLVM::DISubprogramAttr>(scope);
189+ // Modules are generated at compile unit scope
190+ if (sp)
191+ scope = sp.getCompileUnit ();
192+
193+ return getOrCreateModuleAttr (result.second .modules [0 ], fileAttr, scope,
194+ std::max (line - 1 , (unsigned )1 ),
195+ !globalOp.isInitialized ());
196+ }
197+
155198void AddDebugInfoPass::handleGlobalOp (fir::GlobalOp globalOp,
156199 mlir::LLVM::DIFileAttr fileAttr,
157200 mlir::LLVM::DIScopeAttr scope,
@@ -174,33 +217,11 @@ void AddDebugInfoPass::handleGlobalOp(fir::GlobalOp globalOp,
174217 return ;
175218
176219 unsigned line = getLineFromLoc (globalOp.getLoc ());
220+ std::optional<mlir::LLVM::DIModuleAttr> modOpt =
221+ getModuleAttrFromGlobalOp (globalOp, fileAttr, scope);
222+ if (modOpt)
223+ scope = *modOpt;
177224
178- // DWARF5 says following about the fortran modules:
179- // A Fortran 90 module may also be represented by a module entry
180- // (but no declaration attribute is warranted because Fortran has no concept
181- // of a corresponding module body).
182- // But in practice, compilers use declaration attribute with a module in cases
183- // where module was defined in another source file (only being used in this
184- // one). The isInitialized() seems to provide the right information
185- // but inverted. It is true where module is actually defined but false where
186- // it is used.
187- // FIXME: Currently we don't have the line number on which a module was
188- // declared. We are using a best guess of line - 1 where line is the source
189- // line of the first member of the module that we encounter.
190-
191- if (result.second .procs .empty ()) {
192- // Only look for module if this variable is not part of a function.
193- if (result.second .modules .empty ())
194- return ;
195-
196- // Modules are generated at compile unit scope
197- if (mlir::LLVM::DISubprogramAttr sp =
198- mlir::dyn_cast_if_present<mlir::LLVM::DISubprogramAttr>(scope))
199- scope = sp.getCompileUnit ();
200-
201- scope = getOrCreateModuleAttr (result.second .modules [0 ], fileAttr, scope,
202- line - 1 , !globalOp.isInitialized ());
203- }
204225 mlir::LLVM::DITypeAttr diType =
205226 typeGen.convertType (globalOp.getType (), fileAttr, scope, declOp);
206227 auto gvAttr = mlir::LLVM::DIGlobalVariableAttr::get (
@@ -262,15 +283,18 @@ void AddDebugInfoPass::handleFuncOp(mlir::func::FuncOp funcOp,
262283 mlir::LLVM::DIFileAttr::get (context, fileName, filePath);
263284
264285 // Only definitions need a distinct identifier and a compilation unit.
265- mlir::DistinctAttr id;
286+ mlir::DistinctAttr id, id2 ;
266287 mlir::LLVM::DIScopeAttr Scope = fileAttr;
267288 mlir::LLVM::DICompileUnitAttr compilationUnit;
268289 mlir::LLVM::DISubprogramFlags subprogramFlags =
269290 mlir::LLVM::DISubprogramFlags{};
270291 if (isOptimized)
271292 subprogramFlags = mlir::LLVM::DISubprogramFlags::Optimized;
272293 if (!funcOp.isExternal ()) {
294+ // Place holder and final function have to have different IDs, otherwise
295+ // translation code will reject one of them.
273296 id = mlir::DistinctAttr::create (mlir::UnitAttr::get (context));
297+ id2 = mlir::DistinctAttr::create (mlir::UnitAttr::get (context));
274298 compilationUnit = cuAttr;
275299 subprogramFlags =
276300 subprogramFlags | mlir::LLVM::DISubprogramFlags::Definition;
@@ -299,14 +323,69 @@ void AddDebugInfoPass::handleFuncOp(mlir::func::FuncOp funcOp,
299323 line - 1 , false );
300324 }
301325
302- auto spAttr = mlir::LLVM::DISubprogramAttr::get (
303- context, id, compilationUnit, Scope, funcName, fullName, funcFileAttr,
304- line, line, subprogramFlags, subTypeAttr, /* retainedNodes=*/ {});
305- funcOp->setLoc (builder.getFusedLoc ({funcOp->getLoc ()}, spAttr));
306-
307326 // Don't process variables if user asked for line tables only.
308- if (debugLevel == mlir::LLVM::DIEmissionKind::LineTablesOnly)
327+ if (debugLevel == mlir::LLVM::DIEmissionKind::LineTablesOnly) {
328+ auto spAttr = mlir::LLVM::DISubprogramAttr::get (
329+ context, id, compilationUnit, Scope, funcName, fullName, funcFileAttr,
330+ line, line, subprogramFlags, subTypeAttr, /* retainedNodes=*/ {});
331+ funcOp->setLoc (builder.getFusedLoc ({l}, spAttr));
309332 return ;
333+ }
334+
335+ mlir::DistinctAttr recId =
336+ mlir::DistinctAttr::create (mlir::UnitAttr::get (context));
337+
338+ // The debug attribute in MLIR are readonly once created. But in case of
339+ // imported entities, we have a circular dependency. The
340+ // DIImportedEntityAttr requires scope information (DISubprogramAttr in this
341+ // case) and DISubprogramAttr requires the list of imported entities. The
342+ // MLIR provides a way where a DISubprogramAttr an be created with a certain
343+ // recID and be used in places like DIImportedEntityAttr. After that another
344+ // DISubprogramAttr can be created with same recID but with list of entities
345+ // now available. The MLIR translation code takes care of updating the
346+ // references. Note that references will be updated only in the things that
347+ // are part of DISubprogramAttr (like DIImportedEntityAttr) so we have to
348+ // create the final DISubprogramAttr before we process local variables.
349+ // Look at DIRecursiveTypeAttrInterface for more details.
350+
351+ auto spAttr = mlir::LLVM::DISubprogramAttr::get (
352+ context, recId, /* isRecSelf=*/ true , id, compilationUnit, Scope, funcName,
353+ fullName, funcFileAttr, line, line, subprogramFlags, subTypeAttr,
354+ /* retainedNodes=*/ {});
355+
356+ // There is no direct information in the IR for any 'use' statement in the
357+ // function. We have to extract that information from the DeclareOp. We do
358+ // a pass on the DeclareOp and generate ModuleAttr and corresponding
359+ // DIImportedEntityAttr for that module.
360+ // FIXME: As we are depending on the variables to see which module is being
361+ // 'used' in the function, there are certain limitations.
362+ // For things like 'use mod1, only: v1', whole module will be brought into the
363+ // namespace in the debug info. It is not a problem as such unless there is a
364+ // clash of names.
365+ // There is no information about module variable renaming
366+ llvm::DenseSet<mlir::LLVM::DIImportedEntityAttr> importedModules;
367+ funcOp.walk ([&](fir::cg::XDeclareOp declOp) {
368+ if (&funcOp.front () == declOp->getBlock ())
369+ if (auto global =
370+ symbolTable->lookup <fir::GlobalOp>(declOp.getUniqName ())) {
371+ std::optional<mlir::LLVM::DIModuleAttr> modOpt =
372+ getModuleAttrFromGlobalOp (global, fileAttr, cuAttr);
373+ if (modOpt) {
374+ auto importedEntity = mlir::LLVM::DIImportedEntityAttr::get (
375+ context, llvm::dwarf::DW_TAG_imported_module, spAttr, *modOpt,
376+ fileAttr, /* line=*/ 1 , /* name=*/ nullptr , /* elements*/ {});
377+ importedModules.insert (importedEntity);
378+ }
379+ }
380+ });
381+ llvm::SmallVector<mlir::LLVM::DINodeAttr> entities (importedModules.begin (),
382+ importedModules.end ());
383+ // We have the imported entities now. Generate the final DISubprogramAttr.
384+ spAttr = mlir::LLVM::DISubprogramAttr::get (
385+ context, recId, /* isRecSelf=*/ false , id2, compilationUnit, Scope,
386+ funcName, fullName, funcFileAttr, line, line, subprogramFlags,
387+ subTypeAttr, entities);
388+ funcOp->setLoc (builder.getFusedLoc ({l}, spAttr));
310389
311390 funcOp.walk ([&](fir::cg::XDeclareOp declOp) {
312391 // FIXME: We currently dont handle variables that are not in the entry
0 commit comments