[Mlir-commits] [mlir] [MLIR][LLVMIR] Handle MDTuple-of-MDStrings module flags (e.g. riscv-isa) (PR #188741)

Mehdi Amini llvmlistbot at llvm.org
Mon Apr 13 04:22:58 PDT 2026


https://github.com/joker-eph updated https://github.com/llvm/llvm-project/pull/188741

>From befb17f428460f87a794a1ebce0f76bf83d4f48d Mon Sep 17 00:00:00 2001
From: Mehdi Amini <joker.eph at gmail.com>
Date: Thu, 26 Mar 2026 05:39:51 -0700
Subject: [PATCH] [MLIR][LLVMIR] Handle MDTuple-of-MDStrings module flags (e.g.
 riscv-isa)

The "riscv-isa" LLVM module flag stores its value as an MDTuple containing
MDStrings (e.g. `\!{\!"rv64i2p1", \!"m2p0"}`). Previously, this fell through the
unrecognized-key path in `convertModuleFlagValueFromMDTuple`, which emitted a
warning and dropped the flag during import.

This patch adds generic handling for MDTuples whose operands are all MDStrings:
- Import: convert to `ArrayAttr<StringAttr>` in `convertModuleFlagValueFromMDTuple`
- Export: convert `ArrayAttr<StringAttr>` back to an MDTuple of MDStrings in
  `convertModuleFlagValue`, enabling a lossless round-trip
- Verifier: allow `ArrayAttr<StringAttr>` as a valid `ModuleFlagAttr` value
  for keys not otherwise handled by specific verifier branches

Fixes #188122

Assisted-by: Claude Code
---
 mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp           | 14 ++++++++++++--
 .../Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp     |  9 +++++++++
 mlir/lib/Target/LLVMIR/ModuleImport.cpp            | 13 ++++++++++++-
 mlir/test/Dialect/LLVMIR/invalid.mlir              |  2 +-
 mlir/test/Target/LLVMIR/Import/module-flags.ll     | 12 ++++++++++++
 mlir/test/Target/LLVMIR/llvmir.mlir                | 11 +++++++++++
 6 files changed, 57 insertions(+), 4 deletions(-)

diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp
index 18fb2e7aadfe2..674a406cb368b 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp
@@ -553,7 +553,17 @@ ModuleFlagAttr::verify(function_ref<InFlightDiagnostic()> emitError,
   if (isa<IntegerAttr, StringAttr>(value))
     return success();
 
-  return emitError() << "only integer and string values are currently "
-                        "supported for unknown key '"
+  // Allow non-empty ArrayAttr of StringAttrs to represent MDTuples of
+  // MDStrings (e.g. the "riscv-isa" module flag). Integer values within
+  // MDTuples are not handled here because integer module flags are encoded as
+  // ConstantAsMetadata at the top level (not as MDTuples), so no known use
+  // case requires an array-of-integers representation.
+  if (auto arrayAttr = dyn_cast<ArrayAttr>(value))
+    if (!arrayAttr.empty() &&
+        llvm::all_of(arrayAttr, [](Attribute a) { return isa<StringAttr>(a); }))
+      return success();
+
+  return emitError() << "only integer, string, and string-array values are "
+                        "currently supported for unknown key '"
                      << key << "'";
 }
diff --git a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
index 9b376e3f9e1b7..62018e8c9f6bb 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
@@ -311,6 +311,15 @@ convertModuleFlagValue(StringRef key, ArrayAttr arrayAttr,
     }
     return llvm::MDTuple::getDistinct(context, nodes);
   }
+  // Handle ArrayAttr of StringAttrs (e.g. "riscv-isa") by converting back to
+  // an MDTuple of MDStrings for a lossless round-trip.
+  if (llvm::all_of(arrayAttr, [](Attribute a) { return isa<StringAttr>(a); })) {
+    assert(!arrayAttr.empty() &&
+           "empty string-array is invalid per ModuleFlagAttr::verify");
+    for (StringAttr strAttr : arrayAttr.getAsRange<StringAttr>())
+      nodes.push_back(llvm::MDString::get(context, strAttr.getValue()));
+    return llvm::MDTuple::get(context, nodes);
+  }
   return nullptr;
 }
 
