[Mlir-commits] [mlir] [mlir][SPIR-V] Let FConvert cast between same width types (PR #225349)

Arseniy Obolenskiy llvmlistbot at llvm.org
Tue Sep 22 03:51:04 PDT 2026


https://github.com/aobolensk updated https://github.com/llvm/llvm-project/pull/225349

>From 4a279ef1d4e943cfef62209bd77fd7fc2d84ae37 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Tue, 22 Sep 2026 12:04:46 +0200
Subject: [PATCH] [mlir][SPIR-V] Let FConvert cast between same width types

The spec requires the component type to differ, not the width, so casts like f16 to bf16 are legal
---
 .../mlir/Dialect/SPIRV/IR/SPIRVCastOps.td     |  4 +-
 mlir/lib/Dialect/SPIRV/IR/CastOps.cpp         | 48 +++++++++++++++----
 mlir/test/Dialect/SPIRV/IR/cast-ops.mlir      | 10 +++-
 mlir/test/Target/SPIRV/cast-ops.mlir          |  7 ++-
 4 files changed, 55 insertions(+), 14 deletions(-)

diff --git a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVCastOps.td b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVCastOps.td
index a434764750a56..7a85a386900f0 100644
--- a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVCastOps.td
+++ b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVCastOps.td
@@ -204,8 +204,8 @@ def SPIRV_FConvertOp : SPIRV_CastOp<"FConvert",
     Result Type must be a scalar or vector of floating-point type.
 
     Float Value must be a scalar or vector of floating-point type.  It must
-    have the same number of components as Result Type.  The component width
-    cannot equal the component width in Result Type.
+    have the same number of components as Result Type.  The component type
+    must not equal the component type in Result Type.
 
     Results are computed per component.
 
diff --git a/mlir/lib/Dialect/SPIRV/IR/CastOps.cpp b/mlir/lib/Dialect/SPIRV/IR/CastOps.cpp
index e8bbb8016baf6..5884afca0fe77 100644
--- a/mlir/lib/Dialect/SPIRV/IR/CastOps.cpp
+++ b/mlir/lib/Dialect/SPIRV/IR/CastOps.cpp
@@ -21,13 +21,8 @@ using namespace mlir::spirv::AttrNames;
 
 namespace mlir::spirv {
 
-static LogicalResult verifyCastOp(Operation *op,
-                                  bool requireSameBitWidth = true,
-                                  bool skipBitWidthCheck = false) {
-  // Some CastOps have no limit on bit widths for result and operand type.
-  if (skipBitWidthCheck)
-    return success();
-
+static FailureOr<std::pair<Type, Type>>
+getCastOpOperandAndResultElementType(Operation *op) {
   Type operandType = op->getOperand(0).getType();
   Type resultType = op->getResult(0).getType();
 
@@ -49,8 +44,26 @@ static LogicalResult verifyCastOp(Operation *op,
             return {operandType, resultType};
           });
 
-  if (!operandElemTy || !resultElemTy)
-    return op->emitOpError("incompatible operand and result types");
+  if (!operandElemTy || !resultElemTy) {
+    op->emitOpError("incompatible operand and result types");
+    return failure();
+  }
+
+  return TypePair{operandElemTy, resultElemTy};
+}
+
+static LogicalResult verifyCastOp(Operation *op,
+                                  bool requireSameBitWidth = true,
+                                  bool skipBitWidthCheck = false) {
+  // Some CastOps have no limit on bit widths for result and operand type.
+  if (skipBitWidthCheck)
+    return success();
+
+  FailureOr<std::pair<Type, Type>> elemTypes =
+      getCastOpOperandAndResultElementType(op);
+  if (failed(elemTypes))
+    return failure();
+  auto [operandElemTy, resultElemTy] = *elemTypes;
 
   unsigned operandTypeBitWidth = operandElemTy.getIntOrFloatBitWidth();
   unsigned resultTypeBitWidth = resultElemTy.getIntOrFloatBitWidth();
@@ -304,7 +317,22 @@ LogicalResult ConvertUToFOp::verify() {
 //===----------------------------------------------------------------------===//
 
 LogicalResult spirv::FConvertOp::verify() {
-  return verifyCastOp(*this, /*requireSameBitWidth=*/false);
+  // The SPIR-V spec requires the component type to differ, not the bit
+  // width: "The component type must not equal the component type in Result
+  // Type." (OpFConvert, section 3.42.11). This allows converting between
+  // same-width encodings such as f16 and bf16.
+  FailureOr<std::pair<Type, Type>> elemTypes =
+      getCastOpOperandAndResultElementType(*this);
+  if (failed(elemTypes))
+    return failure();
+  auto [operandElemTy, resultElemTy] = *elemTypes;
+
+  if (operandElemTy == resultElemTy) {
+    return emitOpError("expected different component types for operand type "
+                       "and result type, but provided ")
+           << operandElemTy << " and " << resultElemTy;
+  }
+  return success();
 }
 
 //===----------------------------------------------------------------------===//
diff --git a/mlir/test/Dialect/SPIRV/IR/cast-ops.mlir b/mlir/test/Dialect/SPIRV/IR/cast-ops.mlir
index 2bd992979e2ab..e5ea675a1f243 100644
--- a/mlir/test/Dialect/SPIRV/IR/cast-ops.mlir
+++ b/mlir/test/Dialect/SPIRV/IR/cast-ops.mlir
@@ -309,7 +309,7 @@ func.func @f_convert_coop_matrix(%arg0 : !spirv.coopmatrix<8x16xf32, Subgroup, M
 // -----
 
 func.func @f_convert_vector(%arg0 : f32) -> f32 {
-  // expected-error @+1 {{expected the different bit widths for operand type and result type, but provided 'f32' and 'f32'}}
+  // expected-error @+1 {{expected different component types for operand type and result type, but provided 'f32' and 'f32'}}
   %0 = spirv.FConvert %arg0 : f32 to f32
   spirv.ReturnValue %0 : f32
 }
@@ -324,6 +324,14 @@ func.func @f_convert_bf16_to_f32_scalar(%arg0 : bf16) -> f32 {
 
 // -----
 
+func.func @f_convert_f16_to_bf16_scalar(%arg0 : f16) -> bf16 {
+  // CHECK: {{%.*}} = spirv.FConvert {{%.*}} : f16 to bf16
+  %0 = spirv.FConvert %arg0 : f16 to bf16
+  spirv.ReturnValue %0 : bf16
+}
+
+// -----
+
 func.func @f_convert_f32_to_bf16_vector(%arg0 : vector<3xf32>) -> vector<3xbf16> {
   // CHECK: {{%.*}} = spirv.FConvert {{%.*}} : vector<3xf32> to vector<3xbf16>
   %0 = spirv.FConvert %arg0 : vector<3xf32> to vector<3xbf16>
diff --git a/mlir/test/Target/SPIRV/cast-ops.mlir b/mlir/test/Target/SPIRV/cast-ops.mlir
index 317264d37b8c8..b058c2cebda2f 100644
--- a/mlir/test/Target/SPIRV/cast-ops.mlir
+++ b/mlir/test/Target/SPIRV/cast-ops.mlir
@@ -29,7 +29,7 @@ spirv.module Logical Vulkan requires #spirv.vce<v1.6, [Shader, Linkage, Cooperat
 
 // -----
 
-spirv.module Logical GLSL450 requires #spirv.vce<v1.0, [Shader, Linkage, BFloat16TypeKHR, Float64, Int64], [SPV_KHR_bfloat16]> {
+spirv.module Logical GLSL450 requires #spirv.vce<v1.0, [Shader, Linkage, BFloat16TypeKHR, Float16, Float64, Int64], [SPV_KHR_bfloat16]> {
   spirv.func @convert_f_to_s(%arg0 : f32) -> i32 "None" {
     // CHECK: {{%.*}} = spirv.ConvertFToS {{%.*}} : f32 to i32
     %0 = spirv.ConvertFToS %arg0 : f32 to i32
@@ -105,6 +105,11 @@ spirv.module Logical GLSL450 requires #spirv.vce<v1.0, [Shader, Linkage, BFloat1
     %0 = spirv.FConvert %arg0 : bf16 to f32
     spirv.ReturnValue %0 : f32
   }
+  spirv.func @convert_f16_to_bf16(%arg0 : f16) -> bf16 "None" {
+    // CHECK: {{%.*}} = spirv.FConvert {{%.*}} : f16 to bf16
+    %0 = spirv.FConvert %arg0 : f16 to bf16
+    spirv.ReturnValue %0 : bf16
+  }
   spirv.func @s_convert(%arg0 : i32) -> i64 "None" {
     // CHECK: {{%.*}} = spirv.SConvert {{%.*}} : i32 to i64
     %0 = spirv.SConvert %arg0 : i32 to i64



More information about the Mlir-commits mailing list