diff --git a/flang/lib/Optimizer/Transforms/AddDebugInfo.cpp b/flang/lib/Optimizer/Transforms/AddDebugInfo.cpp index 30fc4185575e6..286af418fc05d 100644 --- a/flang/lib/Optimizer/Transforms/AddDebugInfo.cpp +++ b/flang/lib/Optimizer/Transforms/AddDebugInfo.cpp @@ -302,7 +302,7 @@ void AddDebugInfoPass::handleFuncOp(mlir::func::FuncOp funcOp, auto spAttr = mlir::LLVM::DISubprogramAttr::get( context, id, compilationUnit, Scope, funcName, fullName, funcFileAttr, - line, line, subprogramFlags, subTypeAttr); + line, line, subprogramFlags, subTypeAttr, /*retainedNodes=*/{}); funcOp->setLoc(builder.getFusedLoc({funcOp->getLoc()}, spAttr)); // Don't process variables if user asked for line tables only. diff --git a/mlir/include/mlir-c/Dialect/LLVM.h b/mlir/include/mlir-c/Dialect/LLVM.h index 631b564618320..5eb96a86e472d 100644 --- a/mlir/include/mlir-c/Dialect/LLVM.h +++ b/mlir/include/mlir-c/Dialect/LLVM.h @@ -316,7 +316,8 @@ MLIR_CAPI_EXPORTED MlirAttribute mlirLLVMDISubprogramAttrGet( MlirContext ctx, MlirAttribute id, MlirAttribute compileUnit, MlirAttribute scope, MlirAttribute name, MlirAttribute linkageName, MlirAttribute file, unsigned int line, unsigned int scopeLine, - uint64_t subprogramFlags, MlirAttribute type); + uint64_t subprogramFlags, MlirAttribute type, intptr_t nRetainedNodes, + MlirAttribute const *retainedNodes); /// Gets the scope from this DISubprogramAttr. MLIR_CAPI_EXPORTED MlirAttribute @@ -353,6 +354,12 @@ MLIR_CAPI_EXPORTED MlirAttribute mlirLLVMDIModuleAttrGet( MlirAttribute name, MlirAttribute configMacros, MlirAttribute includePath, MlirAttribute apinotes, unsigned int line, bool isDecl); +/// Creates a LLVM DIImportedEntityAttr attribute. +MLIR_CAPI_EXPORTED MlirAttribute mlirLLVMDIImportedEntityAttrGet( + MlirContext ctx, unsigned int tag, MlirAttribute entity, MlirAttribute file, + unsigned int line, MlirAttribute name, intptr_t nElements, + MlirAttribute const *elements); + /// Gets the scope of this DIModuleAttr. MLIR_CAPI_EXPORTED MlirAttribute mlirLLVMDIModuleAttrGetScope(MlirAttribute diModule); diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td index 5d96f50634258..e57be7f760d38 100644 --- a/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td @@ -565,19 +565,21 @@ def LLVM_DISubprogramAttr : LLVM_Attr<"DISubprogram", "di_subprogram", OptionalParameter<"unsigned">:$line, OptionalParameter<"unsigned">:$scopeLine, OptionalParameter<"DISubprogramFlags">:$subprogramFlags, - OptionalParameter<"DISubroutineTypeAttr">:$type + OptionalParameter<"DISubroutineTypeAttr">:$type, + OptionalArrayRefParameter<"DINodeAttr">:$retainedNodes ); let builders = [ AttrBuilderWithInferredContext<(ins "DistinctAttr":$id, "DICompileUnitAttr":$compileUnit, "DIScopeAttr":$scope, "StringRef":$name, "StringRef":$linkageName, "DIFileAttr":$file, "unsigned":$line, "unsigned":$scopeLine, - "DISubprogramFlags":$subprogramFlags, "DISubroutineTypeAttr":$type + "DISubprogramFlags":$subprogramFlags, "DISubroutineTypeAttr":$type, + "ArrayRef":$retainedNodes ), [{ MLIRContext *ctx = file.getContext(); return $_get(ctx, id, compileUnit, scope, StringAttr::get(ctx, name), StringAttr::get(ctx, linkageName), file, line, - scopeLine, subprogramFlags, type); + scopeLine, subprogramFlags, type, retainedNodes); }]> ]; @@ -619,6 +621,29 @@ def LLVM_DINamespaceAttr : LLVM_Attr<"DINamespace", "di_namespace", let assemblyFormat = "`<` struct(params) `>`"; } +//===----------------------------------------------------------------------===// +// DIImportedEntityAttr +//===----------------------------------------------------------------------===// + +def LLVM_DIImportedEntityAttr : LLVM_Attr<"DIImportedEntity", "di_imported_entity", + /*traits=*/[], "DINodeAttr"> { + /// TODO: DIImportedEntity has a 'scope' field which represents the scope where + /// this entity is imported. Currently, we are not adding a 'scope' field in + /// DIImportedEntityAttr to avoid cyclic dependency. As DIImportedEntityAttr + /// entries will be contained inside a scope entity (e.g. DISubprogramAttr), + /// the scope can easily be inferred. + let parameters = (ins + LLVM_DITagParameter:$tag, + "DINodeAttr":$entity, + OptionalParameter<"DIFileAttr">:$file, + OptionalParameter<"unsigned">:$line, + OptionalParameter<"StringAttr">:$name, + OptionalArrayRefParameter<"DINodeAttr">:$elements + ); + + let assemblyFormat = "`<` struct(params) `>`"; +} + //===----------------------------------------------------------------------===// // DISubrangeAttr //===----------------------------------------------------------------------===// diff --git a/mlir/lib/CAPI/Dialect/LLVM.cpp b/mlir/lib/CAPI/Dialect/LLVM.cpp index 03e2f2be2156a..13341f0c4de88 100644 --- a/mlir/lib/CAPI/Dialect/LLVM.cpp +++ b/mlir/lib/CAPI/Dialect/LLVM.cpp @@ -293,14 +293,20 @@ MlirAttribute mlirLLVMDISubprogramAttrGet( MlirContext ctx, MlirAttribute id, MlirAttribute compileUnit, MlirAttribute scope, MlirAttribute name, MlirAttribute linkageName, MlirAttribute file, unsigned int line, unsigned int scopeLine, - uint64_t subprogramFlags, MlirAttribute type) { + uint64_t subprogramFlags, MlirAttribute type, intptr_t nRetainedNodes, + MlirAttribute const *retainedNodes) { + SmallVector nodesStorage; + nodesStorage.reserve(nRetainedNodes); return wrap(DISubprogramAttr::get( unwrap(ctx), cast(unwrap(id)), cast(unwrap(compileUnit)), cast(unwrap(scope)), cast(unwrap(name)), cast(unwrap(linkageName)), cast(unwrap(file)), line, scopeLine, DISubprogramFlags(subprogramFlags), - cast(unwrap(type)))); + cast(unwrap(type)), + llvm::map_to_vector( + unwrapList(nRetainedNodes, retainedNodes, nodesStorage), + [](Attribute a) { return cast(a); }))); } MlirAttribute mlirLLVMDISubprogramAttrGetScope(MlirAttribute diSubprogram) { @@ -345,3 +351,16 @@ MlirAttribute mlirLLVMDIModuleAttrGet(MlirContext ctx, MlirAttribute file, MlirAttribute mlirLLVMDIModuleAttrGetScope(MlirAttribute diModule) { return wrap(cast(unwrap(diModule)).getScope()); } + +MlirAttribute mlirLLVMDIImportedEntityAttrGet( + MlirContext ctx, unsigned int tag, MlirAttribute entity, MlirAttribute file, + unsigned int line, MlirAttribute name, intptr_t nElements, + MlirAttribute const *elements) { + SmallVector elementsStorage; + elementsStorage.reserve(nElements); + return wrap(DIImportedEntityAttr::get( + unwrap(ctx), tag, cast(unwrap(entity)), + cast(unwrap(file)), line, cast(unwrap(name)), + llvm::map_to_vector(unwrapList(nElements, elements, elementsStorage), + [](Attribute a) { return cast(a); }))); +} diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp index 963a4be25079e..98a9659735e7e 100644 --- a/mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp +++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp @@ -58,10 +58,11 @@ void LLVMDialect::registerAttributes() { bool DINodeAttr::classof(Attribute attr) { return llvm::isa(attr); + DIImportedEntityAttr, DILabelAttr, DILexicalBlockAttr, + DILexicalBlockFileAttr, DILocalVariableAttr, DIModuleAttr, + DINamespaceAttr, DINullTypeAttr, DIStringTypeAttr, + DISubprogramAttr, DISubrangeAttr, DISubroutineTypeAttr>( + attr); } //===----------------------------------------------------------------------===// diff --git a/mlir/lib/Dialect/LLVMIR/Transforms/DIScopeForLLVMFuncOp.cpp b/mlir/lib/Dialect/LLVMIR/Transforms/DIScopeForLLVMFuncOp.cpp index 395ff6ed1e48e..758700c9272bc 100644 --- a/mlir/lib/Dialect/LLVMIR/Transforms/DIScopeForLLVMFuncOp.cpp +++ b/mlir/lib/Dialect/LLVMIR/Transforms/DIScopeForLLVMFuncOp.cpp @@ -79,7 +79,8 @@ static void addScopeToFunction(LLVM::LLVMFuncOp llvmFunc, context, id, compileUnitAttr, fileAttr, funcNameAttr, funcNameAttr, fileAttr, /*line=*/line, - /*scopeline=*/col, subprogramFlags, subroutineTypeAttr); + /*scopeline=*/col, subprogramFlags, subroutineTypeAttr, + /*retainedNodes=*/{}); llvmFunc->setLoc(FusedLoc::get(context, {loc}, subprogramAttr)); } diff --git a/mlir/lib/Target/LLVMIR/DebugImporter.cpp b/mlir/lib/Target/LLVMIR/DebugImporter.cpp index 1817c1271b43e..ce3643f513d34 100644 --- a/mlir/lib/Target/LLVMIR/DebugImporter.cpp +++ b/mlir/lib/Target/LLVMIR/DebugImporter.cpp @@ -208,6 +208,20 @@ DINamespaceAttr DebugImporter::translateImpl(llvm::DINamespace *node) { node->getExportSymbols()); } +DIImportedEntityAttr +DebugImporter::translateImpl(llvm::DIImportedEntity *node) { + SmallVector elements; + for (llvm::DINode *element : node->getElements()) { + assert(element && "expected a non-null element type"); + elements.push_back(translate(element)); + } + + return DIImportedEntityAttr::get( + context, node->getTag(), translate(node->getEntity()), + translate(node->getFile()), node->getLine(), + getStringAttrOrNull(node->getRawName()), elements); +} + DISubprogramAttr DebugImporter::translateImpl(llvm::DISubprogram *node) { // Only definitions require a distinct identifier. mlir::DistinctAttr id; @@ -223,11 +237,17 @@ DISubprogramAttr DebugImporter::translateImpl(llvm::DISubprogram *node) { DISubroutineTypeAttr type = translate(node->getType()); if (node->getType() && !type) return nullptr; + + SmallVector retainedNodes; + for (llvm::DINode *retainedNode : node->getRetainedNodes()) + retainedNodes.push_back(translate(retainedNode)); + return DISubprogramAttr::get(context, id, translate(node->getUnit()), scope, getStringAttrOrNull(node->getRawName()), getStringAttrOrNull(node->getRawLinkageName()), translate(node->getFile()), node->getLine(), - node->getScopeLine(), *subprogramFlags, type); + node->getScopeLine(), *subprogramFlags, type, + retainedNodes); } DISubrangeAttr DebugImporter::translateImpl(llvm::DISubrange *node) { @@ -308,6 +328,8 @@ DINodeAttr DebugImporter::translate(llvm::DINode *node) { return translateImpl(casted); if (auto *casted = dyn_cast(node)) return translateImpl(casted); + if (auto *casted = dyn_cast(node)) + return translateImpl(casted); if (auto *casted = dyn_cast(node)) return translateImpl(casted); if (auto *casted = dyn_cast(node)) diff --git a/mlir/lib/Target/LLVMIR/DebugImporter.h b/mlir/lib/Target/LLVMIR/DebugImporter.h index 0e040891ba6c0..cb796676759c3 100644 --- a/mlir/lib/Target/LLVMIR/DebugImporter.h +++ b/mlir/lib/Target/LLVMIR/DebugImporter.h @@ -75,6 +75,7 @@ class DebugImporter { DIVariableAttr translateImpl(llvm::DIVariable *node); DIModuleAttr translateImpl(llvm::DIModule *node); DINamespaceAttr translateImpl(llvm::DINamespace *node); + DIImportedEntityAttr translateImpl(llvm::DIImportedEntity *node); DIScopeAttr translateImpl(llvm::DIScope *node); DISubprogramAttr translateImpl(llvm::DISubprogram *node); DISubrangeAttr translateImpl(llvm::DISubrange *node); diff --git a/mlir/lib/Target/LLVMIR/DebugTranslation.cpp b/mlir/lib/Target/LLVMIR/DebugTranslation.cpp index 95b37e47d0461..042e015f107fe 100644 --- a/mlir/lib/Target/LLVMIR/DebugTranslation.cpp +++ b/mlir/lib/Target/LLVMIR/DebugTranslation.cpp @@ -306,6 +306,19 @@ llvm::DISubprogram *DebugTranslation::translateImpl(DISubprogramAttr attr) { static_cast(attr.getSubprogramFlags()), compileUnit); + // DIImportedEntity requires scope information which DIImportedEntityAttr does + // not have. This is why we translate DIImportedEntityAttr after we have + // created DISubprogram as we can use it as the scope. + SmallVector retainedNodes; + for (DINodeAttr nodeAttr : attr.getRetainedNodes()) { + if (auto importedAttr = dyn_cast(nodeAttr)) { + llvm::DINode *dn = translate(importedAttr, node); + retainedNodes.push_back(dn); + } + } + if (!retainedNodes.empty()) + node->replaceRetainedNodes(llvm::MDTuple::get(llvmCtx, retainedNodes)); + if (attr.getId()) distinctAttrToNode.try_emplace(attr.getId(), node); return node; @@ -326,6 +339,18 @@ llvm::DINamespace *DebugTranslation::translateImpl(DINamespaceAttr attr) { attr.getExportSymbols()); } +llvm::DIImportedEntity *DebugTranslation::translate(DIImportedEntityAttr attr, + llvm::DIScope *scope) { + SmallVector elements; + for (DINodeAttr member : attr.getElements()) + elements.push_back(translate(member)); + + return llvm::DIImportedEntity::get( + llvmCtx, attr.getTag(), scope, translate(attr.getEntity()), + translate(attr.getFile()), attr.getLine(), + getMDStringOrNull(attr.getName()), llvm::MDNode::get(llvmCtx, elements)); +} + llvm::DISubrange *DebugTranslation::translateImpl(DISubrangeAttr attr) { auto getMetadataOrNull = [&](Attribute attr) -> llvm::Metadata * { if (!attr) diff --git a/mlir/lib/Target/LLVMIR/DebugTranslation.h b/mlir/lib/Target/LLVMIR/DebugTranslation.h index 16a853736226d..37b985acf8541 100644 --- a/mlir/lib/Target/LLVMIR/DebugTranslation.h +++ b/mlir/lib/Target/LLVMIR/DebugTranslation.h @@ -90,6 +90,12 @@ class DebugTranslation { llvm::DISubroutineType *translateImpl(DISubroutineTypeAttr attr); llvm::DIType *translateImpl(DITypeAttr attr); + /// Currently, DIImportedEntityAttr does not have a scope field to avoid a + /// cyclic dependency. The scope information is obtained from the entity + /// which holds the list of DIImportedEntityAttr. This requires that scope + /// information be passed to translate function. + llvm::DIImportedEntity *translate(DIImportedEntityAttr attr, llvm::DIScope *); + /// Attributes that support self recursion need to implement an additional /// method to hook into `translateRecursive`. /// - ` translateTemporaryImpl()`: diff --git a/mlir/test/CAPI/llvm.c b/mlir/test/CAPI/llvm.c index d3054aa6a0d93..da28a96f89691 100644 --- a/mlir/test/CAPI/llvm.c +++ b/mlir/test/CAPI/llvm.c @@ -312,9 +312,15 @@ static void testDebugInfoAttributes(MlirContext ctx) { // CHECK: #llvm.di_subroutine_type<{{.*}}> mlirAttributeDump(subroutine_type); - MlirAttribute di_subprogram = - mlirLLVMDISubprogramAttrGet(ctx, id, compile_unit, compile_unit, foo, bar, - file, 1, 2, 0, subroutine_type); + MlirAttribute di_imported_entity = mlirLLVMDIImportedEntityAttrGet( + ctx, 0, di_module, file, 1, foo, 1, &local_var); + + mlirAttributeDump(di_imported_entity); + // CHECK: #llvm.di_imported_entity<{{.*}}> + + MlirAttribute di_subprogram = mlirLLVMDISubprogramAttrGet( + ctx, id, compile_unit, compile_unit, foo, bar, file, 1, 2, 0, + subroutine_type, 1, &di_imported_entity); // CHECK: #llvm.di_subprogram<{{.*}}> mlirAttributeDump(di_subprogram); diff --git a/mlir/test/Target/LLVMIR/Import/debug-info.ll b/mlir/test/Target/LLVMIR/Import/debug-info.ll index 03c3855a9a324..bb03da37c0d09 100644 --- a/mlir/test/Target/LLVMIR/Import/debug-info.ll +++ b/mlir/test/Target/LLVMIR/Import/debug-info.ll @@ -792,3 +792,28 @@ define void @string_type(ptr %arg1) { ; CHECK-SAME: stringLengthExp = <[DW_OP_push_object_address, DW_OP_plus_uconst(8)]> ; CHECK-SAME: stringLocationExp = <[DW_OP_push_object_address, DW_OP_deref]>> ; CHECK: #di_local_variable1 = #llvm.di_local_variable + +; // ----- + +; Test that imported entities for a functions are handled correctly. + +define void @imp_fn() !dbg !12 { + ret void +} + +!llvm.module.flags = !{!10} +!llvm.dbg.cu = !{!4} + +!2 = !DIModule(scope: !4, name: "mod1", file: !3, line: 1) +!3 = !DIFile(filename: "test.f90", directory: "") +!4 = distinct !DICompileUnit(language: DW_LANG_Fortran95, file: !3) +!8 = !DIModule(scope: !4, name: "mod1", file: !3, line: 5) +!10 = !{i32 2, !"Debug Info Version", i32 3} +!12 = distinct !DISubprogram(name: "imp_fn", linkageName: "imp_fn", scope: !3, file: !3, line: 10, type: !14, scopeLine: 10, spFlags: DISPFlagDefinition, unit: !4, retainedNodes: !16) +!14 = !DISubroutineType(cc: DW_CC_program, types: !15) +!15 = !{} +!16 = !{!17} +!17 = !DIImportedEntity(tag: DW_TAG_imported_module, scope: !12, entity: !8, file: !3, line: 1, elements: !15) + +; CHECK-DAG: #[[M:.+]] = #llvm.di_module<{{.*}}name = "mod1"{{.*}}> +; CHECK-DAG: #[[SP:.+]] = #llvm.di_subprogram<{{.*}}name = "imp_fn"{{.*}}retainedNodes = #llvm.di_imported_entity> diff --git a/mlir/test/Target/LLVMIR/llvmir-debug.mlir b/mlir/test/Target/LLVMIR/llvmir-debug.mlir index 1a9a8561de00d..30b2ba5e9bad1 100644 --- a/mlir/test/Target/LLVMIR/llvmir-debug.mlir +++ b/mlir/test/Target/LLVMIR/llvmir-debug.mlir @@ -366,6 +366,36 @@ llvm.func @fn_with_gl() { // ----- +// Test that imported entries correctly generates 'retainedNodes' in the +// subprogram. + +llvm.func @imp_fn() { + llvm.return +} loc(#loc2) +#file = #llvm.di_file<"test.f90" in ""> +#SP_TY = #llvm.di_subroutine_type +#CU = #llvm.di_compile_unit, + sourceLanguage = DW_LANG_Fortran95, file = #file, isOptimized = false, + emissionKind = Full> +#MOD = #llvm.di_module +#MOD1 = #llvm.di_module +#SP = #llvm.di_subprogram, compileUnit = #CU, scope = #file, + name = "imp_fn", file = #file, subprogramFlags = Definition, type = #SP_TY, + retainedNodes = #llvm.di_imported_entity, #llvm.di_imported_entity> +#loc1 = loc("test.f90":12:14) +#loc2 = loc(fused<#SP>[#loc1]) + +// CHECK-DAG: ![[SP:[0-9]+]] = {{.*}}!DISubprogram(name: "imp_fn"{{.*}}retainedNodes: ![[NODES:[0-9]+]]) +// CHECK-DAG: ![[NODES]] = !{![[NODE2:[0-9]+]], ![[NODE1:[0-9]+]]} +// CHECK-DAG: ![[NODE1]] = !DIImportedEntity(tag: DW_TAG_imported_module, scope: ![[SP]], entity: ![[MOD1:[0-9]+]]{{.*}}) +// CHECK-DAG: ![[NODE2]] = !DIImportedEntity(tag: DW_TAG_imported_module, scope: ![[SP]], entity: ![[MOD2:[0-9]+]]{{.*}}) +// CHECK-DAG: ![[MOD1]] = !DIModule({{.*}}name: "mod1"{{.*}}) +// CHECK-DAG: ![[MOD2]] = !DIModule({{.*}}name: "mod2"{{.*}}) + +// ----- + // Nameless and scopeless global constant. // CHECK-LABEL: @.str.1 = external constant [10 x i8]