[Mlir-commits] [mlir] [MLIR][LLVMIR] Add support for intrinsics with metadata arguments (PR #200308)

Andy Kaylor llvmlistbot at llvm.org
Thu Jun 4 09:43:58 PDT 2026


https://github.com/andykaylor updated https://github.com/llvm/llvm-project/pull/200308

>From 0ba7a6a52bc37bd8bc5ea1a43947d503eb8a8b3f Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Wed, 27 May 2026 16:38:40 -0700
Subject: [PATCH 1/5] [MLIR][LLVMIR] Add support for intrinsics with metadata
 arguments

This updates the LLVM dialect to properly handle intrinsics with
metadata arguments.

Assisted-by: Cursor / claude-opus-4.7
---
 .../mlir/Dialect/LLVMIR/LLVMAttrDefs.td       |   4 +
 .../include/mlir/Dialect/LLVMIR/LLVMOpBase.td |   6 +
 mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td   |  62 +++
 mlir/lib/Target/LLVMIR/ModuleImport.cpp       |  59 +-
 mlir/lib/Target/LLVMIR/TypeToLLVM.cpp         |   5 +-
 mlir/test/Dialect/LLVMIR/call-intrin.mlir     |  24 +
 mlir/test/Dialect/LLVMIR/roundtrip.mlir       |  26 +
 .../LLVMIR/Import/intrinsic-constrained-fp.ll | 512 ++++++++++++++++++
 .../LLVMIR/Import/intrinsic-unregistered.ll   |  35 ++
 9 files changed, 728 insertions(+), 5 deletions(-)
 create mode 100644 mlir/test/Target/LLVMIR/Import/intrinsic-constrained-fp.ll

diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td
index 4304371cc0a54..56f3afba6a030 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td
@@ -1784,4 +1784,8 @@ def LLVM_MDNodeAttr : LLVM_Attr<"MDNode", "md_node"> {
   let assemblyFormat = "`<` (`>`) : ($operands^ `>`)?";
 }
 
+def LLVM_AnyMDAttr : AnyAttrOf<[
+    LLVM_MDStringAttr, LLVM_MDConstantAttr, LLVM_MDFuncAttr, LLVM_MDNodeAttr],
+    "LLVM metadata attribute (md_string, md_const, md_func, or md_node)">;
+
 #endif // LLVMIR_ATTRDEFS
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOpBase.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOpBase.td
index bd59319c79ad3..49b2bbde478e9 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOpBase.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOpBase.td
@@ -35,6 +35,12 @@ def LLVM_TokenType : Type<
   "LLVM token type">,
   BuildableType<"::mlir::LLVM::LLVMTokenType::get($_builder.getContext())">;
 
+// Type constraint accepting LLVM metadata type.
+def LLVM_MetadataType : Type<
+  CPred<"::llvm::isa<::mlir::LLVM::LLVMMetadataType>($_self)">,
+  "LLVM metadata type">,
+  BuildableType<"::mlir::LLVM::LLVMMetadataType::get($_builder.getContext())">;
+
 // Type constraint accepting LLVM primitive types, i.e. all types except void
 // and function.
 def LLVM_PrimitiveType : Type<
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
index d7c8cf236f0da..667352b295ee9 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
@@ -2502,6 +2502,68 @@ def LLVM_InlineAsmOp : LLVM_Op<"inline_asm", [DeclareOpInterfaceMethods<MemoryEf
   let hasVerifier = 1;
 }
 
+//===--------------------------------------------------------------------===//
+// MetadataAsValueOp
+//===--------------------------------------------------------------------===//
+
+def LLVM_MetadataAsValueOp : LLVM_Op<"mlir.metadata_as_value", [Pure]> {
+  let summary =
+      "Wraps an LLVM `metadata` constant so it can be used as an SSA value.";
+  let description = [{
+    Materializes an `!llvm.metadata` SSA value that mirrors LLVM IR's
+    `llvm::MetadataAsValue`: a wrapper that lifts an arbitrary
+    `llvm::Metadata` node into the value domain so it can be used as an
+    operand to instructions that take `metadata` arguments (for example the
+    constrained floating-point intrinsics or `llvm.read_register`).
+
+    The wrapped metadata is described by the `metadata` attribute, which
+    must be one of the LLVM dialect's metadata-attribute classes:
+
+    * `#llvm.md_string<"...">` — `llvm::MDString`.
+    * `#llvm.md_const<...>` — `llvm::ConstantAsMetadata`.
+    * `#llvm.md_func<@symbol>` — `llvm::ValueAsMetadata` of a function.
+    * `#llvm.md_node<...>` — `llvm::MDNode` over any of the above.
+
+    These can be nested arbitrarily to form metadata trees. Lowering to LLVM
+    IR materialises the corresponding `llvm::Metadata` via the dialect's
+    metadata-attribute converter and wraps the result with
+    `llvm::MetadataAsValue::get(ctx, md)`.
+
+    Example:
+
+    ```mlir
+    // Rounding-mode operand of a constrained-FP intrinsic:
+    %rm = llvm.mlir.metadata_as_value #llvm.md_string<"round.tonearest">
+        : !llvm.metadata
+    // Named-register metadata for llvm.read_register:
+    %nr = llvm.mlir.metadata_as_value #llvm.md_node<#llvm.md_string<"sp">>
+        : !llvm.metadata
+    ```
+  }];
+
+  let arguments = (ins LLVM_AnyMDAttr:$metadata);
+  let results = (outs LLVM_MetadataType:$res);
+
+  let assemblyFormat = "$metadata attr-dict";
+
+  let builders = [
+    OpBuilder<(ins "::mlir::Attribute":$metadata), [{
+      build($_builder, $_state,
+            ::mlir::LLVM::LLVMMetadataType::get($_builder.getContext()),
+            metadata);
+    }]>,
+  ];
+
+  let llvmBuilder = [{
+    ::llvm::Metadata *md = convertMetadataAttr(
+        op.getMetadataAttr(), builder, moduleTranslation);
+    if (!md)
+      return ::mlir::emitError(op.getLoc(),
+          "llvm.mlir.metadata_as_value: cannot lower metadata attribute");
+    $res = ::llvm::MetadataAsValue::get(builder.getContext(), md);
+  }];
+}
+
 //===--------------------------------------------------------------------===//
 // CallIntrinsicOp
 //===--------------------------------------------------------------------===//
diff --git a/mlir/lib/Target/LLVMIR/ModuleImport.cpp b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
index eab4379a28610..3b26c1e904884 100644
--- a/mlir/lib/Target/LLVMIR/ModuleImport.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
@@ -149,6 +149,42 @@ static LogicalResult convertInstructionImpl(OpBuilder &odsBuilder,
   return failure();
 }
 
+/// Recursively converts an `llvm::Metadata` node to the matching LLVM dialect
+/// metadata attribute. Returns a null attribute for shapes that the dialect's
+/// metadata-attribute hierarchy does not currently model.
+static Attribute convertMetadataToAttr(MLIRContext *ctx,
+                                       const llvm::Metadata *md) {
+  if (!md)
+    return {};
+  if (auto *mdStr = dyn_cast<llvm::MDString>(md))
+    return MDStringAttr::get(ctx, StringAttr::get(ctx, mdStr->getString()));
+  if (auto *cam = dyn_cast<llvm::ConstantAsMetadata>(md)) {
+    auto *ci = dyn_cast<llvm::ConstantInt>(cam->getValue());
+    if (!ci)
+      return {};
+    auto intType = IntegerType::get(ctx, ci->getBitWidth());
+    return MDConstantAttr::get(ctx, IntegerAttr::get(intType, ci->getValue()));
+  }
+  if (auto *vam = dyn_cast<llvm::ValueAsMetadata>(md)) {
+    auto *fn = dyn_cast<llvm::Function>(vam->getValue());
+    if (!fn)
+      return {};
+    return MDFuncAttr::get(ctx, FlatSymbolRefAttr::get(ctx, fn->getName()));
+  }
+  if (auto *node = dyn_cast<llvm::MDNode>(md)) {
+    SmallVector<Attribute> operands;
+    operands.reserve(node->getNumOperands());
+    for (const llvm::MDOperand &op : node->operands()) {
+      Attribute opAttr = convertMetadataToAttr(ctx, op.get());
+      if (!opAttr)
+        return {};
+      operands.push_back(opAttr);
+    }
+    return MDNodeAttr::get(ctx, operands);
+  }
+  return {};
+}
+
 /// Get a topologically sorted list of blocks for the given basic blocks.
 static SetVector<llvm::BasicBlock *>
 getTopologicallySortedBlocks(ArrayRef<llvm::BasicBlock *> basicBlocks) {
@@ -1980,10 +2016,29 @@ LogicalResult ModuleImport::convertIntrinsicArguments(
     value = nullptr;
   }
 
+  // Convert a single intrinsic operand into an MLIR value.
+  // `llvm::MetadataAsValue` operands (e.g. the rounding-mode / FP-exception
+  // MDString arguments used by the constrained floating-point intrinsics, or
+  // the named-register MDNode used by `llvm.read_register`) are lifted into a
+  // `llvm.mlir.metadata_as_value` SSA op carrying the corresponding metadata
+  // attribute. All other operands go through the standard `convertValue` path,
+  // which rejects metadata values.
+  auto convertOperand = [&](llvm::Value *value) -> FailureOr<Value> {
+    if (auto *mdAsVal = dyn_cast<llvm::MetadataAsValue>(value)) {
+      Attribute mdAttr = convertMetadataToAttr(context, mdAsVal->getMetadata());
+      if (!mdAttr)
+        return failure();
+      return MetadataAsValueOp::create(builder, UnknownLoc::get(context),
+                                       mdAttr)
+          .getRes();
+    }
+    return convertValue(value);
+  };
+
   for (llvm::Value *value : operands) {
     if (!value)
       continue;
-    auto mlirValue = convertValue(value);
+    auto mlirValue = convertOperand(value);
     if (failed(mlirValue))
       return failure();
     valuesOut.push_back(*mlirValue);
@@ -2000,7 +2055,7 @@ LogicalResult ModuleImport::convertIntrinsicArguments(
       opBundleTagAttrs.push_back(StringAttr::get(context, bundle.getTagName()));
 
       for (const llvm::Use &opBundleOperand : bundle.Inputs) {
-        auto operandMlirValue = convertValue(opBundleOperand.get());
+        auto operandMlirValue = convertOperand(opBundleOperand.get());
         if (failed(operandMlirValue))
           return failure();
         valuesOut.push_back(*operandMlirValue);
diff --git a/mlir/lib/Target/LLVMIR/TypeToLLVM.cpp b/mlir/lib/Target/LLVMIR/TypeToLLVM.cpp
index 807a94c61f0c8..34f3d9b671f40 100644
--- a/mlir/lib/Target/LLVMIR/TypeToLLVM.cpp
+++ b/mlir/lib/Target/LLVMIR/TypeToLLVM.cpp
@@ -64,9 +64,8 @@ class TypeToLLVMIRTranslatorImpl {
             .Case([this](LLVM::LLVMLabelType) {
               return llvm::Type::getLabelTy(context);
             })
-            .Case([this](LLVM::LLVMMetadataType) {
-              return llvm::Type::getMetadataTy(context);
-            })
+            .Case<LLVM::LLVMMetadataType>(
+                [this](Type) { return llvm::Type::getMetadataTy(context); })
             .Case([this](LLVM::LLVMX86AMXType) {
               return llvm::Type::getX86_AMXTy(context);
             })
diff --git a/mlir/test/Dialect/LLVMIR/call-intrin.mlir b/mlir/test/Dialect/LLVMIR/call-intrin.mlir
index 81590d8c5f69d..c58a74c0327f1 100644
--- a/mlir/test/Dialect/LLVMIR/call-intrin.mlir
+++ b/mlir/test/Dialect/LLVMIR/call-intrin.mlir
@@ -122,3 +122,27 @@ llvm.func @intrinsic_element_type(%arg0: !llvm.ptr) {
   %0 = llvm.call_intrinsic "llvm.aarch64.ldxr.p0"(%arg0) : (!llvm.ptr {llvm.elementtype = i8}) -> i64
   llvm.return
 }
+
+// -----
+
+// CHECK-LABEL: define float @constrained_sqrt(float %0)
+// CHECK: call float @llvm.experimental.constrained.sqrt.f32(float %{{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict")
+llvm.func @constrained_sqrt(%a: f32) -> f32 {
+  %rm = llvm.mlir.metadata_as_value #llvm.md_string<"round.tonearest">
+  %eb = llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
+  %r = llvm.call_intrinsic "llvm.experimental.constrained.sqrt.f32"(%a, %rm, %eb)
+      : (f32, !llvm.metadata, !llvm.metadata) -> f32
+  llvm.return %r : f32
+}
+
+// -----
+
+// CHECK-LABEL: define i32 @read_named_register()
+// CHECK: call i32 @llvm.read_register.i32(metadata !{{[0-9]+}})
+// CHECK: !{{[0-9]+}} = !{!"sp"}
+llvm.func @read_named_register() -> i32 {
+  %md = llvm.mlir.metadata_as_value #llvm.md_node<#llvm.md_string<"sp">>
+  %r = llvm.call_intrinsic "llvm.read_register.i32"(%md)
+      : (!llvm.metadata) -> i32
+  llvm.return %r : i32
+}
diff --git a/mlir/test/Dialect/LLVMIR/roundtrip.mlir b/mlir/test/Dialect/LLVMIR/roundtrip.mlir
index 83d1f1b8e2884..d0acdfec6c8de 100644
--- a/mlir/test/Dialect/LLVMIR/roundtrip.mlir
+++ b/mlir/test/Dialect/LLVMIR/roundtrip.mlir
@@ -1128,6 +1128,32 @@ llvm.func @intrinsic_call_arg_attrs_bundles(%arg0: i32) -> i32 {
   llvm.return %0 : i32
 }
 
+// CHECK-LABEL: constrained_fp_metadata
+llvm.func @constrained_fp_metadata(%a: f32, %b: f32) -> f32 {
+  // CHECK: %[[RM:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"round.tonearest">
+  // CHECK: %[[EB:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
+  // CHECK: %{{.*}} = llvm.call_intrinsic "llvm.experimental.constrained.fadd.f32"(%{{.*}}, %{{.*}}, %[[RM]], %[[EB]]) : (f32, f32, !llvm.metadata, !llvm.metadata) -> f32
+  %rm = llvm.mlir.metadata_as_value #llvm.md_string<"round.tonearest">
+  %eb = llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
+  %r = llvm.call_intrinsic "llvm.experimental.constrained.fadd.f32"(%a, %b, %rm, %eb)
+      : (f32, f32, !llvm.metadata, !llvm.metadata) -> f32
+  llvm.return %r : f32
+}
+
+// CHECK-LABEL: metadata_as_value_shapes
+llvm.func @metadata_as_value_shapes() {
+  // Exercise each of the LLVM metadata-attribute kinds the op accepts.
+  // CHECK: %{{.*}} = llvm.mlir.metadata_as_value #llvm.md_string<"sp">
+  %0 = llvm.mlir.metadata_as_value #llvm.md_string<"sp">
+  // CHECK: %{{.*}} = llvm.mlir.metadata_as_value #llvm.md_const<42 : i32>
+  %1 = llvm.mlir.metadata_as_value #llvm.md_const<42 : i32>
+  // CHECK: %{{.*}} = llvm.mlir.metadata_as_value #llvm.md_func<@md_kernel>
+  %2 = llvm.mlir.metadata_as_value #llvm.md_func<@md_kernel>
+  // CHECK: %{{.*}} = llvm.mlir.metadata_as_value #llvm.md_node<#llvm.md_string<"sp">>
+  %3 = llvm.mlir.metadata_as_value #llvm.md_node<#llvm.md_string<"sp">>
+  llvm.return
+}
+
 llvm.mlir.global private @blockaddr_global() {addr_space = 0 : i32, dso_local} : !llvm.ptr {
   %0 = llvm.blockaddress <function = @blockaddr_fn, tag = <id = 0>> : !llvm.ptr
   llvm.return %0 : !llvm.ptr
diff --git a/mlir/test/Target/LLVMIR/Import/intrinsic-constrained-fp.ll b/mlir/test/Target/LLVMIR/Import/intrinsic-constrained-fp.ll
new file mode 100644
index 0000000000000..e41a09bcf61e8
--- /dev/null
+++ b/mlir/test/Target/LLVMIR/Import/intrinsic-constrained-fp.ll
@@ -0,0 +1,512 @@
+; RUN: mlir-translate -import-llvm %s -split-input-file | FileCheck %s
+
+; -----------------------------------------------------------------------------
+; FP -> int conversions: one fp arg, single exception-behavior metadata.
+; -----------------------------------------------------------------------------
+
+declare i32 @llvm.experimental.constrained.fptosi.i32.f32(float, metadata)
+
+; CHECK-LABEL: llvm.func @constrained_fptosi
+; CHECK: %[[EB:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
+; CHECK: %{{.*}} = llvm.call_intrinsic "llvm.experimental.constrained.fptosi.i32.f32"(%{{.*}}, %[[EB]]) : (f32, !llvm.metadata) -> i32
+define i32 @constrained_fptosi(float %a) {
+  %r = call i32 @llvm.experimental.constrained.fptosi.i32.f32(float %a, metadata !"fpexcept.strict")
+  ret i32 %r
+}
+
+; // -----
+
+declare i32 @llvm.experimental.constrained.fptoui.i32.f32(float, metadata)
+
+; CHECK-LABEL: llvm.func @constrained_fptoui
+; CHECK: %[[EB:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.ignore">
+; CHECK: %{{.*}} = llvm.call_intrinsic "llvm.experimental.constrained.fptoui.i32.f32"(%{{.*}}, %[[EB]]) : (f32, !llvm.metadata) -> i32
+define i32 @constrained_fptoui(float %a) {
+  %r = call i32 @llvm.experimental.constrained.fptoui.i32.f32(float %a, metadata !"fpexcept.ignore")
+  ret i32 %r
+}
+
+; -----------------------------------------------------------------------------
+; Unary math intrinsics: one fp arg, rounding-mode + exception-behavior.
+; -----------------------------------------------------------------------------
+
+; // -----
+
+declare float @llvm.experimental.constrained.sqrt.f32(float, metadata, metadata)
+
+; CHECK-LABEL: llvm.func @constrained_sqrt
+; CHECK: %[[RM:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"round.tonearest">
+; CHECK: %[[EB:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
+; CHECK: %{{.*}} = llvm.call_intrinsic "llvm.experimental.constrained.sqrt.f32"(%{{.*}}, %[[RM]], %[[EB]]) : (f32, !llvm.metadata, !llvm.metadata) -> f32
+define float @constrained_sqrt(float %a) {
+  %r = call float @llvm.experimental.constrained.sqrt.f32(float %a, metadata !"round.tonearest", metadata !"fpexcept.strict")
+  ret float %r
+}
+
+; // -----
+
+declare float @llvm.experimental.constrained.asin.f32(float, metadata, metadata)
+
+; CHECK-LABEL: llvm.func @constrained_asin
+; CHECK: %[[RM:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"round.tonearest">
+; CHECK: %[[EB:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
+; CHECK: %{{.*}} = llvm.call_intrinsic "llvm.experimental.constrained.asin.f32"(%{{.*}}, %[[RM]], %[[EB]]) : (f32, !llvm.metadata, !llvm.metadata) -> f32
+define float @constrained_asin(float %a) {
+  %r = call float @llvm.experimental.constrained.asin.f32(float %a, metadata !"round.tonearest", metadata !"fpexcept.strict")
+  ret float %r
+}
+
+; // -----
+
+declare float @llvm.experimental.constrained.acos.f32(float, metadata, metadata)
+
+; CHECK-LABEL: llvm.func @constrained_acos
+; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"round.towardzero">
+; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.maytrap">
+; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.acos.f32"
+define float @constrained_acos(float %a) {
+  %r = call float @llvm.experimental.constrained.acos.f32(float %a, metadata !"round.towardzero", metadata !"fpexcept.maytrap")
+  ret float %r
+}
+
+; // -----
+
+declare float @llvm.experimental.constrained.atan.f32(float, metadata, metadata)
+
+; CHECK-LABEL: llvm.func @constrained_atan
+; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"round.upward">
+; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.ignore">
+; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.atan.f32"
+define float @constrained_atan(float %a) {
+  %r = call float @llvm.experimental.constrained.atan.f32(float %a, metadata !"round.upward", metadata !"fpexcept.ignore")
+  ret float %r
+}
+
+; // -----
+
+declare float @llvm.experimental.constrained.sin.f32(float, metadata, metadata)
+
+; CHECK-LABEL: llvm.func @constrained_sin
+; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"round.downward">
+; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
+; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.sin.f32"
+define float @constrained_sin(float %a) {
+  %r = call float @llvm.experimental.constrained.sin.f32(float %a, metadata !"round.downward", metadata !"fpexcept.strict")
+  ret float %r
+}
+
+; // -----
+
+declare float @llvm.experimental.constrained.cos.f32(float, metadata, metadata)
+
+; CHECK-LABEL: llvm.func @constrained_cos
+; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"round.tonearestaway">
+; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.ignore">
+; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.cos.f32"
+define float @constrained_cos(float %a) {
+  %r = call float @llvm.experimental.constrained.cos.f32(float %a, metadata !"round.tonearestaway", metadata !"fpexcept.ignore")
+  ret float %r
+}
+
+; // -----
+
+declare float @llvm.experimental.constrained.tan.f32(float, metadata, metadata)
+
+; CHECK-LABEL: llvm.func @constrained_tan
+; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"round.dynamic">
+; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
+; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.tan.f32"
+define float @constrained_tan(float %a) {
+  %r = call float @llvm.experimental.constrained.tan.f32(float %a, metadata !"round.dynamic", metadata !"fpexcept.strict")
+  ret float %r
+}
+
+; // -----
+
+declare float @llvm.experimental.constrained.sinh.f32(float, metadata, metadata)
+
+; CHECK-LABEL: llvm.func @constrained_sinh
+; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"round.tonearest">
+; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
+; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.sinh.f32"
+define float @constrained_sinh(float %a) {
+  %r = call float @llvm.experimental.constrained.sinh.f32(float %a, metadata !"round.tonearest", metadata !"fpexcept.strict")
+  ret float %r
+}
+
+; // -----
+
+declare float @llvm.experimental.constrained.cosh.f32(float, metadata, metadata)
+
+; CHECK-LABEL: llvm.func @constrained_cosh
+; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"round.tonearest">
+; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
+; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.cosh.f32"
+define float @constrained_cosh(float %a) {
+  %r = call float @llvm.experimental.constrained.cosh.f32(float %a, metadata !"round.tonearest", metadata !"fpexcept.strict")
+  ret float %r
+}
+
+; // -----
+
+declare float @llvm.experimental.constrained.tanh.f32(float, metadata, metadata)
+
+; CHECK-LABEL: llvm.func @constrained_tanh
+; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"round.tonearest">
+; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
+; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.tanh.f32"
+define float @constrained_tanh(float %a) {
+  %r = call float @llvm.experimental.constrained.tanh.f32(float %a, metadata !"round.tonearest", metadata !"fpexcept.strict")
+  ret float %r
+}
+
+; // -----
+
+declare float @llvm.experimental.constrained.log.f32(float, metadata, metadata)
+
+; CHECK-LABEL: llvm.func @constrained_log
+; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"round.tonearest">
+; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
+; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.log.f32"
+define float @constrained_log(float %a) {
+  %r = call float @llvm.experimental.constrained.log.f32(float %a, metadata !"round.tonearest", metadata !"fpexcept.strict")
+  ret float %r
+}
+
+; // -----
+
+declare float @llvm.experimental.constrained.log10.f32(float, metadata, metadata)
+
+; CHECK-LABEL: llvm.func @constrained_log10
+; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"round.tonearest">
+; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
+; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.log10.f32"
+define float @constrained_log10(float %a) {
+  %r = call float @llvm.experimental.constrained.log10.f32(float %a, metadata !"round.tonearest", metadata !"fpexcept.strict")
+  ret float %r
+}
+
+; // -----
+
+declare float @llvm.experimental.constrained.log2.f32(float, metadata, metadata)
+
+; CHECK-LABEL: llvm.func @constrained_log2
+; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"round.tonearest">
+; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
+; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.log2.f32"
+define float @constrained_log2(float %a) {
+  %r = call float @llvm.experimental.constrained.log2.f32(float %a, metadata !"round.tonearest", metadata !"fpexcept.strict")
+  ret float %r
+}
+
+; // -----
+
+declare float @llvm.experimental.constrained.exp.f32(float, metadata, metadata)
+
+; CHECK-LABEL: llvm.func @constrained_exp
+; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"round.tonearest">
+; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
+; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.exp.f32"
+define float @constrained_exp(float %a) {
+  %r = call float @llvm.experimental.constrained.exp.f32(float %a, metadata !"round.tonearest", metadata !"fpexcept.strict")
+  ret float %r
+}
+
+; // -----
+
+declare float @llvm.experimental.constrained.exp2.f32(float, metadata, metadata)
+
+; CHECK-LABEL: llvm.func @constrained_exp2
+; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"round.tonearest">
+; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
+; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.exp2.f32"
+define float @constrained_exp2(float %a) {
+  %r = call float @llvm.experimental.constrained.exp2.f32(float %a, metadata !"round.tonearest", metadata !"fpexcept.strict")
+  ret float %r
+}
+
+; // -----
+
+declare float @llvm.experimental.constrained.rint.f32(float, metadata, metadata)
+
+; CHECK-LABEL: llvm.func @constrained_rint
+; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"round.upward">
+; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
+; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.rint.f32"
+define float @constrained_rint(float %a) {
+  %r = call float @llvm.experimental.constrained.rint.f32(float %a, metadata !"round.upward", metadata !"fpexcept.strict")
+  ret float %r
+}
+
+; // -----
+
+declare float @llvm.experimental.constrained.nearbyint.f32(float, metadata, metadata)
+
+; CHECK-LABEL: llvm.func @constrained_nearbyint
+; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"round.downward">
+; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.ignore">
+; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.nearbyint.f32"
+define float @constrained_nearbyint(float %a) {
+  %r = call float @llvm.experimental.constrained.nearbyint.f32(float %a, metadata !"round.downward", metadata !"fpexcept.ignore")
+  ret float %r
+}
+
+; -----------------------------------------------------------------------------
+; Binary math intrinsics: two fp args, rounding + exception metadata.
+; -----------------------------------------------------------------------------
+
+; // -----
+
+declare float @llvm.experimental.constrained.atan2.f32(float, float, metadata, metadata)
+
+; CHECK-LABEL: llvm.func @constrained_atan2
+; CHECK: %[[RM:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"round.tonearest">
+; CHECK: %[[EB:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
+; CHECK: %{{.*}} = llvm.call_intrinsic "llvm.experimental.constrained.atan2.f32"(%{{.*}}, %{{.*}}, %[[RM]], %[[EB]]) : (f32, f32, !llvm.metadata, !llvm.metadata) -> f32
+define float @constrained_atan2(float %a, float %b) {
+  %r = call float @llvm.experimental.constrained.atan2.f32(float %a, float %b, metadata !"round.tonearest", metadata !"fpexcept.strict")
+  ret float %r
+}
+
+; // -----
+
+declare float @llvm.experimental.constrained.pow.f32(float, float, metadata, metadata)
+
+; CHECK-LABEL: llvm.func @constrained_pow
+; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"round.tonearest">
+; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
+; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.pow.f32"
+define float @constrained_pow(float %a, float %b) {
+  %r = call float @llvm.experimental.constrained.pow.f32(float %a, float %b, metadata !"round.tonearest", metadata !"fpexcept.strict")
+  ret float %r
+}
+
+; -----------------------------------------------------------------------------
+; Mixed-type binary intrinsics.
+; -----------------------------------------------------------------------------
+
+; // -----
+
+declare float @llvm.experimental.constrained.powi.f32(float, i32, metadata, metadata)
+
+; CHECK-LABEL: llvm.func @constrained_powi
+; CHECK: %[[RM:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"round.tonearest">
+; CHECK: %[[EB:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
+; CHECK: %{{.*}} = llvm.call_intrinsic "llvm.experimental.constrained.powi.f32"(%{{.*}}, %{{.*}}, %[[RM]], %[[EB]]) : (f32, i32, !llvm.metadata, !llvm.metadata) -> f32
+define float @constrained_powi(float %a, i32 %b) {
+  %r = call float @llvm.experimental.constrained.powi.f32(float %a, i32 %b, metadata !"round.tonearest", metadata !"fpexcept.strict")
+  ret float %r
+}
+
+; // -----
+
+declare float @llvm.experimental.constrained.ldexp.f32.i32(float, i32, metadata, metadata)
+
+; CHECK-LABEL: llvm.func @constrained_ldexp
+; CHECK: %[[RM:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"round.tonearest">
+; CHECK: %[[EB:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
+; CHECK: %{{.*}} = llvm.call_intrinsic "llvm.experimental.constrained.ldexp.f32.i32"(%{{.*}}, %{{.*}}, %[[RM]], %[[EB]]) : (f32, i32, !llvm.metadata, !llvm.metadata) -> f32
+define float @constrained_ldexp(float %a, i32 %b) {
+  %r = call float @llvm.experimental.constrained.ldexp.f32.i32(float %a, i32 %b, metadata !"round.tonearest", metadata !"fpexcept.strict")
+  ret float %r
+}
+
+; -----------------------------------------------------------------------------
+; FP -> int rounding (lrint / llrint): rounding + exception metadata.
+; -----------------------------------------------------------------------------
+
+; // -----
+
+declare i32 @llvm.experimental.constrained.lrint.i32.f32(float, metadata, metadata)
+
+; CHECK-LABEL: llvm.func @constrained_lrint
+; CHECK: %[[RM:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"round.tonearest">
+; CHECK: %[[EB:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
+; CHECK: %{{.*}} = llvm.call_intrinsic "llvm.experimental.constrained.lrint.i32.f32"(%{{.*}}, %[[RM]], %[[EB]]) : (f32, !llvm.metadata, !llvm.metadata) -> i32
+define i32 @constrained_lrint(float %a) {
+  %r = call i32 @llvm.experimental.constrained.lrint.i32.f32(float %a, metadata !"round.tonearest", metadata !"fpexcept.strict")
+  ret i32 %r
+}
+
+; // -----
+
+declare i64 @llvm.experimental.constrained.llrint.i64.f32(float, metadata, metadata)
+
+; CHECK-LABEL: llvm.func @constrained_llrint
+; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"round.tonearest">
+; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
+; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.llrint.i64.f32"
+define i64 @constrained_llrint(float %a) {
+  %r = call i64 @llvm.experimental.constrained.llrint.i64.f32(float %a, metadata !"round.tonearest", metadata !"fpexcept.strict")
+  ret i64 %r
+}
+
+; -----------------------------------------------------------------------------
+; Binary intrinsics with exception-behavior metadata only (no rounding mode).
+; -----------------------------------------------------------------------------
+
+; // -----
+
+declare float @llvm.experimental.constrained.maxnum.f32(float, float, metadata)
+
+; CHECK-LABEL: llvm.func @constrained_maxnum
+; CHECK: %[[EB:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
+; CHECK: %{{.*}} = llvm.call_intrinsic "llvm.experimental.constrained.maxnum.f32"(%{{.*}}, %{{.*}}, %[[EB]]) : (f32, f32, !llvm.metadata) -> f32
+define float @constrained_maxnum(float %a, float %b) {
+  %r = call float @llvm.experimental.constrained.maxnum.f32(float %a, float %b, metadata !"fpexcept.strict")
+  ret float %r
+}
+
+; // -----
+
+declare float @llvm.experimental.constrained.minnum.f32(float, float, metadata)
+
+; CHECK-LABEL: llvm.func @constrained_minnum
+; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.ignore">
+; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.minnum.f32"
+define float @constrained_minnum(float %a, float %b) {
+  %r = call float @llvm.experimental.constrained.minnum.f32(float %a, float %b, metadata !"fpexcept.ignore")
+  ret float %r
+}
+
+; // -----
+
+declare float @llvm.experimental.constrained.maximum.f32(float, float, metadata)
+
+; CHECK-LABEL: llvm.func @constrained_maximum
+; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.maytrap">
+; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.maximum.f32"
+define float @constrained_maximum(float %a, float %b) {
+  %r = call float @llvm.experimental.constrained.maximum.f32(float %a, float %b, metadata !"fpexcept.maytrap")
+  ret float %r
+}
+
+; // -----
+
+declare float @llvm.experimental.constrained.minimum.f32(float, float, metadata)
+
+; CHECK-LABEL: llvm.func @constrained_minimum
+; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
+; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.minimum.f32"
+define float @constrained_minimum(float %a, float %b) {
+  %r = call float @llvm.experimental.constrained.minimum.f32(float %a, float %b, metadata !"fpexcept.strict")
+  ret float %r
+}
+
+; -----------------------------------------------------------------------------
+; Unary intrinsics with exception-behavior metadata only (no rounding mode).
+; -----------------------------------------------------------------------------
+
+; // -----
+
+declare float @llvm.experimental.constrained.ceil.f32(float, metadata)
+
+; CHECK-LABEL: llvm.func @constrained_ceil
+; CHECK: %[[EB:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
+; CHECK: %{{.*}} = llvm.call_intrinsic "llvm.experimental.constrained.ceil.f32"(%{{.*}}, %[[EB]]) : (f32, !llvm.metadata) -> f32
+define float @constrained_ceil(float %a) {
+  %r = call float @llvm.experimental.constrained.ceil.f32(float %a, metadata !"fpexcept.strict")
+  ret float %r
+}
+
+; // -----
+
+declare float @llvm.experimental.constrained.floor.f32(float, metadata)
+
+; CHECK-LABEL: llvm.func @constrained_floor
+; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
+; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.floor.f32"
+define float @constrained_floor(float %a) {
+  %r = call float @llvm.experimental.constrained.floor.f32(float %a, metadata !"fpexcept.strict")
+  ret float %r
+}
+
+; // -----
+
+declare float @llvm.experimental.constrained.round.f32(float, metadata)
+
+; CHECK-LABEL: llvm.func @constrained_round
+; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
+; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.round.f32"
+define float @constrained_round(float %a) {
+  %r = call float @llvm.experimental.constrained.round.f32(float %a, metadata !"fpexcept.strict")
+  ret float %r
+}
+
+; // -----
+
+declare float @llvm.experimental.constrained.roundeven.f32(float, metadata)
+
+; CHECK-LABEL: llvm.func @constrained_roundeven
+; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
+; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.roundeven.f32"
+define float @constrained_roundeven(float %a) {
+  %r = call float @llvm.experimental.constrained.roundeven.f32(float %a, metadata !"fpexcept.strict")
+  ret float %r
+}
+
+; // -----
+
+declare float @llvm.experimental.constrained.trunc.f32(float, metadata)
+
+; CHECK-LABEL: llvm.func @constrained_trunc
+; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
+; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.trunc.f32"
+define float @constrained_trunc(float %a) {
+  %r = call float @llvm.experimental.constrained.trunc.f32(float %a, metadata !"fpexcept.strict")
+  ret float %r
+}
+
+; // -----
+
+declare i32 @llvm.experimental.constrained.lround.i32.f32(float, metadata)
+
+; CHECK-LABEL: llvm.func @constrained_lround
+; CHECK: %[[EB:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
+; CHECK: %{{.*}} = llvm.call_intrinsic "llvm.experimental.constrained.lround.i32.f32"(%{{.*}}, %[[EB]]) : (f32, !llvm.metadata) -> i32
+define i32 @constrained_lround(float %a) {
+  %r = call i32 @llvm.experimental.constrained.lround.i32.f32(float %a, metadata !"fpexcept.strict")
+  ret i32 %r
+}
+
+; // -----
+
+declare i64 @llvm.experimental.constrained.llround.i64.f32(float, metadata)
+
+; CHECK-LABEL: llvm.func @constrained_llround
+; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
+; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.llround.i64.f32"
+define i64 @constrained_llround(float %a) {
+  %r = call i64 @llvm.experimental.constrained.llround.i64.f32(float %a, metadata !"fpexcept.strict")
+  ret i64 %r
+}
+
+; -----------------------------------------------------------------------------
+; Constrained comparisons (fcmp / fcmps): predicate string + exception metadata.
+; -----------------------------------------------------------------------------
+
+; // -----
+
+declare i1 @llvm.experimental.constrained.fcmp.f32(float, float, metadata, metadata)
+
+; CHECK-LABEL: llvm.func @constrained_fcmp
+; CHECK: %[[PRED:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"oeq">
+; CHECK: %[[EB:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
+; CHECK: %{{.*}} = llvm.call_intrinsic "llvm.experimental.constrained.fcmp.f32"(%{{.*}}, %{{.*}}, %[[PRED]], %[[EB]]) : (f32, f32, !llvm.metadata, !llvm.metadata) -> i1
+define i1 @constrained_fcmp(float %a, float %b) {
+  %r = call i1 @llvm.experimental.constrained.fcmp.f32(float %a, float %b, metadata !"oeq", metadata !"fpexcept.strict")
+  ret i1 %r
+}
+
+; // -----
+
+declare i1 @llvm.experimental.constrained.fcmps.f32(float, float, metadata, metadata)
+
+; CHECK-LABEL: llvm.func @constrained_fcmps
+; CHECK: %[[PRED:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"olt">
+; CHECK: %[[EB:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
+; CHECK: %{{.*}} = llvm.call_intrinsic "llvm.experimental.constrained.fcmps.f32"(%{{.*}}, %{{.*}}, %[[PRED]], %[[EB]]) : (f32, f32, !llvm.metadata, !llvm.metadata) -> i1
+define i1 @constrained_fcmps(float %a, float %b) {
+  %r = call i1 @llvm.experimental.constrained.fcmps.f32(float %a, float %b, metadata !"olt", metadata !"fpexcept.strict")
+  ret i1 %r
+}
diff --git a/mlir/test/Target/LLVMIR/Import/intrinsic-unregistered.ll b/mlir/test/Target/LLVMIR/Import/intrinsic-unregistered.ll
index 46dd4b532cc82..01dd342b7a0ce 100644
--- a/mlir/test/Target/LLVMIR/Import/intrinsic-unregistered.ll
+++ b/mlir/test/Target/LLVMIR/Import/intrinsic-unregistered.ll
@@ -77,3 +77,38 @@ define signext i32 @test_intrin_arg_attr(i32 signext %a) nounwind {
     %val = call i32 @llvm.riscv.sha256sig0(i32 signext %a)
     ret i32 %val
 }
+
+; // -----
+
+; Constrained FP intrinsics with no dedicated MLIR op should fall back to
+; `llvm.call_intrinsic`, and their `metadata !"..."` operands should be
+; imported as `llvm.mlir.metadata_as_value` ops wrapping the corresponding
+; `#llvm.md_string` attribute.
+
+declare float @llvm.experimental.constrained.sqrt.f32(float, metadata, metadata)
+
+; CHECK-LABEL: llvm.func @constrained_sqrt
+; CHECK: %[[RM:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"round.tonearest">
+; CHECK: %[[EB:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
+; CHECK: %{{.*}} = llvm.call_intrinsic "llvm.experimental.constrained.sqrt.f32"(%{{.*}}, %[[RM]], %[[EB]]) : (f32, !llvm.metadata, !llvm.metadata) -> f32
+define float @constrained_sqrt(float %a) {
+  %r = call float @llvm.experimental.constrained.sqrt.f32(float %a, metadata !"round.tonearest", metadata !"fpexcept.strict")
+  ret float %r
+}
+
+; // -----
+
+; Importer should also handle MDNode metadata operands such as the
+; `!{!"register_name"}` form used by `llvm.read_register`.
+
+declare i32 @llvm.read_register.i32(metadata)
+
+; CHECK-LABEL: llvm.func @read_named_register
+; CHECK: %[[MD:.*]] = llvm.mlir.metadata_as_value #llvm.md_node<#llvm.md_string<"sp">>
+; CHECK: %{{.*}} = llvm.call_intrinsic "llvm.read_register.i32"(%[[MD]]) : (!llvm.metadata) -> i32
+define i32 @read_named_register() {
+  %r = call i32 @llvm.read_register.i32(metadata !0)
+  ret i32 %r
+}
+
+!0 = !{!"sp"}

>From c8a80340747b042748e0d59c61570bab5b277e24 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Thu, 28 May 2026 17:39:25 -0700
Subject: [PATCH 2/5] Undo an unnecessary change

---
 mlir/lib/Target/LLVMIR/TypeToLLVM.cpp | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/mlir/lib/Target/LLVMIR/TypeToLLVM.cpp b/mlir/lib/Target/LLVMIR/TypeToLLVM.cpp
index 34f3d9b671f40..807a94c61f0c8 100644
--- a/mlir/lib/Target/LLVMIR/TypeToLLVM.cpp
+++ b/mlir/lib/Target/LLVMIR/TypeToLLVM.cpp
@@ -64,8 +64,9 @@ class TypeToLLVMIRTranslatorImpl {
             .Case([this](LLVM::LLVMLabelType) {
               return llvm::Type::getLabelTy(context);
             })
-            .Case<LLVM::LLVMMetadataType>(
-                [this](Type) { return llvm::Type::getMetadataTy(context); })
+            .Case([this](LLVM::LLVMMetadataType) {
+              return llvm::Type::getMetadataTy(context);
+            })
             .Case([this](LLVM::LLVMX86AMXType) {
               return llvm::Type::getX86_AMXTy(context);
             })

>From 4692905dc90a088c81b5b348f558d400366a4bd7 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Tue, 2 Jun 2026 18:21:12 -0700
Subject: [PATCH 3/5] Address review feedback

---
 mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td   | 13 +--
 mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp    | 13 +++
 mlir/lib/Target/LLVMIR/ModuleImport.cpp       | 86 ++++++++++++-------
 mlir/test/Dialect/LLVMIR/canonicalize.mlir    | 16 ++++
 .../Target/LLVMIR/Import/import-failure.ll    | 12 +++
 5 files changed, 104 insertions(+), 36 deletions(-)

diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
index 667352b295ee9..fc7975bb99f13 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
@@ -2506,7 +2506,8 @@ def LLVM_InlineAsmOp : LLVM_Op<"inline_asm", [DeclareOpInterfaceMethods<MemoryEf
 // MetadataAsValueOp
 //===--------------------------------------------------------------------===//
 
-def LLVM_MetadataAsValueOp : LLVM_Op<"mlir.metadata_as_value", [Pure]> {
+def LLVM_MetadataAsValueOp
+    : LLVM_Op<"mlir.metadata_as_value", [Pure, ConstantLike]> {
   let summary =
       "Wraps an LLVM `metadata` constant so it can be used as an SSA value.";
   let description = [{
@@ -2519,10 +2520,10 @@ def LLVM_MetadataAsValueOp : LLVM_Op<"mlir.metadata_as_value", [Pure]> {
     The wrapped metadata is described by the `metadata` attribute, which
     must be one of the LLVM dialect's metadata-attribute classes:
 
-    * `#llvm.md_string<"...">` — `llvm::MDString`.
-    * `#llvm.md_const<...>` — `llvm::ConstantAsMetadata`.
-    * `#llvm.md_func<@symbol>` — `llvm::ValueAsMetadata` of a function.
-    * `#llvm.md_node<...>` — `llvm::MDNode` over any of the above.
+    * `#llvm.md_string<"...">` -> `llvm::MDString`.
+    * `#llvm.md_const<...>` -> `llvm::ConstantAsMetadata`.
+    * `#llvm.md_func<@symbol>` -> `llvm::ValueAsMetadata` of a function.
+    * `#llvm.md_node<...>` -> `llvm::MDNode` over any of the above.
 
     These can be nested arbitrarily to form metadata trees. Lowering to LLVM
     IR materialises the corresponding `llvm::Metadata` via the dialect's
@@ -2562,6 +2563,8 @@ def LLVM_MetadataAsValueOp : LLVM_Op<"mlir.metadata_as_value", [Pure]> {
           "llvm.mlir.metadata_as_value: cannot lower metadata attribute");
     $res = ::llvm::MetadataAsValue::get(builder.getContext(), md);
   }];
+
+  let hasFolder = 1;
 }
 
 //===--------------------------------------------------------------------===//
diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
index 63bd9f8a3d625..867f5185419f2 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
@@ -3363,6 +3363,15 @@ OpFoldResult LLVM::PoisonOp::fold(FoldAdaptor) {
   return LLVM::PoisonAttr::get(getContext());
 }
 
+//===----------------------------------------------------------------------===//
+// MetadataAsValueOp.
+//===----------------------------------------------------------------------===//
+
+/// Fold a metadata-as-value operation to its wrapped metadata attribute.
+OpFoldResult LLVM::MetadataAsValueOp::fold(FoldAdaptor) {
+  return getMetadataAttr();
+}
+
 //===----------------------------------------------------------------------===//
 // ZeroOp.
 //===----------------------------------------------------------------------===//
@@ -4698,6 +4707,10 @@ Operation *LLVMDialect::materializeConstant(OpBuilder &builder, Attribute value,
     return LLVM::PoisonOp::create(builder, loc, type);
   if (isa<LLVM::ZeroAttr>(value))
     return LLVM::ZeroOp::create(builder, loc, type);
+  if (isa<LLVM::MDStringAttr, LLVM::MDConstantAttr, LLVM::MDFuncAttr,
+          LLVM::MDNodeAttr>(value))
+    if (isa<LLVM::LLVMMetadataType>(type))
+      return LLVM::MetadataAsValueOp::create(builder, loc, type, value);
   // Otherwise try materializing it as a regular llvm.mlir.constant op.
   return LLVM::ConstantOp::materialize(builder, value, type, loc);
 }
diff --git a/mlir/lib/Target/LLVMIR/ModuleImport.cpp b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
index 3b26c1e904884..b04310b07432c 100644
--- a/mlir/lib/Target/LLVMIR/ModuleImport.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
@@ -30,6 +30,7 @@
 #include "llvm/ADT/DepthFirstIterator.h"
 #include "llvm/ADT/PostOrderIterator.h"
 #include "llvm/ADT/ScopeExit.h"
+#include "llvm/ADT/SmallPtrSet.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/TypeSwitch.h"
 #include "llvm/IR/Comdat.h"
@@ -149,11 +150,19 @@ static LogicalResult convertInstructionImpl(OpBuilder &odsBuilder,
   return failure();
 }
 
-/// Recursively converts an `llvm::Metadata` node to the matching LLVM dialect
-/// metadata attribute. Returns a null attribute for shapes that the dialect's
-/// metadata-attribute hierarchy does not currently model.
-static Attribute convertMetadataToAttr(MLIRContext *ctx,
-                                       const llvm::Metadata *md) {
+/// Depth-first conversion of the metadata node `md` to the matching LLVM
+/// dialect metadata attribute. Returns a null attribute for shapes that the
+/// dialect's metadata-attribute hierarchy does not currently model. `path`
+/// holds the metadata nodes on the current depth-first search path. Cyclic
+/// metadata graphs are valid in LLVM IR, but they cannot be expressed by the
+/// immutable, structurally-uniqued metadata attributes built here. The `path`
+/// set lets the traversal recognize such a back-edge and bail out. `attrMap`
+/// caches the attributes of fully converted nodes so that shared subgraphs
+/// are visited only once.
+static Attribute
+convertMetadataToAttrImpl(MLIRContext *ctx, const llvm::Metadata *md,
+                          SmallPtrSetImpl<const llvm::Metadata *> &path,
+                          DenseMap<const llvm::Metadata *, Attribute> &attrMap) {
   if (!md)
     return {};
   if (auto *mdStr = dyn_cast<llvm::MDString>(md))
@@ -172,19 +181,41 @@ static Attribute convertMetadataToAttr(MLIRContext *ctx,
     return MDFuncAttr::get(ctx, FlatSymbolRefAttr::get(ctx, fn->getName()));
   }
   if (auto *node = dyn_cast<llvm::MDNode>(md)) {
+    if (Attribute cached = attrMap.lookup(node))
+      return cached;
+    // If `node` is already on the current search path, this is a back-edge into
+    // a cyclic graph. While that's valid it isn't implemented yet, so bail out.
+    if (!path.insert(node).second)
+      return {};
     SmallVector<Attribute> operands;
     operands.reserve(node->getNumOperands());
     for (const llvm::MDOperand &op : node->operands()) {
-      Attribute opAttr = convertMetadataToAttr(ctx, op.get());
-      if (!opAttr)
+      Attribute opAttr = convertMetadataToAttrImpl(ctx, op.get(), path, attrMap);
+      if (!opAttr) {
+        path.erase(node);
         return {};
+      }
       operands.push_back(opAttr);
     }
-    return MDNodeAttr::get(ctx, operands);
+    path.erase(node);
+    Attribute nodeAttr = MDNodeAttr::get(ctx, operands);
+    attrMap.try_emplace(node, nodeAttr);
+    return nodeAttr;
   }
   return {};
 }
 
+/// Converts the metadata node `md` to the matching LLVM dialect metadata
+/// attribute. Returns a null attribute for shapes that the dialect's
+/// metadata-attribute hierarchy does not currently model, including cyclic
+/// metadata graphs that the immutable metadata attributes cannot express.
+static Attribute convertMetadataToAttr(MLIRContext *ctx,
+                                       const llvm::Metadata *md) {
+  SmallPtrSet<const llvm::Metadata *, 8> path;
+  DenseMap<const llvm::Metadata *, Attribute> attrMap;
+  return convertMetadataToAttrImpl(ctx, md, path, attrMap);
+}
+
 /// Get a topologically sorted list of blocks for the given basic blocks.
 static SetVector<llvm::BasicBlock *>
 getTopologicallySortedBlocks(ArrayRef<llvm::BasicBlock *> basicBlocks) {
@@ -1940,8 +1971,20 @@ FailureOr<Value> ModuleImport::convertConstantExpr(llvm::Constant *constant) {
 }
 
 FailureOr<Value> ModuleImport::convertValue(llvm::Value *value) {
-  assert(!isa<llvm::MetadataAsValue>(value) &&
-         "expected value to not be metadata");
+  // `llvm::MetadataAsValue` operands (e.g. the rounding-mode / FP-exception
+  // MDString arguments used by the constrained floating-point intrinsics, or
+  // the named-register MDNode used by `llvm.read_register`) are lifted into a
+  // `llvm.mlir.metadata_as_value` SSA op carrying the corresponding metadata
+  // attribute.
+  if (auto *mdAsVal = dyn_cast<llvm::MetadataAsValue>(value)) {
+    llvm::Metadata *md = mdAsVal->getMetadata();
+    Attribute mdAttr = convertMetadataToAttr(context, md);
+    if (!mdAttr)
+      return emitError(mlirModule.getLoc())
+             << "unsupported metadata: " << diagMD(md, llvmModule.get());
+    return MetadataAsValueOp::create(builder, UnknownLoc::get(context), mdAttr)
+        .getRes();
+  }
 
   // Return the mapped value if it has been converted before.
   auto it = valueMapping.find(value);
@@ -2016,29 +2059,10 @@ LogicalResult ModuleImport::convertIntrinsicArguments(
     value = nullptr;
   }
 
-  // Convert a single intrinsic operand into an MLIR value.
-  // `llvm::MetadataAsValue` operands (e.g. the rounding-mode / FP-exception
-  // MDString arguments used by the constrained floating-point intrinsics, or
-  // the named-register MDNode used by `llvm.read_register`) are lifted into a
-  // `llvm.mlir.metadata_as_value` SSA op carrying the corresponding metadata
-  // attribute. All other operands go through the standard `convertValue` path,
-  // which rejects metadata values.
-  auto convertOperand = [&](llvm::Value *value) -> FailureOr<Value> {
-    if (auto *mdAsVal = dyn_cast<llvm::MetadataAsValue>(value)) {
-      Attribute mdAttr = convertMetadataToAttr(context, mdAsVal->getMetadata());
-      if (!mdAttr)
-        return failure();
-      return MetadataAsValueOp::create(builder, UnknownLoc::get(context),
-                                       mdAttr)
-          .getRes();
-    }
-    return convertValue(value);
-  };
-
   for (llvm::Value *value : operands) {
     if (!value)
       continue;
-    auto mlirValue = convertOperand(value);
+    auto mlirValue = convertValue(value);
     if (failed(mlirValue))
       return failure();
     valuesOut.push_back(*mlirValue);
@@ -2055,7 +2079,7 @@ LogicalResult ModuleImport::convertIntrinsicArguments(
       opBundleTagAttrs.push_back(StringAttr::get(context, bundle.getTagName()));
 
       for (const llvm::Use &opBundleOperand : bundle.Inputs) {
-        auto operandMlirValue = convertOperand(opBundleOperand.get());
+        auto operandMlirValue = convertValue(opBundleOperand.get());
         if (failed(operandMlirValue))
           return failure();
         valuesOut.push_back(*operandMlirValue);
diff --git a/mlir/test/Dialect/LLVMIR/canonicalize.mlir b/mlir/test/Dialect/LLVMIR/canonicalize.mlir
index b1c2df87d4867..544666fb30099 100644
--- a/mlir/test/Dialect/LLVMIR/canonicalize.mlir
+++ b/mlir/test/Dialect/LLVMIR/canonicalize.mlir
@@ -427,3 +427,19 @@ llvm.func @inline_asm_side_effects(%x : i32) {
   llvm.inline_asm has_side_effects "inline asm with side effects", "r" %x : (i32) -> ()
   llvm.return
 }
+
+// -----
+
+// CHECK-LABEL: func @metadata_as_value_dedup
+llvm.func @metadata_as_value_dedup() -> i32 {
+  // CHECK: %[[MD:.*]] = llvm.mlir.metadata_as_value #llvm.md_node<#llvm.md_string<"sp">>
+  // CHECK-NOT: llvm.mlir.metadata_as_value
+  %0 = llvm.mlir.metadata_as_value #llvm.md_node<#llvm.md_string<"sp">>
+  %1 = llvm.mlir.metadata_as_value #llvm.md_node<#llvm.md_string<"sp">>
+  // CHECK: llvm.call_intrinsic "llvm.read_register.i32"(%[[MD]])
+  %2 = llvm.call_intrinsic "llvm.read_register.i32"(%0) : (!llvm.metadata) -> i32
+  // CHECK: llvm.call_intrinsic "llvm.read_register.i32"(%[[MD]])
+  %3 = llvm.call_intrinsic "llvm.read_register.i32"(%1) : (!llvm.metadata) -> i32
+  %4 = llvm.add %2, %3 : i32
+  llvm.return %4 : i32
+}
diff --git a/mlir/test/Target/LLVMIR/Import/import-failure.ll b/mlir/test/Target/LLVMIR/Import/import-failure.ll
index b468a3e95c907..57c8438bfd362 100644
--- a/mlir/test/Target/LLVMIR/Import/import-failure.ll
+++ b/mlir/test/Target/LLVMIR/Import/import-failure.ll
@@ -456,3 +456,15 @@ bb1:
 !91885 = !{!91886, !91887}
 !91886 = !{i32 10000, i64 86427, i32 1}
 !91887 = !{i32 100000, i64 86427, i32 1}
+
+; // -----
+
+; CHECK: error: unsupported metadata: !{{[0-9]+}} = distinct !{!{{[0-9]+}}}
+declare i32 @llvm.read_register.i32(metadata)
+
+define i32 @cyclic_metadata_as_value() {
+  %r = call i32 @llvm.read_register.i32(metadata !0)
+  ret i32 %r
+}
+
+!0 = distinct !{!0}

>From 762b635502a2cfe045b1b09788ba25fa31c8e08d Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Wed, 3 Jun 2026 11:51:17 -0700
Subject: [PATCH 4/5] Address second round of review feedback

---
 mlir/lib/Target/LLVMIR/ModuleImport.cpp       |  32 +-
 .../LLVMIR/Import/intrinsic-constrained-fp.ll | 512 ------------------
 .../LLVMIR/Import/intrinsic-unregistered.ll   |  17 +-
 3 files changed, 27 insertions(+), 534 deletions(-)
 delete mode 100644 mlir/test/Target/LLVMIR/Import/intrinsic-constrained-fp.ll

diff --git a/mlir/lib/Target/LLVMIR/ModuleImport.cpp b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
index b04310b07432c..c5f6a837bc3ad 100644
--- a/mlir/lib/Target/LLVMIR/ModuleImport.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
@@ -159,10 +159,10 @@ static LogicalResult convertInstructionImpl(OpBuilder &odsBuilder,
 /// set lets the traversal recognize such a back-edge and bail out. `attrMap`
 /// caches the attributes of fully converted nodes so that shared subgraphs
 /// are visited only once.
-static Attribute
-convertMetadataToAttrImpl(MLIRContext *ctx, const llvm::Metadata *md,
-                          SmallPtrSetImpl<const llvm::Metadata *> &path,
-                          DenseMap<const llvm::Metadata *, Attribute> &attrMap) {
+static Attribute convertMetadataToAttrImpl(
+    MLIRContext *ctx, const llvm::Metadata *md,
+    SmallPtrSetImpl<const llvm::Metadata *> &path,
+    DenseMap<const llvm::Metadata *, Attribute> &attrMap) {
   if (!md)
     return {};
   if (auto *mdStr = dyn_cast<llvm::MDString>(md))
@@ -190,11 +190,10 @@ convertMetadataToAttrImpl(MLIRContext *ctx, const llvm::Metadata *md,
     SmallVector<Attribute> operands;
     operands.reserve(node->getNumOperands());
     for (const llvm::MDOperand &op : node->operands()) {
-      Attribute opAttr = convertMetadataToAttrImpl(ctx, op.get(), path, attrMap);
-      if (!opAttr) {
-        path.erase(node);
+      Attribute opAttr =
+          convertMetadataToAttrImpl(ctx, op.get(), path, attrMap);
+      if (!opAttr)
         return {};
-      }
       operands.push_back(opAttr);
     }
     path.erase(node);
@@ -1971,6 +1970,11 @@ FailureOr<Value> ModuleImport::convertConstantExpr(llvm::Constant *constant) {
 }
 
 FailureOr<Value> ModuleImport::convertValue(llvm::Value *value) {
+  // Return the mapped value if it has been converted before.
+  auto it = valueMapping.find(value);
+  if (it != valueMapping.end())
+    return it->getSecond();
+
   // `llvm::MetadataAsValue` operands (e.g. the rounding-mode / FP-exception
   // MDString arguments used by the constrained floating-point intrinsics, or
   // the named-register MDNode used by `llvm.read_register`) are lifted into a
@@ -1982,15 +1986,13 @@ FailureOr<Value> ModuleImport::convertValue(llvm::Value *value) {
     if (!mdAttr)
       return emitError(mlirModule.getLoc())
              << "unsupported metadata: " << diagMD(md, llvmModule.get());
-    return MetadataAsValueOp::create(builder, UnknownLoc::get(context), mdAttr)
-        .getRes();
+    auto op =
+        MetadataAsValueOp::create(builder, UnknownLoc::get(context), mdAttr)
+            .getRes();
+    mapValue(value, op);
+    return op;
   }
 
-  // Return the mapped value if it has been converted before.
-  auto it = valueMapping.find(value);
-  if (it != valueMapping.end())
-    return it->getSecond();
-
   // Convert constants such as immediate values that have no mapping yet.
   if (auto *constant = dyn_cast<llvm::Constant>(value))
     return convertConstantExpr(constant);
diff --git a/mlir/test/Target/LLVMIR/Import/intrinsic-constrained-fp.ll b/mlir/test/Target/LLVMIR/Import/intrinsic-constrained-fp.ll
deleted file mode 100644
index e41a09bcf61e8..0000000000000
--- a/mlir/test/Target/LLVMIR/Import/intrinsic-constrained-fp.ll
+++ /dev/null
@@ -1,512 +0,0 @@
-; RUN: mlir-translate -import-llvm %s -split-input-file | FileCheck %s
-
-; -----------------------------------------------------------------------------
-; FP -> int conversions: one fp arg, single exception-behavior metadata.
-; -----------------------------------------------------------------------------
-
-declare i32 @llvm.experimental.constrained.fptosi.i32.f32(float, metadata)
-
-; CHECK-LABEL: llvm.func @constrained_fptosi
-; CHECK: %[[EB:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
-; CHECK: %{{.*}} = llvm.call_intrinsic "llvm.experimental.constrained.fptosi.i32.f32"(%{{.*}}, %[[EB]]) : (f32, !llvm.metadata) -> i32
-define i32 @constrained_fptosi(float %a) {
-  %r = call i32 @llvm.experimental.constrained.fptosi.i32.f32(float %a, metadata !"fpexcept.strict")
-  ret i32 %r
-}
-
-; // -----
-
-declare i32 @llvm.experimental.constrained.fptoui.i32.f32(float, metadata)
-
-; CHECK-LABEL: llvm.func @constrained_fptoui
-; CHECK: %[[EB:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.ignore">
-; CHECK: %{{.*}} = llvm.call_intrinsic "llvm.experimental.constrained.fptoui.i32.f32"(%{{.*}}, %[[EB]]) : (f32, !llvm.metadata) -> i32
-define i32 @constrained_fptoui(float %a) {
-  %r = call i32 @llvm.experimental.constrained.fptoui.i32.f32(float %a, metadata !"fpexcept.ignore")
-  ret i32 %r
-}
-
-; -----------------------------------------------------------------------------
-; Unary math intrinsics: one fp arg, rounding-mode + exception-behavior.
-; -----------------------------------------------------------------------------
-
-; // -----
-
-declare float @llvm.experimental.constrained.sqrt.f32(float, metadata, metadata)
-
-; CHECK-LABEL: llvm.func @constrained_sqrt
-; CHECK: %[[RM:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"round.tonearest">
-; CHECK: %[[EB:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
-; CHECK: %{{.*}} = llvm.call_intrinsic "llvm.experimental.constrained.sqrt.f32"(%{{.*}}, %[[RM]], %[[EB]]) : (f32, !llvm.metadata, !llvm.metadata) -> f32
-define float @constrained_sqrt(float %a) {
-  %r = call float @llvm.experimental.constrained.sqrt.f32(float %a, metadata !"round.tonearest", metadata !"fpexcept.strict")
-  ret float %r
-}
-
-; // -----
-
-declare float @llvm.experimental.constrained.asin.f32(float, metadata, metadata)
-
-; CHECK-LABEL: llvm.func @constrained_asin
-; CHECK: %[[RM:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"round.tonearest">
-; CHECK: %[[EB:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
-; CHECK: %{{.*}} = llvm.call_intrinsic "llvm.experimental.constrained.asin.f32"(%{{.*}}, %[[RM]], %[[EB]]) : (f32, !llvm.metadata, !llvm.metadata) -> f32
-define float @constrained_asin(float %a) {
-  %r = call float @llvm.experimental.constrained.asin.f32(float %a, metadata !"round.tonearest", metadata !"fpexcept.strict")
-  ret float %r
-}
-
-; // -----
-
-declare float @llvm.experimental.constrained.acos.f32(float, metadata, metadata)
-
-; CHECK-LABEL: llvm.func @constrained_acos
-; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"round.towardzero">
-; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.maytrap">
-; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.acos.f32"
-define float @constrained_acos(float %a) {
-  %r = call float @llvm.experimental.constrained.acos.f32(float %a, metadata !"round.towardzero", metadata !"fpexcept.maytrap")
-  ret float %r
-}
-
-; // -----
-
-declare float @llvm.experimental.constrained.atan.f32(float, metadata, metadata)
-
-; CHECK-LABEL: llvm.func @constrained_atan
-; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"round.upward">
-; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.ignore">
-; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.atan.f32"
-define float @constrained_atan(float %a) {
-  %r = call float @llvm.experimental.constrained.atan.f32(float %a, metadata !"round.upward", metadata !"fpexcept.ignore")
-  ret float %r
-}
-
-; // -----
-
-declare float @llvm.experimental.constrained.sin.f32(float, metadata, metadata)
-
-; CHECK-LABEL: llvm.func @constrained_sin
-; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"round.downward">
-; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
-; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.sin.f32"
-define float @constrained_sin(float %a) {
-  %r = call float @llvm.experimental.constrained.sin.f32(float %a, metadata !"round.downward", metadata !"fpexcept.strict")
-  ret float %r
-}
-
-; // -----
-
-declare float @llvm.experimental.constrained.cos.f32(float, metadata, metadata)
-
-; CHECK-LABEL: llvm.func @constrained_cos
-; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"round.tonearestaway">
-; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.ignore">
-; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.cos.f32"
-define float @constrained_cos(float %a) {
-  %r = call float @llvm.experimental.constrained.cos.f32(float %a, metadata !"round.tonearestaway", metadata !"fpexcept.ignore")
-  ret float %r
-}
-
-; // -----
-
-declare float @llvm.experimental.constrained.tan.f32(float, metadata, metadata)
-
-; CHECK-LABEL: llvm.func @constrained_tan
-; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"round.dynamic">
-; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
-; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.tan.f32"
-define float @constrained_tan(float %a) {
-  %r = call float @llvm.experimental.constrained.tan.f32(float %a, metadata !"round.dynamic", metadata !"fpexcept.strict")
-  ret float %r
-}
-
-; // -----
-
-declare float @llvm.experimental.constrained.sinh.f32(float, metadata, metadata)
-
-; CHECK-LABEL: llvm.func @constrained_sinh
-; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"round.tonearest">
-; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
-; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.sinh.f32"
-define float @constrained_sinh(float %a) {
-  %r = call float @llvm.experimental.constrained.sinh.f32(float %a, metadata !"round.tonearest", metadata !"fpexcept.strict")
-  ret float %r
-}
-
-; // -----
-
-declare float @llvm.experimental.constrained.cosh.f32(float, metadata, metadata)
-
-; CHECK-LABEL: llvm.func @constrained_cosh
-; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"round.tonearest">
-; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
-; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.cosh.f32"
-define float @constrained_cosh(float %a) {
-  %r = call float @llvm.experimental.constrained.cosh.f32(float %a, metadata !"round.tonearest", metadata !"fpexcept.strict")
-  ret float %r
-}
-
-; // -----
-
-declare float @llvm.experimental.constrained.tanh.f32(float, metadata, metadata)
-
-; CHECK-LABEL: llvm.func @constrained_tanh
-; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"round.tonearest">
-; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
-; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.tanh.f32"
-define float @constrained_tanh(float %a) {
-  %r = call float @llvm.experimental.constrained.tanh.f32(float %a, metadata !"round.tonearest", metadata !"fpexcept.strict")
-  ret float %r
-}
-
-; // -----
-
-declare float @llvm.experimental.constrained.log.f32(float, metadata, metadata)
-
-; CHECK-LABEL: llvm.func @constrained_log
-; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"round.tonearest">
-; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
-; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.log.f32"
-define float @constrained_log(float %a) {
-  %r = call float @llvm.experimental.constrained.log.f32(float %a, metadata !"round.tonearest", metadata !"fpexcept.strict")
-  ret float %r
-}
-
-; // -----
-
-declare float @llvm.experimental.constrained.log10.f32(float, metadata, metadata)
-
-; CHECK-LABEL: llvm.func @constrained_log10
-; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"round.tonearest">
-; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
-; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.log10.f32"
-define float @constrained_log10(float %a) {
-  %r = call float @llvm.experimental.constrained.log10.f32(float %a, metadata !"round.tonearest", metadata !"fpexcept.strict")
-  ret float %r
-}
-
-; // -----
-
-declare float @llvm.experimental.constrained.log2.f32(float, metadata, metadata)
-
-; CHECK-LABEL: llvm.func @constrained_log2
-; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"round.tonearest">
-; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
-; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.log2.f32"
-define float @constrained_log2(float %a) {
-  %r = call float @llvm.experimental.constrained.log2.f32(float %a, metadata !"round.tonearest", metadata !"fpexcept.strict")
-  ret float %r
-}
-
-; // -----
-
-declare float @llvm.experimental.constrained.exp.f32(float, metadata, metadata)
-
-; CHECK-LABEL: llvm.func @constrained_exp
-; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"round.tonearest">
-; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
-; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.exp.f32"
-define float @constrained_exp(float %a) {
-  %r = call float @llvm.experimental.constrained.exp.f32(float %a, metadata !"round.tonearest", metadata !"fpexcept.strict")
-  ret float %r
-}
-
-; // -----
-
-declare float @llvm.experimental.constrained.exp2.f32(float, metadata, metadata)
-
-; CHECK-LABEL: llvm.func @constrained_exp2
-; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"round.tonearest">
-; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
-; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.exp2.f32"
-define float @constrained_exp2(float %a) {
-  %r = call float @llvm.experimental.constrained.exp2.f32(float %a, metadata !"round.tonearest", metadata !"fpexcept.strict")
-  ret float %r
-}
-
-; // -----
-
-declare float @llvm.experimental.constrained.rint.f32(float, metadata, metadata)
-
-; CHECK-LABEL: llvm.func @constrained_rint
-; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"round.upward">
-; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
-; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.rint.f32"
-define float @constrained_rint(float %a) {
-  %r = call float @llvm.experimental.constrained.rint.f32(float %a, metadata !"round.upward", metadata !"fpexcept.strict")
-  ret float %r
-}
-
-; // -----
-
-declare float @llvm.experimental.constrained.nearbyint.f32(float, metadata, metadata)
-
-; CHECK-LABEL: llvm.func @constrained_nearbyint
-; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"round.downward">
-; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.ignore">
-; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.nearbyint.f32"
-define float @constrained_nearbyint(float %a) {
-  %r = call float @llvm.experimental.constrained.nearbyint.f32(float %a, metadata !"round.downward", metadata !"fpexcept.ignore")
-  ret float %r
-}
-
-; -----------------------------------------------------------------------------
-; Binary math intrinsics: two fp args, rounding + exception metadata.
-; -----------------------------------------------------------------------------
-
-; // -----
-
-declare float @llvm.experimental.constrained.atan2.f32(float, float, metadata, metadata)
-
-; CHECK-LABEL: llvm.func @constrained_atan2
-; CHECK: %[[RM:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"round.tonearest">
-; CHECK: %[[EB:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
-; CHECK: %{{.*}} = llvm.call_intrinsic "llvm.experimental.constrained.atan2.f32"(%{{.*}}, %{{.*}}, %[[RM]], %[[EB]]) : (f32, f32, !llvm.metadata, !llvm.metadata) -> f32
-define float @constrained_atan2(float %a, float %b) {
-  %r = call float @llvm.experimental.constrained.atan2.f32(float %a, float %b, metadata !"round.tonearest", metadata !"fpexcept.strict")
-  ret float %r
-}
-
-; // -----
-
-declare float @llvm.experimental.constrained.pow.f32(float, float, metadata, metadata)
-
-; CHECK-LABEL: llvm.func @constrained_pow
-; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"round.tonearest">
-; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
-; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.pow.f32"
-define float @constrained_pow(float %a, float %b) {
-  %r = call float @llvm.experimental.constrained.pow.f32(float %a, float %b, metadata !"round.tonearest", metadata !"fpexcept.strict")
-  ret float %r
-}
-
-; -----------------------------------------------------------------------------
-; Mixed-type binary intrinsics.
-; -----------------------------------------------------------------------------
-
-; // -----
-
-declare float @llvm.experimental.constrained.powi.f32(float, i32, metadata, metadata)
-
-; CHECK-LABEL: llvm.func @constrained_powi
-; CHECK: %[[RM:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"round.tonearest">
-; CHECK: %[[EB:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
-; CHECK: %{{.*}} = llvm.call_intrinsic "llvm.experimental.constrained.powi.f32"(%{{.*}}, %{{.*}}, %[[RM]], %[[EB]]) : (f32, i32, !llvm.metadata, !llvm.metadata) -> f32
-define float @constrained_powi(float %a, i32 %b) {
-  %r = call float @llvm.experimental.constrained.powi.f32(float %a, i32 %b, metadata !"round.tonearest", metadata !"fpexcept.strict")
-  ret float %r
-}
-
-; // -----
-
-declare float @llvm.experimental.constrained.ldexp.f32.i32(float, i32, metadata, metadata)
-
-; CHECK-LABEL: llvm.func @constrained_ldexp
-; CHECK: %[[RM:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"round.tonearest">
-; CHECK: %[[EB:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
-; CHECK: %{{.*}} = llvm.call_intrinsic "llvm.experimental.constrained.ldexp.f32.i32"(%{{.*}}, %{{.*}}, %[[RM]], %[[EB]]) : (f32, i32, !llvm.metadata, !llvm.metadata) -> f32
-define float @constrained_ldexp(float %a, i32 %b) {
-  %r = call float @llvm.experimental.constrained.ldexp.f32.i32(float %a, i32 %b, metadata !"round.tonearest", metadata !"fpexcept.strict")
-  ret float %r
-}
-
-; -----------------------------------------------------------------------------
-; FP -> int rounding (lrint / llrint): rounding + exception metadata.
-; -----------------------------------------------------------------------------
-
-; // -----
-
-declare i32 @llvm.experimental.constrained.lrint.i32.f32(float, metadata, metadata)
-
-; CHECK-LABEL: llvm.func @constrained_lrint
-; CHECK: %[[RM:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"round.tonearest">
-; CHECK: %[[EB:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
-; CHECK: %{{.*}} = llvm.call_intrinsic "llvm.experimental.constrained.lrint.i32.f32"(%{{.*}}, %[[RM]], %[[EB]]) : (f32, !llvm.metadata, !llvm.metadata) -> i32
-define i32 @constrained_lrint(float %a) {
-  %r = call i32 @llvm.experimental.constrained.lrint.i32.f32(float %a, metadata !"round.tonearest", metadata !"fpexcept.strict")
-  ret i32 %r
-}
-
-; // -----
-
-declare i64 @llvm.experimental.constrained.llrint.i64.f32(float, metadata, metadata)
-
-; CHECK-LABEL: llvm.func @constrained_llrint
-; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"round.tonearest">
-; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
-; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.llrint.i64.f32"
-define i64 @constrained_llrint(float %a) {
-  %r = call i64 @llvm.experimental.constrained.llrint.i64.f32(float %a, metadata !"round.tonearest", metadata !"fpexcept.strict")
-  ret i64 %r
-}
-
-; -----------------------------------------------------------------------------
-; Binary intrinsics with exception-behavior metadata only (no rounding mode).
-; -----------------------------------------------------------------------------
-
-; // -----
-
-declare float @llvm.experimental.constrained.maxnum.f32(float, float, metadata)
-
-; CHECK-LABEL: llvm.func @constrained_maxnum
-; CHECK: %[[EB:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
-; CHECK: %{{.*}} = llvm.call_intrinsic "llvm.experimental.constrained.maxnum.f32"(%{{.*}}, %{{.*}}, %[[EB]]) : (f32, f32, !llvm.metadata) -> f32
-define float @constrained_maxnum(float %a, float %b) {
-  %r = call float @llvm.experimental.constrained.maxnum.f32(float %a, float %b, metadata !"fpexcept.strict")
-  ret float %r
-}
-
-; // -----
-
-declare float @llvm.experimental.constrained.minnum.f32(float, float, metadata)
-
-; CHECK-LABEL: llvm.func @constrained_minnum
-; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.ignore">
-; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.minnum.f32"
-define float @constrained_minnum(float %a, float %b) {
-  %r = call float @llvm.experimental.constrained.minnum.f32(float %a, float %b, metadata !"fpexcept.ignore")
-  ret float %r
-}
-
-; // -----
-
-declare float @llvm.experimental.constrained.maximum.f32(float, float, metadata)
-
-; CHECK-LABEL: llvm.func @constrained_maximum
-; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.maytrap">
-; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.maximum.f32"
-define float @constrained_maximum(float %a, float %b) {
-  %r = call float @llvm.experimental.constrained.maximum.f32(float %a, float %b, metadata !"fpexcept.maytrap")
-  ret float %r
-}
-
-; // -----
-
-declare float @llvm.experimental.constrained.minimum.f32(float, float, metadata)
-
-; CHECK-LABEL: llvm.func @constrained_minimum
-; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
-; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.minimum.f32"
-define float @constrained_minimum(float %a, float %b) {
-  %r = call float @llvm.experimental.constrained.minimum.f32(float %a, float %b, metadata !"fpexcept.strict")
-  ret float %r
-}
-
-; -----------------------------------------------------------------------------
-; Unary intrinsics with exception-behavior metadata only (no rounding mode).
-; -----------------------------------------------------------------------------
-
-; // -----
-
-declare float @llvm.experimental.constrained.ceil.f32(float, metadata)
-
-; CHECK-LABEL: llvm.func @constrained_ceil
-; CHECK: %[[EB:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
-; CHECK: %{{.*}} = llvm.call_intrinsic "llvm.experimental.constrained.ceil.f32"(%{{.*}}, %[[EB]]) : (f32, !llvm.metadata) -> f32
-define float @constrained_ceil(float %a) {
-  %r = call float @llvm.experimental.constrained.ceil.f32(float %a, metadata !"fpexcept.strict")
-  ret float %r
-}
-
-; // -----
-
-declare float @llvm.experimental.constrained.floor.f32(float, metadata)
-
-; CHECK-LABEL: llvm.func @constrained_floor
-; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
-; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.floor.f32"
-define float @constrained_floor(float %a) {
-  %r = call float @llvm.experimental.constrained.floor.f32(float %a, metadata !"fpexcept.strict")
-  ret float %r
-}
-
-; // -----
-
-declare float @llvm.experimental.constrained.round.f32(float, metadata)
-
-; CHECK-LABEL: llvm.func @constrained_round
-; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
-; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.round.f32"
-define float @constrained_round(float %a) {
-  %r = call float @llvm.experimental.constrained.round.f32(float %a, metadata !"fpexcept.strict")
-  ret float %r
-}
-
-; // -----
-
-declare float @llvm.experimental.constrained.roundeven.f32(float, metadata)
-
-; CHECK-LABEL: llvm.func @constrained_roundeven
-; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
-; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.roundeven.f32"
-define float @constrained_roundeven(float %a) {
-  %r = call float @llvm.experimental.constrained.roundeven.f32(float %a, metadata !"fpexcept.strict")
-  ret float %r
-}
-
-; // -----
-
-declare float @llvm.experimental.constrained.trunc.f32(float, metadata)
-
-; CHECK-LABEL: llvm.func @constrained_trunc
-; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
-; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.trunc.f32"
-define float @constrained_trunc(float %a) {
-  %r = call float @llvm.experimental.constrained.trunc.f32(float %a, metadata !"fpexcept.strict")
-  ret float %r
-}
-
-; // -----
-
-declare i32 @llvm.experimental.constrained.lround.i32.f32(float, metadata)
-
-; CHECK-LABEL: llvm.func @constrained_lround
-; CHECK: %[[EB:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
-; CHECK: %{{.*}} = llvm.call_intrinsic "llvm.experimental.constrained.lround.i32.f32"(%{{.*}}, %[[EB]]) : (f32, !llvm.metadata) -> i32
-define i32 @constrained_lround(float %a) {
-  %r = call i32 @llvm.experimental.constrained.lround.i32.f32(float %a, metadata !"fpexcept.strict")
-  ret i32 %r
-}
-
-; // -----
-
-declare i64 @llvm.experimental.constrained.llround.i64.f32(float, metadata)
-
-; CHECK-LABEL: llvm.func @constrained_llround
-; CHECK: llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
-; CHECK: llvm.call_intrinsic "llvm.experimental.constrained.llround.i64.f32"
-define i64 @constrained_llround(float %a) {
-  %r = call i64 @llvm.experimental.constrained.llround.i64.f32(float %a, metadata !"fpexcept.strict")
-  ret i64 %r
-}
-
-; -----------------------------------------------------------------------------
-; Constrained comparisons (fcmp / fcmps): predicate string + exception metadata.
-; -----------------------------------------------------------------------------
-
-; // -----
-
-declare i1 @llvm.experimental.constrained.fcmp.f32(float, float, metadata, metadata)
-
-; CHECK-LABEL: llvm.func @constrained_fcmp
-; CHECK: %[[PRED:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"oeq">
-; CHECK: %[[EB:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
-; CHECK: %{{.*}} = llvm.call_intrinsic "llvm.experimental.constrained.fcmp.f32"(%{{.*}}, %{{.*}}, %[[PRED]], %[[EB]]) : (f32, f32, !llvm.metadata, !llvm.metadata) -> i1
-define i1 @constrained_fcmp(float %a, float %b) {
-  %r = call i1 @llvm.experimental.constrained.fcmp.f32(float %a, float %b, metadata !"oeq", metadata !"fpexcept.strict")
-  ret i1 %r
-}
-
-; // -----
-
-declare i1 @llvm.experimental.constrained.fcmps.f32(float, float, metadata, metadata)
-
-; CHECK-LABEL: llvm.func @constrained_fcmps
-; CHECK: %[[PRED:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"olt">
-; CHECK: %[[EB:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
-; CHECK: %{{.*}} = llvm.call_intrinsic "llvm.experimental.constrained.fcmps.f32"(%{{.*}}, %{{.*}}, %[[PRED]], %[[EB]]) : (f32, f32, !llvm.metadata, !llvm.metadata) -> i1
-define i1 @constrained_fcmps(float %a, float %b) {
-  %r = call i1 @llvm.experimental.constrained.fcmps.f32(float %a, float %b, metadata !"olt", metadata !"fpexcept.strict")
-  ret i1 %r
-}
diff --git a/mlir/test/Target/LLVMIR/Import/intrinsic-unregistered.ll b/mlir/test/Target/LLVMIR/Import/intrinsic-unregistered.ll
index 01dd342b7a0ce..da58d48d995be 100644
--- a/mlir/test/Target/LLVMIR/Import/intrinsic-unregistered.ll
+++ b/mlir/test/Target/LLVMIR/Import/intrinsic-unregistered.ll
@@ -88,10 +88,10 @@ define signext i32 @test_intrin_arg_attr(i32 signext %a) nounwind {
 declare float @llvm.experimental.constrained.sqrt.f32(float, metadata, metadata)
 
 ; CHECK-LABEL: llvm.func @constrained_sqrt
-; CHECK: %[[RM:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"round.tonearest">
-; CHECK: %[[EB:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
-; CHECK: %{{.*}} = llvm.call_intrinsic "llvm.experimental.constrained.sqrt.f32"(%{{.*}}, %[[RM]], %[[EB]]) : (f32, !llvm.metadata, !llvm.metadata) -> f32
 define float @constrained_sqrt(float %a) {
+  ; CHECK: %[[RM:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"round.tonearest">
+  ; CHECK: %[[EB:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict">
+  ; CHECK: %{{.*}} = llvm.call_intrinsic "llvm.experimental.constrained.sqrt.f32"(%{{.*}}, %[[RM]], %[[EB]]) : (f32, !llvm.metadata, !llvm.metadata) -> f32
   %r = call float @llvm.experimental.constrained.sqrt.f32(float %a, metadata !"round.tonearest", metadata !"fpexcept.strict")
   ret float %r
 }
@@ -104,11 +104,14 @@ define float @constrained_sqrt(float %a) {
 declare i32 @llvm.read_register.i32(metadata)
 
 ; CHECK-LABEL: llvm.func @read_named_register
-; CHECK: %[[MD:.*]] = llvm.mlir.metadata_as_value #llvm.md_node<#llvm.md_string<"sp">>
-; CHECK: %{{.*}} = llvm.call_intrinsic "llvm.read_register.i32"(%[[MD]]) : (!llvm.metadata) -> i32
 define i32 @read_named_register() {
-  %r = call i32 @llvm.read_register.i32(metadata !0)
-  ret i32 %r
+  ; CHECK: %[[MD:.*]] = llvm.mlir.metadata_as_value #llvm.md_node<#llvm.md_string<"sp">>
+  ; CHECK: %[[R0:.*]] = llvm.call_intrinsic "llvm.read_register.i32"(%[[MD]]) : (!llvm.metadata) -> i32
+  %r0 = call i32 @llvm.read_register.i32(metadata !0)
+  ; CHECK-NOT: llvm.mlir.metadata_as_value
+  ; CHECK: %[[R1:.*]] = llvm.call_intrinsic "llvm.read_register.i32"(%[[MD]]) : (!llvm.metadata) -> i32
+  %r1 = call i32 @llvm.read_register.i32(metadata !0)
+  ret i32 %r1
 }
 
 !0 = !{!"sp"}

>From cd270e11f80f73703c9c6da05486d484c640a6bc Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Thu, 4 Jun 2026 09:43:32 -0700
Subject: [PATCH 5/5] Fix last nit

---
 mlir/lib/Target/LLVMIR/ModuleImport.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/mlir/lib/Target/LLVMIR/ModuleImport.cpp b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
index c5f6a837bc3ad..3038a6a2ed986 100644
--- a/mlir/lib/Target/LLVMIR/ModuleImport.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
@@ -1986,11 +1986,11 @@ FailureOr<Value> ModuleImport::convertValue(llvm::Value *value) {
     if (!mdAttr)
       return emitError(mlirModule.getLoc())
              << "unsupported metadata: " << diagMD(md, llvmModule.get());
-    auto op =
+    Value result =
         MetadataAsValueOp::create(builder, UnknownLoc::get(context), mdAttr)
             .getRes();
-    mapValue(value, op);
-    return op;
+    mapValue(value, result);
+    return result;
   }
 
   // Convert constants such as immediate values that have no mapping yet.



More information about the Mlir-commits mailing list