[Mlir-commits] [mlir] [mlir][spirv] Add definition of OpImageRead (PR #144038)
Igor Wodiany
llvmlistbot at llvm.org
Fri Jun 13 02:28:39 PDT 2025
https://github.com/IgWod-IMG created https://github.com/llvm/llvm-project/pull/144038
None
>From d973ab08f4e1ae6a3a804027b341a2441c32302c Mon Sep 17 00:00:00 2001
From: Igor Wodiany <igor.wodiany at imgtec.com>
Date: Thu, 12 Jun 2025 17:53:20 +0100
Subject: [PATCH] [mlir][spirv] Add definition of OpImageRead
---
.../mlir/Dialect/SPIRV/IR/SPIRVBase.td | 4 +-
.../mlir/Dialect/SPIRV/IR/SPIRVImageOps.td | 57 +++++++++++++++++++
mlir/lib/Dialect/SPIRV/IR/ImageOps.cpp | 17 ++++++
mlir/test/Dialect/SPIRV/IR/image-ops.mlir | 28 +++++++++
mlir/test/Target/SPIRV/image-ops.mlir | 11 +++-
5 files changed, 114 insertions(+), 3 deletions(-)
diff --git a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVBase.td b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVBase.td
index b143cf9a5f509..7e18144aadff3 100644
--- a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVBase.td
+++ b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVBase.td
@@ -4339,6 +4339,7 @@ def SPIRV_OC_OpImageSampleImplicitLod : I32EnumAttrCase<"OpImageSampleIm
def SPIRV_OC_OpImageSampleExplicitLod : I32EnumAttrCase<"OpImageSampleExplicitLod", 88>;
def SPIRV_OC_OpImageSampleProjDrefImplicitLod : I32EnumAttrCase<"OpImageSampleProjDrefImplicitLod", 93>;
def SPIRV_OC_OpImageDrefGather : I32EnumAttrCase<"OpImageDrefGather", 97>;
+def SPIRV_OC_OpImageRead : I32EnumAttrCase<"OpImageRead", 98>;
def SPIRV_OC_OpImageWrite : I32EnumAttrCase<"OpImageWrite", 99>;
def SPIRV_OC_OpImage : I32EnumAttrCase<"OpImage", 100>;
def SPIRV_OC_OpImageQuerySize : I32EnumAttrCase<"OpImageQuerySize", 104>;
@@ -4546,7 +4547,8 @@ def SPIRV_OpcodeAttr :
SPIRV_OC_OpCompositeInsert, SPIRV_OC_OpTranspose,
SPIRV_OC_OpImageSampleImplicitLod, SPIRV_OC_OpImageSampleExplicitLod,
SPIRV_OC_OpImageSampleProjDrefImplicitLod, SPIRV_OC_OpImageDrefGather,
- SPIRV_OC_OpImageWrite, SPIRV_OC_OpImage, SPIRV_OC_OpImageQuerySize,
+ SPIRV_OC_OpImageRead, SPIRV_OC_OpImageWrite, SPIRV_OC_OpImage,
+ SPIRV_OC_OpImageQuerySize,
SPIRV_OC_OpConvertFToU, SPIRV_OC_OpConvertFToS, SPIRV_OC_OpConvertSToF,
SPIRV_OC_OpConvertUToF, SPIRV_OC_OpUConvert, SPIRV_OC_OpSConvert,
SPIRV_OC_OpFConvert, SPIRV_OC_OpConvertPtrToU, SPIRV_OC_OpConvertUToPtr,
diff --git a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVImageOps.td b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVImageOps.td
index 9999e5cc07b86..7610966b84be3 100644
--- a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVImageOps.td
+++ b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVImageOps.td
@@ -186,6 +186,63 @@ def SPIRV_ImageQuerySizeOp : SPIRV_Op<"ImageQuerySize", [Pure]> {
// -----
+def SPIRV_ImageReadOp : SPIRV_Op<"ImageRead",
+ [SPIRV_SampledOperandIs<"image", ["SamplerUnknown", "NoSampler"]>,
+ SPIRV_NoneOrElementMatchImage<"result", "image">]> {
+ let summary = "Read a texel from an image without a sampler.";
+
+ let description = [{
+ Result Type must be a scalar or vector of floating-point type or integer
+ type. It must be a scalar or vector with component type the same as Sampled
+ Type of the OpTypeImage (unless that Sampled Type is OpTypeVoid).
+
+ Image must be an object whose type is OpTypeImage with a Sampled operand of
+ 0 or 2. If the Arrayed operand is 1, then additional capabilities may be
+ required; e.g., ImageCubeArray, or ImageMSArray.
+
+ Coordinate must be a scalar or vector of floating-point type or integer
+ type. It contains non-normalized texel coordinates (u[, v] ... [, array
+ layer]) as needed by the definition of Image. See the client API
+ specification for handling of coordinates outside the image.
+
+ If the Image Dim operand is SubpassData, Coordinate is relative to the
+ current fragment location. See the client API specification for more detail
+ on how these coordinates are applied.
+
+ If the Image Dim operand is not SubpassData, the Image Format must not be
+ Unknown, unless the StorageImageReadWithoutFormat Capability was declared.
+
+ Image Operands encodes what operands follow, as per Image Operands.
+
+ <!-- End of AutoGen section -->
+
+ #### Example:
+
+ ```mlir
+ %0 = spirv.ImageRead %1, %2 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, R32f>, vector<2xsi32> -> vector<4xf32>
+ ```
+ }];
+
+ let arguments = (ins
+ SPIRV_AnyImage:$image,
+ AnyTypeOf<[SPIRV_ScalarOrVectorOf<SPIRV_Float>, SPIRV_ScalarOrVectorOf<SPIRV_Integer>]>:$coordinate,
+ OptionalAttr<SPIRV_ImageOperandsAttr>:$image_operands,
+ Variadic<SPIRV_Type>:$operand_arguments
+ );
+
+ let results = (outs
+ AnyTypeOf<[SPIRV_ScalarOrVectorOf<SPIRV_Float>, SPIRV_ScalarOrVectorOf<SPIRV_Integer>]>:$result
+ );
+
+ let assemblyFormat = [{
+ $image `,` $coordinate custom<ImageOperands>($image_operands) ( `,` $operand_arguments^ )? attr-dict
+ `:` type($image) `,` type($coordinate) ( `,` type($operand_arguments)^ )?
+ `->` type($result)
+ }];
+}
+
+// -----
+
def SPIRV_ImageWriteOp : SPIRV_Op<"ImageWrite",
[SPIRV_SampledOperandIs<"image", ["SamplerUnknown", "NoSampler"]>,
SPIRV_DimIsNot<"image", ["SubpassData"]>,
diff --git a/mlir/lib/Dialect/SPIRV/IR/ImageOps.cpp b/mlir/lib/Dialect/SPIRV/IR/ImageOps.cpp
index a021931425fb0..f7af79ceefa82 100644
--- a/mlir/lib/Dialect/SPIRV/IR/ImageOps.cpp
+++ b/mlir/lib/Dialect/SPIRV/IR/ImageOps.cpp
@@ -204,6 +204,23 @@ LogicalResult spirv::ImageDrefGatherOp::verify() {
getOperandArguments());
}
+//===----------------------------------------------------------------------===//
+// spirv.ImageReadOp
+//===----------------------------------------------------------------------===//
+
+LogicalResult spirv::ImageReadOp::verify() {
+ // TODO: Do we need check for: "If the Arrayed operand is 1, then additional
+ // capabilities may be required; e.g., ImageCubeArray, or ImageMSArray."?
+
+ // TODO: Ideally it should be somewhere verified that "If the Image Dim
+ // operand is not SubpassData, the Image Format must not be Unknown, unless
+ // the StorageImageReadWithoutFormat Capability was declared." This function
+ // however may not be the suitable place for such verification.
+
+ return verifyImageOperands(getOperation(), getImageOperandsAttr(),
+ getOperandArguments());
+}
+
//===----------------------------------------------------------------------===//
// spirv.ImageWriteOp
//===----------------------------------------------------------------------===//
diff --git a/mlir/test/Dialect/SPIRV/IR/image-ops.mlir b/mlir/test/Dialect/SPIRV/IR/image-ops.mlir
index 1ebdfdb41de1b..ce9b14fc9ac03 100644
--- a/mlir/test/Dialect/SPIRV/IR/image-ops.mlir
+++ b/mlir/test/Dialect/SPIRV/IR/image-ops.mlir
@@ -116,6 +116,34 @@ func.func @image_query_size_error_result2(%arg0 : !spirv.image<f32, Buffer, NoDe
// -----
+//===----------------------------------------------------------------------===//
+// spirv.ImageRead
+//===----------------------------------------------------------------------===//
+
+func.func @image_read(%arg0: !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Rgba8>, %arg1: vector<2xsi32>) -> () {
+ // CHECK: {{%.*}} = spirv.ImageRead {{%.*}}, {{%.*}}: !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Rgba8>, vector<2xsi32> -> vector<4xf32>
+ %0 = spirv.ImageRead %arg0, %arg1 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Rgba8>, vector<2xsi32> -> vector<4xf32>
+ spirv.Return
+}
+
+// -----
+
+func.func @image_read_type_mismatch(%arg0: !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Rgba8>, %arg1: vector<2xsi32>) -> () {
+ // expected-error @+1 {{op failed to verify that the result component type must match the image sampled type}}
+ %0 = spirv.ImageRead %arg0, %arg1 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Rgba8>, vector<2xsi32> -> vector<4xf16>
+ spirv.Return
+}
+
+// -----
+
+func.func @image_read_need_sampler(%arg0: !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NeedSampler, Rgba8>, %arg1: vector<2xsi32>) -> () {
+ // expected-error @+1 {{op failed to verify that the sampled operand of the underlying image must be SamplerUnknown or NoSampler}}
+ %0 = spirv.ImageRead %arg0, %arg1 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NeedSampler, Rgba8>, vector<2xsi32> -> vector<4xf16>
+ spirv.Return
+}
+
+// -----
+
//===----------------------------------------------------------------------===//
// spirv.ImageWrite
//===----------------------------------------------------------------------===//
diff --git a/mlir/test/Target/SPIRV/image-ops.mlir b/mlir/test/Target/SPIRV/image-ops.mlir
index 6dd23844d46a9..b8d19f0f9a7d1 100644
--- a/mlir/test/Target/SPIRV/image-ops.mlir
+++ b/mlir/test/Target/SPIRV/image-ops.mlir
@@ -13,6 +13,11 @@ spirv.module Logical GLSL450 requires #spirv.vce<v1.0, [Shader, ImageQuery, Link
%0 = spirv.ImageQuerySize %arg0 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Unknown> -> vector<2xi32>
spirv.Return
}
+ spirv.func @image_read(%arg0 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Rgba8>, %arg1 : vector<2xsi32>) "None" {
+ // CHECK: {{.*}} = spirv.ImageRead {{%.*}}, {{%.*}} : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Rgba8>, vector<2xsi32> -> vector<4xf32>
+ %0 = spirv.ImageRead %arg0, %arg1 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Rgba8>, vector<2xsi32> -> vector<4xf32>
+ spirv.Return
+ }
spirv.func @image_write(%arg0 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Rgba8>, %arg1 : vector<2xsi32>, %arg2 : vector<4xf32>) "None" {
// CHECK: spirv.ImageWrite {{%.*}}, {{%.*}}, {{%.*}} : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Rgba8>, vector<2xsi32>, vector<4xf32>
spirv.ImageWrite %arg0, %arg1, %arg2 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Rgba8>, vector<2xsi32>, vector<4xf32>
@@ -38,9 +43,11 @@ spirv.module Logical GLSL450 requires #spirv.vce<v1.0, [Shader, ImageQuery, Link
// -----
spirv.module Logical GLSL450 requires #spirv.vce<v1.0, [Shader, StorageImageWriteWithoutFormat, Linkage], []> {
- spirv.func @image_write(%arg0 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Unknown>, %arg1 : vector<2xsi32>, %arg2 : vector<4xf32>) "None" {
+ spirv.func @image_read_write(%arg0 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Unknown>, %arg1 : vector<2xsi32>) "None" {
+ // CHECK: spirv.ImageRead {{%.*}}, {{%.*}} : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Unknown>, vector<2xsi32> -> vector<4xf32>
+ %0 = spirv.ImageRead %arg0, %arg1 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Unknown>, vector<2xsi32> -> vector<4xf32>
// CHECK: spirv.ImageWrite {{%.*}}, {{%.*}}, {{%.*}} : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Unknown>, vector<2xsi32>, vector<4xf32>
- spirv.ImageWrite %arg0, %arg1, %arg2 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Unknown>, vector<2xsi32>, vector<4xf32>
+ spirv.ImageWrite %arg0, %arg1, %0 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Unknown>, vector<2xsi32>, vector<4xf32>
spirv.Return
}
}
More information about the Mlir-commits
mailing list