[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