Skip to content

[mlir] Add importedEntities field in DIComplileUnitAttr.#188576

Merged
abidh merged 2 commits into
llvm:mainfrom
abidh:cu_import
Mar 26, 2026
Merged

[mlir] Add importedEntities field in DIComplileUnitAttr.#188576
abidh merged 2 commits into
llvm:mainfrom
abidh:cu_import

Conversation

@abidh
Copy link
Copy Markdown
Contributor

@abidh abidh commented Mar 25, 2026

Mostly mechanical changes to add the missing field. It can help when importing something at non-local level like Fortran modules or C++ namespaces.

@llvmbot
Copy link
Copy Markdown
Member

llvmbot commented Mar 25, 2026

@llvm/pr-subscribers-mlir-llvm

@llvm/pr-subscribers-mlir

Author: Abid Qadeer (abidh)

Changes

Mostly mechanical changes to add the missing field. It can help when importing something at non-local level like Fortran modules or C++ namespaces.


Full diff: https://github.com/llvm/llvm-project/pull/188576.diff

8 Files Affected:

  • (modified) mlir/include/mlir-c/Dialect/LLVM.h (+2-1)
  • (modified) mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td (+6-3)
  • (modified) mlir/lib/CAPI/Dialect/LLVM.cpp (+8-2)
  • (modified) mlir/lib/Target/LLVMIR/DebugImporter.cpp (+8-1)
  • (modified) mlir/lib/Target/LLVMIR/DebugTranslation.cpp (+9-1)
  • (modified) mlir/test/CAPI/llvm.c (+1-1)
  • (modified) mlir/test/Target/LLVMIR/Import/debug-info.ll (+23)
  • (modified) mlir/test/Target/LLVMIR/llvmir-debug.mlir (+30)
diff --git a/mlir/include/mlir-c/Dialect/LLVM.h b/mlir/include/mlir-c/Dialect/LLVM.h
index 4c359b17f7e14..993f85ace1cf2 100644
--- a/mlir/include/mlir-c/Dialect/LLVM.h
+++ b/mlir/include/mlir-c/Dialect/LLVM.h
@@ -344,7 +344,8 @@ MLIR_CAPI_EXPORTED MlirAttribute mlirLLVMDICompileUnitAttrGet(
     MlirContext ctx, MlirAttribute id, unsigned int sourceLanguage,
     MlirAttribute file, MlirAttribute producer, bool isOptimized,
     MlirLLVMDIEmissionKind emissionKind, MlirLLVMDINameTableKind nameTableKind,
-    MlirAttribute splitDebugFilename);
+    MlirAttribute splitDebugFilename, intptr_t nImportedEntities,
+    MlirAttribute const *importedEntities);
 
 MLIR_CAPI_EXPORTED MlirStringRef mlirLLVMDICompileUnitAttrGetName(void);
 
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td
index 36acf244865eb..e10818bad56d6 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td
@@ -426,7 +426,8 @@ def LLVM_DICompileUnitAttr : LLVM_Attr<"DICompileUnit", "di_compile_unit",
     "bool":$isOptimized,
     "DIEmissionKind":$emissionKind,
     OptionalParameter<"DINameTableKind">:$nameTableKind,
-    OptionalParameter<"StringAttr">:$splitDebugFilename
+    OptionalParameter<"StringAttr">:$splitDebugFilename,
+    OptionalArrayRefParameter<"DINodeAttr">:$importedEntities
   );
   let builders = [
     AttrBuilderWithInferredContext<(ins
@@ -434,10 +435,12 @@ def LLVM_DICompileUnitAttr : LLVM_Attr<"DICompileUnit", "di_compile_unit",
       "StringAttr":$producer, "bool":$isOptimized,
       "DIEmissionKind":$emissionKind,
       CArg<"DINameTableKind", "DINameTableKind::Default">:$nameTableKind,
-      CArg<"StringAttr", "{}">:$splitDebugFilename
+      CArg<"StringAttr", "{}">:$splitDebugFilename,
+      CArg<"ArrayRef<DINodeAttr>", "{}">:$importedEntities
     ), [{
       return $_get(id.getContext(), id, sourceLanguage, file, producer,
-                   isOptimized, emissionKind, nameTableKind, splitDebugFilename);
+                   isOptimized, emissionKind, nameTableKind, splitDebugFilename,
+                   importedEntities);
     }]>
   ];
   let assemblyFormat = "`<` struct(params) `>`";