diff --git a/mlir/lib/Target/LLVMIR/ModuleImport.cpp b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
index 4ea16800d8982..eab4379a28610 100644
--- a/mlir/lib/Target/LLVMIR/ModuleImport.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
@@ -841,7 +841,18 @@ convertModuleFlagValueFromMDTuple(ModuleOp mlirModule,
   if (key == LLVMDialect::getModuleFlagKeyProfileSummaryName())
     return convertProfileSummaryModuleFlagValue(mlirModule, llvmModule,
                                                 mdTuple);
-  return nullptr;
+  // Handle MDTuples whose operands are all MDStrings (e.g. "riscv-isa").
+  // Convert them to ArrayAttr of StringAttrs for a lossless round-trip.
+  Builder builder(mlirModule->getContext());
+  SmallVector<Attribute> strings;
+  strings.reserve(mdTuple->getNumOperands());
+  for (const llvm::MDOperand &operand : mdTuple->operands()) {
+    auto *mdString = dyn_cast_if_present<llvm::MDString>(operand.get());
+    if (!mdString)
+      return nullptr;
+    strings.push_back(builder.getStringAttr(mdString->getString()));
+  }
+  return builder.getArrayAttr(strings);
 }
 
 LogicalResult ModuleImport::convertModuleFlagsMetadata() {
diff --git a/mlir/test/Dialect/LLVMIR/invalid.mlir b/mlir/test/Dialect/LLVMIR/invalid.mlir
index f51948eec1b24..e849b59b846f7 100644
--- a/mlir/test/Dialect/LLVMIR/invalid.mlir
+++ b/mlir/test/Dialect/LLVMIR/invalid.mlir
@@ -1850,7 +1850,7 @@ llvm.mlir.alias external @y5 : i32 {
 module {
   llvm.func @foo()
 
-  // expected-error at below {{only integer and string values are currently supported}}
+  // expected-error at below {{only integer, string, and string-array values are currently supported for unknown key '"yolo"'}}
   llvm.module_flags [#llvm.mlir.module_flag<error, "yolo", @foo>]
 }
 
diff --git a/mlir/test/Target/LLVMIR/Import/module-flags.ll b/mlir/test/Target/LLVMIR/Import/module-flags.ll
index 725bd14deb651..e406bf985ff90 100644
--- a/mlir/test/Target/LLVMIR/Import/module-flags.ll
+++ b/mlir/test/Target/LLVMIR/Import/module-flags.ll
@@ -95,3 +95,15 @@ declare void @to()
 ; CHECK-SAME: <cut_off = 10000, min_count = 86427, num_counts = 1>,
 ; CHECK-SAME: <cut_off = 100000, min_count = 86427, num_counts = 1>
 ; CHECK-SAME: >>]
+
+; // -----
+
+; Test that MDTuples of MDStrings (e.g. "riscv-isa") are imported as ArrayAttr
+; of StringAttrs rather than emitting a warning and dropping the flag.
+
+!llvm.module.flags = !{!0}
+
+!0 = !{i32 1, !"riscv-isa", !1}
+!1 = !{!"rv64i2p1", !"m2p0"}
+
+; CHECK: llvm.module_flags [#llvm.mlir.module_flag<error, "riscv-isa", ["rv64i2p1", "m2p0"]>]
diff --git a/mlir/test/Target/LLVMIR/llvmir.mlir b/mlir/test/Target/LLVMIR/llvmir.mlir
index 6882de8ed446c..ef4082d88853f 100644
--- a/mlir/test/Target/LLVMIR/llvmir.mlir
+++ b/mlir/test/Target/LLVMIR/llvmir.mlir
@@ -3401,6 +3401,17 @@ llvm.module_flags [#llvm.mlir.module_flag<error, "ProfileSummary",
 
 // -----
 
+// Test that ArrayAttr of StringAttrs (e.g. "riscv-isa") is exported as an
+// MDTuple of MDStrings for a lossless round-trip.
+
+llvm.module_flags [#llvm.mlir.module_flag<error, "riscv-isa", ["rv64i2p1", "m2p0"]>]
+
+// CHECK: !llvm.module.flags = !{![[#RISCV:]], {{.*}}}
+// CHECK: ![[#RISCV]] = !{i32 1, !"riscv-isa", ![[#ISA:]]}
+// CHECK: ![[#ISA]] = !{!"rv64i2p1", !"m2p0"}
+
+// -----
+
 module attributes {llvm.dependent_libraries = ["foo", "bar"]} {}
 
 // CHECK: !llvm.dependent-libraries =  !{![[#LIBFOO:]], ![[#LIBBAR:]]}



More information about the Mlir-commits mailing list