diff --git a/mlir/lib/CAPI/Dialect/LLVM.cpp b/mlir/lib/CAPI/Dialect/LLVM.cpp
index ce5c111b048e4..2e031bae3aa8d 100644
--- a/mlir/lib/CAPI/Dialect/LLVM.cpp
+++ b/mlir/lib/CAPI/Dialect/LLVM.cpp
@@ -322,12 +322,18 @@ MlirAttribute mlirLLVMDICompileUnitAttrGet(
     MlirContext ctx, MlirAttribute id, unsigned int sourceLanguage,
     MlirAttribute file, MlirAttribute producer, bool isOptimized,
     MlirLLVMDIEmissionKind emissionKind, MlirLLVMDINameTableKind nameTableKind,
-    MlirAttribute splitDebugFilename) {
+    MlirAttribute splitDebugFilename, intptr_t nImportedEntities,
+    MlirAttribute const *importedEntities) {
+  SmallVector<Attribute> importsStorage;
+  importsStorage.reserve(nImportedEntities);
   return wrap(DICompileUnitAttr::get(
       unwrap(ctx), cast<DistinctAttr>(unwrap(id)), sourceLanguage,
       cast<DIFileAttr>(unwrap(file)), cast<StringAttr>(unwrap(producer)),
       isOptimized, DIEmissionKind(emissionKind), DINameTableKind(nameTableKind),
-      cast<StringAttr>(unwrap(splitDebugFilename))));
+      cast<StringAttr>(unwrap(splitDebugFilename)),
+      llvm::map_to_vector(
+          unwrapList(nImportedEntities, importedEntities, importsStorage),
+          llvm::CastTo<DINodeAttr>)));
 }
 
 MlirStringRef mlirLLVMDICompileUnitAttrGetName(void) {
diff --git a/mlir/lib/Target/LLVMIR/DebugImporter.cpp b/mlir/lib/Target/LLVMIR/DebugImporter.cpp
index 37140e3a949c7..713fefc7a1f9c 100644
--- a/mlir/lib/Target/LLVMIR/DebugImporter.cpp
+++ b/mlir/lib/Target/LLVMIR/DebugImporter.cpp
@@ -57,12 +57,19 @@ DICompileUnitAttr DebugImporter::translateImpl(llvm::DICompileUnit *node) {
       static_cast<
           std::underlying_type_t<llvm::DICompileUnit::DebugNameTableKind>>(
           node->getNameTableKind()));
+  SmallVector<DINodeAttr> imports;
+  if (node->getImportedEntities()) {
+    for (llvm::DIImportedEntity *ie : node->getImportedEntities()) {
+      if (DINodeAttr na = translate(static_cast<llvm::DINode *>(ie)))
+        imports.push_back(na);
+    }
+  }
   return DICompileUnitAttr::get(
       context, getOrCreateDistinctID(node),
       node->getSourceLanguage().getUnversionedName(),
       translate(node->getFile()), getStringAttrOrNull(node->getRawProducer()),
       node->isOptimized(), emissionKind.value(), nameTableKind.value(),
-      getStringAttrOrNull(node->getRawSplitDebugFilename()));
+      getStringAttrOrNull(node->getRawSplitDebugFilename()), imports);
 }
 
 DICompositeTypeAttr DebugImporter::translateImpl(llvm::DICompositeType *node) {
diff --git a/mlir/lib/Target/LLVMIR/DebugTranslation.cpp b/mlir/lib/Target/LLVMIR/DebugTranslation.cpp
index 08eee68c195db..411335ee8e873 100644
--- a/mlir/lib/Target/LLVMIR/DebugTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/DebugTranslation.cpp
@@ -121,7 +121,7 @@ llvm::DIBasicType *DebugTranslation::translateImpl(DIBasicTypeAttr attr) {
 
 llvm::DICompileUnit *DebugTranslation::translateImpl(DICompileUnitAttr attr) {
   llvm::DIBuilder builder(llvmModule);
-  return builder.createCompileUnit(
+  llvm::DICompileUnit *cu = builder.createCompileUnit(
       attr.getSourceLanguage(), translate(attr.getFile()),
       attr.getProducer() ? attr.getProducer().getValue() : "",
       attr.getIsOptimized(),
@@ -133,6 +133,14 @@ llvm::DICompileUnit *DebugTranslation::translateImpl(DICompileUnitAttr attr) {
       0, true, false,
       static_cast<llvm::DICompileUnit::DebugNameTableKind>(
           attr.getNameTableKind()));
+
+  llvm::SmallVector<llvm::Metadata *> importNodes;
+  for (DINodeAttr n : attr.getImportedEntities())
+    importNodes.push_back(translate(n));
+  if (!importNodes.empty())
+    cu->replaceImportedEntities(llvm::MDTuple::get(llvmCtx, importNodes));
+
+  return cu;
 }
 
 /// Returns a new `DINodeT` that is either distinct or not, depending on
diff --git a/mlir/test/CAPI/llvm.c b/mlir/test/CAPI/llvm.c
index 922491a1f39d6..13b560371d9c9 100644
--- a/mlir/test/CAPI/llvm.c
+++ b/mlir/test/CAPI/llvm.c
@@ -270,7 +270,7 @@ static void testDebugInfoAttributes(MlirContext ctx) {
 
   MlirAttribute compile_unit = mlirLLVMDICompileUnitAttrGet(
       ctx, id, LLVMDWARFSourceLanguageC99, file, foo, false,
-      MlirLLVMDIEmissionKindFull, MlirLLVMDINameTableKindDefault, bar);
+      MlirLLVMDIEmissionKindFull, MlirLLVMDINameTableKindDefault, bar, 0, NULL);
 
   // CHECK: #llvm.di_compile_unit<{{.*}}>
   mlirAttributeDump(compile_unit);
diff --git a/mlir/test/Target/LLVMIR/Import/debug-info.ll b/mlir/test/Target/LLVMIR/Import/debug-info.ll
index 3c2691217e0bf..f365ea50a44c2 100644
--- a/mlir/test/Target/LLVMIR/Import/debug-info.ll
+++ b/mlir/test/Target/LLVMIR/Import/debug-info.ll
@@ -823,6 +823,29 @@ define void @imp_fn() !dbg !12 {
 
 ; // -----
 
+; CHECK-DAG: #[[M:.+]] = #llvm.di_module<{{.*}}name = "mod1"{{.*}}>
+; CHECK-DAG: #[[IE:.+]] = #llvm.di_imported_entity<tag = DW_TAG_imported_module{{.*}}entity = #[[M]]{{.*}}>
+; CHECK-DAG: #llvm.di_compile_unit<{{.*}}importedEntities = #[[IE]]>
+
+define void @compile_unit_imports() !dbg !4 {
+  ret void
+}
+
+!llvm.dbg.cu = !{!1}
+!llvm.module.flags = !{!0}
+
+!0 = !{i32 2, !"Debug Info Version", i32 3}
+!1 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, imports: !5)
+!2 = !DIFile(filename: "debug-info.ll", directory: "/")
+!3 = !DISubroutineType(types: !8)
+!4 = distinct !DISubprogram(name: "compile_unit_imports", linkageName: "compile_unit_imports", scope: !2, file: !2, line: 1, type: !3, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !1)
+!5 = !{!6}
+!6 = !DIImportedEntity(tag: DW_TAG_imported_module, scope: !2, entity: !7)
+!7 = !DIModule(scope: !2, name: "mod1", line: 5)
+!8 = !{}
+
+; // -----
+
 ; Test that annotations are handled correctly
 
 ; CHECK-LABEL: @fn_with_annotations
diff --git a/mlir/test/Target/LLVMIR/llvmir-debug.mlir b/mlir/test/Target/LLVMIR/llvmir-debug.mlir
index 1aa6362fc42d3..a579a2adb5857 100644
--- a/mlir/test/Target/LLVMIR/llvmir-debug.mlir
+++ b/mlir/test/Target/LLVMIR/llvmir-debug.mlir
@@ -740,3 +740,33 @@ llvm.mlir.global @data() {dbg_exprs = [#var_expression, #var_expression1]} : i64
 // CHECK: ![[VAR1]] = {{.*}}!DIGlobalVariable(name: "a"{{.*}})
 // CHECK: ![[EXP2]] = !DIGlobalVariableExpression(var: ![[VAR2:[0-9]+]], expr: !DIExpression())
 // CHECK: ![[VAR2]] = {{.*}}!DIGlobalVariable(name: "b"{{.*}})
+
+// -----
+
+// Test lowering compile unit `importedEntities` to LLVM IR `imports`.
+
+#file = #llvm.di_file<"test.mlir" in "">
+#ns = #llvm.di_namespace<name = "imported_ns", exportSymbols = false>
+#ie = #llvm.di_imported_entity<tag = DW_TAG_imported_module, scope = #file, entity = #ns, file = #file>
+#cu = #llvm.di_compile_unit<
+  id = distinct[0]<>, sourceLanguage = DW_LANG_C, file = #file,
+  producer = "MLIR", isOptimized = false, emissionKind = Full,
+  importedEntities = #ie
+>
+#sp_ty = #llvm.di_subroutine_type<callingConvention = DW_CC_normal>
+#sp = #llvm.di_subprogram<
+  compileUnit = #cu, scope = #file, name = "fn_cu_imports",
+  file = #file, line = 1, scopeLine = 1, subprogramFlags = Definition,
+  type = #sp_ty
+>
+
+// CHECK-LABEL: define void @fn_cu_imports()
+llvm.func @fn_cu_imports() {
+  llvm.return
+} loc(fused<#sp>["cu_imports.mlir":1:1])
+
+// CHECK-DAG: ![[FILE:[0-9]+]] = !DIFile(filename: "test.mlir", directory: "")
+// CHECK-DAG: ![[NS:[0-9]+]] = !DINamespace(name: "imported_ns"{{.*}})
+// CHECK-DAG: ![[IE:[0-9]+]] = !DIImportedEntity(tag: DW_TAG_imported_module{{.*}}entity: ![[NS]]{{.*}})
+// CHECK-DAG: ![[IMP_LIST:[0-9]+]] = !{![[IE]]}
+// CHECK-DAG: !DICompileUnit({{.*}}imports: ![[IMP_LIST]])

Copy link
Copy Markdown
Contributor

@gysit gysit left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for extending the debug info!

I believe the bytecode tablegen needs to be updated as well?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also update the byte code tablegen mlir/include/mlir/Dialect/LLVMIR/LLVMDialectBytecode.td to reflect this change, and add a byte code roundtrip test in mlir/test/Dialect/LLVMIR/debuginfo.mlir?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing it out. Done.

Comment on lines 61 to 66
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (node->getImportedEntities())
for (llvm::DIImportedEntity *ie : node->getImportedEntities())
if (DINodeAttr import = translate(static_cast<llvm::DINode *>(ie)))
imports.push_back(import);

ultra nit: I think the braces are not recommended here plus I would use a slightly longer variable name.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment on lines 138 to 139
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for (DINodeAttr importNode : attr.getImportedEntities())
importNodes.push_back(translate(importNode));

ultra nit: I prefer longer variable names.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Copy Markdown
Contributor

@gysit gysit left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for addressing the comments.

LGTM

abidh added 2 commits March 26, 2026 16:42
Mostly mechanical changes to add the missing field. It can be help when importing something at non-local level like fortran modules or C++ namespaces.
1. Added field in LLVMDialectBytecode.td. Also added another missing field.

2. Updated test in debuginfo.mlir

3. Improve variable names
@abidh abidh merged commit b912bad into llvm:main Mar 26, 2026
10 checks passed
Aadarsh-Keshri pushed a commit to Aadarsh-Keshri/llvm-project that referenced this pull request Mar 28, 2026
Mostly mechanical changes to add the missing field. It can help when
importing something at non-local level like Fortran modules or C++
namespaces.
fzou1 pushed a commit to fzou1/llvm-project that referenced this pull request Mar 30, 2026
Mostly mechanical changes to add the missing field. It can help when
importing something at non-local level like Fortran modules or C++
namespaces.
@Dinistro
Copy link
Copy Markdown
Contributor

Dinistro commented Apr 2, 2026

It seems that these importedEntities can form cyclic structures in LLVM IR. Feeding such an input to the importer causes a crash.

An example input is

source_filename = "reduced.ll"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

@__lookup_table_f64_log_coarse = external constant [512 x i128], !dbg !0

!llvm.dbg.cu = !{!2}
!llvm.module.flags = !{!17}

!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
!1 = distinct !DIGlobalVariable(name: "__lookup_table_f64_log_coarse", scope: !2, file: !12, line: 7, type: !13, isLocal: false, isDefinition: true)
!2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !3, producer: "clang version 19.1.7", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, retainedTypes: !4, globals: !4, imports: !5, splitDebugInlining: false, nameTableKind: None)
!3 = !DIFile(filename: "f64_log.cpp", directory: "/build")
!4 = !{}
!5 = !{!6}
!6 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !7, file: !11, line: 10)
!7 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "fma_u128_t", scope: !9, file: !8, line: 495, size: 128, flags: DIFlagTypePassByValue | DIFlagNonTrivial, elements: !4, identifier: "_ZTSN3foo5types10fma_u128_tE")
!8 = !DIFile(filename: "fma_definitions.hpp", directory: "/build")
!9 = !DINamespace(name: "types", scope: !10)
!10 = !DINamespace(name: "foo", scope: null)
!11 = !DIFile(filename: "arithmetic_helpers.hpp", directory: "/build")
!12 = !DIFile(filename: "f64_log_coarse.hpp", directory: "/build")
!13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !14, size: 65536, elements: !4)
!14 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !15)
!15 = !DIDerivedType(tag: DW_TAG_typedef, name: "__uint128_t", file: !3, baseType: !16)
!16 = !DIBasicType(name: "unsigned __int128", size: 128, encoding: DW_ATE_unsigned)
!17 = !{i32 2, !"Debug Info Version", i32 3}

@abidh
Copy link
Copy Markdown
Contributor Author

abidh commented Apr 3, 2026

I think DICompileUnitAttr should also have DIRecursiveTypeAttrInterface to handle such cases. I have a patch that does that. Will open a PR.

@Dinistro
Copy link
Copy Markdown
Contributor

Dinistro commented Apr 7, 2026

@abidh What's the status of this? This is currently blocking down-stream integration for us.

@abidh
Copy link
Copy Markdown
Contributor Author

abidh commented Apr 7, 2026

Hi @Dinistro
Sorry it was easter holidays in the UK. I will try to get on it today. Although I wonder how it was working before. I guess because we just ignored the imports field in the DICompileUnit while translating.

@Dinistro
Copy link
Copy Markdown
Contributor

Dinistro commented Apr 7, 2026

Hi @Dinistro Sorry it was easter holidays in the UK. I will try to get on it today. Although I wonder how it was working before. I guess because we just ignored the imports field in the DICompileUnit while translating.

I guess the cycle was only introduced once the DIImportedEntity are being properly imported. Before that, the cycle was broken during the import.

@abidh
Copy link
Copy Markdown
Contributor Author

abidh commented Apr 7, 2026

#190808

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants