[Mlir-commits] [mlir] 8609e27 - [mlir][spirv] Add definition for ImageWriteOp (#124124)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Thu Feb 6 06:25:13 PST 2025
Author: Igor Wodiany
Date: 2025-02-06T09:25:08-05:00
New Revision: 8609e27a58b94cc5e9daf87cb2609c9b3760bd03
URL: https://github.com/llvm/llvm-project/commit/8609e27a58b94cc5e9daf87cb2609c9b3760bd03
DIFF: https://github.com/llvm/llvm-project/commit/8609e27a58b94cc5e9daf87cb2609c9b3760bd03.diff
LOG: [mlir][spirv] Add definition for ImageWriteOp (#124124)
This Pull Request adds OpImageWrite as defined in section 3.52.10.
(Image Instructions). The tests in
`mlir/test/Target/SPIRV/image-ops.mlir` are also updated (and extended
with the new op), so they now pass validation with `spirv-val` after
serialization into SPIR-V. The test was missing `ImageQuery` capability
and entry points. For entry points dummy `main` functions were added.
Added:
Modified:
mlir/include/mlir/Dialect/SPIRV/IR/SPIRVBase.td
mlir/include/mlir/Dialect/SPIRV/IR/SPIRVImageOps.td
mlir/lib/Dialect/SPIRV/IR/SPIRVOps.cpp
mlir/test/Dialect/SPIRV/IR/image-ops.mlir
mlir/test/Target/SPIRV/image-ops.mlir
Removed:
################################################################################
diff --git a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVBase.td b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVBase.td
index ff738fc2555734a..e9922b6fedb12bf 100644
--- a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVBase.td
+++ b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVBase.td
@@ -4362,6 +4362,7 @@ def SPIRV_OC_OpCompositeExtract : I32EnumAttrCase<"OpCompositeExtrac
def SPIRV_OC_OpCompositeInsert : I32EnumAttrCase<"OpCompositeInsert", 82>;
def SPIRV_OC_OpTranspose : I32EnumAttrCase<"OpTranspose", 84>;
def SPIRV_OC_OpImageDrefGather : I32EnumAttrCase<"OpImageDrefGather", 97>;
+def SPIRV_OC_OpImageWrite : I32EnumAttrCase<"OpImageWrite", 99>;
def SPIRV_OC_OpImage : I32EnumAttrCase<"OpImage", 100>;
def SPIRV_OC_OpImageQuerySize : I32EnumAttrCase<"OpImageQuerySize", 104>;
def SPIRV_OC_OpConvertFToU : I32EnumAttrCase<"OpConvertFToU", 109>;
@@ -4558,10 +4559,10 @@ def SPIRV_OpcodeAttr :
SPIRV_OC_OpVectorInsertDynamic, SPIRV_OC_OpVectorShuffle,
SPIRV_OC_OpCompositeConstruct, SPIRV_OC_OpCompositeExtract,
SPIRV_OC_OpCompositeInsert, SPIRV_OC_OpTranspose, SPIRV_OC_OpImageDrefGather,
- 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,
+ 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,
SPIRV_OC_OpPtrCastToGeneric, SPIRV_OC_OpGenericCastToPtr,
SPIRV_OC_OpGenericCastToPtrExplicit, SPIRV_OC_OpBitcast, SPIRV_OC_OpSNegate,
SPIRV_OC_OpFNegate, SPIRV_OC_OpIAdd, SPIRV_OC_OpFAdd, SPIRV_OC_OpISub,
diff --git a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVImageOps.td b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVImageOps.td
index 755d26de9d47262..b7d6ec70ce14137 100644
--- a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVImageOps.td
+++ b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVImageOps.td
@@ -135,6 +135,58 @@ def SPIRV_ImageQuerySizeOp : SPIRV_Op<"ImageQuerySize", [Pure]> {
// -----
+def SPIRV_ImageWriteOp : SPIRV_Op<"ImageWrite", []> {
+ let summary = "Write a texel to an image without a sampler.";
+
+ let description = [{
+ 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. Its Dim operand
+ must not be SubpassData.
+
+ 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.
+
+ Texel is the data to write. It must be a scalar or vector with component
+ type the same as Sampled Type of the OpTypeImage (unless that Sampled
+ Type is OpTypeVoid).
+
+ The Image Format must not be Unknown, unless the
+ StorageImageWriteWithoutFormat Capability was declared.
+
+ Image Operands encodes what operands follow, as per Image Operands.
+
+ <!-- End of AutoGen section -->
+
+ #### Example:
+
+ ```mlir
+ spirv.ImageWrite %0 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Rgba16>, %1 : vector<2xsi32>, %2 : vector<4xf32>
+ ```
+ }];
+
+ let arguments = (ins
+ SPIRV_AnyImage:$image,
+ AnyTypeOf<[SPIRV_ScalarOrVectorOf<SPIRV_Float>, SPIRV_ScalarOrVectorOf<SPIRV_Integer>]>:$coordinate,
+ AnyTypeOf<[SPIRV_ScalarOrVectorOf<SPIRV_Float>, SPIRV_ScalarOrVectorOf<SPIRV_Integer>]>:$texel,
+ OptionalAttr<SPIRV_ImageOperandsAttr>:$image_operands,
+ Variadic<SPIRV_Type>:$operand_arguments
+ );
+
+ let results = (outs);
+
+ let assemblyFormat = [{$image `:` type($image) `,`
+ $coordinate `:` type($coordinate) `,`
+ $texel `:` type($texel)
+ custom<ImageOperands>($image_operands)
+ ( `(` $operand_arguments^ `:` type($operand_arguments) `)`)?
+ attr-dict}];
+}
+
+// -----
+
def SPIRV_ImageOp : SPIRV_Op<"Image",
[Pure,
TypesMatchWith<"type of 'result' matches image type of 'sampledimage'",
diff --git a/mlir/lib/Dialect/SPIRV/IR/SPIRVOps.cpp b/mlir/lib/Dialect/SPIRV/IR/SPIRVOps.cpp
index 160c264fc32d902..dc414339ae7b831 100644
--- a/mlir/lib/Dialect/SPIRV/IR/SPIRVOps.cpp
+++ b/mlir/lib/Dialect/SPIRV/IR/SPIRVOps.cpp
@@ -2042,6 +2042,45 @@ LogicalResult spirv::ImageDrefGatherOp::verify() {
return verifyImageOperands(*this, attr, operandArguments);
}
+//===----------------------------------------------------------------------===//
+// spirv.ImageWriteOp
+//===----------------------------------------------------------------------===//
+
+LogicalResult spirv::ImageWriteOp::verify() {
+ ImageType imageType = cast<ImageType>(getImage().getType());
+ Type sampledType = imageType.getElementType();
+ ImageSamplerUseInfo samplerInfo = imageType.getSamplerUseInfo();
+
+ if (!llvm::is_contained({spirv::ImageSamplerUseInfo::SamplerUnknown,
+ spirv::ImageSamplerUseInfo::NoSampler},
+ samplerInfo)) {
+ return emitOpError(
+ "the sampled operand of the underlying image must be 0 or 2");
+ }
+
+ // TODO: Do we need check for: "If the Arrayed operand is 1, then additional
+ // capabilities may be required; e.g., ImageCubeArray, or ImageMSArray."?
+
+ if (imageType.getDim() == spirv::Dim::SubpassData) {
+ return emitOpError(
+ "the Dim operand of the underlying image must not be SubpassData");
+ }
+
+ Type texelType = getElementTypeOrSelf(getTexel());
+ if (!isa<NoneType>(sampledType) && texelType != sampledType) {
+ return emitOpError(
+ "the texel component type must match the image sampled type");
+ }
+
+ // TODO: Ideally it should be somewhere verified that "The Image Format must
+ // not be Unknown, unless the StorageImageWriteWithoutFormat Capability was
+ // declared." This function however may not be the suitable place for such
+ // verification.
+
+ return verifyImageOperands(*this, getImageOperandsAttr(),
+ getOperandArguments());
+}
+
//===----------------------------------------------------------------------===//
// spirv.ShiftLeftLogicalOp
//===----------------------------------------------------------------------===//
diff --git a/mlir/test/Dialect/SPIRV/IR/image-ops.mlir b/mlir/test/Dialect/SPIRV/IR/image-ops.mlir
index ab674ee0809ae67..1161f85563ae617 100644
--- a/mlir/test/Dialect/SPIRV/IR/image-ops.mlir
+++ b/mlir/test/Dialect/SPIRV/IR/image-ops.mlir
@@ -115,3 +115,45 @@ func.func @image_query_size_error_result2(%arg0 : !spirv.image<f32, Buffer, NoDe
}
// -----
+
+//===----------------------------------------------------------------------===//
+// spirv.ImageWrite
+//===----------------------------------------------------------------------===//
+
+func.func @image_write(%arg0 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Rgba16>, %arg1 : vector<2xsi32>, %arg2 : vector<4xf32>) -> () {
+ // CHECK: spirv.ImageWrite %arg0 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Rgba16>, %arg1 : vector<2xsi32>, %arg2 : vector<4xf32>
+ spirv.ImageWrite %arg0 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Rgba16>, %arg1 : vector<2xsi32>, %arg2 : vector<4xf32>
+ spirv.Return
+}
+
+// -----
+
+func.func @image_write_scalar_texel(%arg0 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Rgba16>, %arg1 : vector<2xsi32>, %arg2 : f32) -> () {
+ // CHECK: spirv.ImageWrite %arg0 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Rgba16>, %arg1 : vector<2xsi32>, %arg2 : f32
+ spirv.ImageWrite %arg0 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Rgba16>, %arg1 : vector<2xsi32>, %arg2 : f32
+ spirv.Return
+}
+
+// -----
+
+func.func @image_write_need_sampler(%arg0 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NeedSampler, Rgba16>, %arg1 : vector<2xsi32>, %arg2 : vector<4xf32>) -> () {
+ // expected-error @+1 {{the sampled operand of the underlying image must be 0 or 2}}
+ spirv.ImageWrite %arg0 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NeedSampler, Rgba16>, %arg1 : vector<2xsi32>, %arg2 : vector<4xf32>
+ spirv.Return
+}
+
+// -----
+
+func.func @image_write_subpass_data(%arg0 : !spirv.image<f32, SubpassData, NoDepth, NonArrayed, SingleSampled, NoSampler, Rgba16>, %arg1 : vector<2xsi32>, %arg2 : vector<4xf32>) -> () {
+ // expected-error @+1 {{the Dim operand of the underlying image must not be SubpassData}}
+ spirv.ImageWrite %arg0 : !spirv.image<f32, SubpassData, NoDepth, NonArrayed, SingleSampled, NoSampler, Rgba16>, %arg1 : vector<2xsi32>, %arg2 : vector<4xf32>
+ spirv.Return
+}
+
+// -----
+
+func.func @image_write_texel_type_mismatch(%arg0 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Rgba16>, %arg1 : vector<2xsi32>, %arg2 : vector<4xi32>) -> () {
+ // expected-error @+1 {{the texel component type must match the image sampled type}}
+ spirv.ImageWrite %arg0 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Rgba16>, %arg1 : vector<2xsi32>, %arg2 : vector<4xi32>
+ spirv.Return
+}
diff --git a/mlir/test/Target/SPIRV/image-ops.mlir b/mlir/test/Target/SPIRV/image-ops.mlir
index 92429fc8023d2a3..6b52a84ba82f7e2 100644
--- a/mlir/test/Target/SPIRV/image-ops.mlir
+++ b/mlir/test/Target/SPIRV/image-ops.mlir
@@ -1,6 +1,6 @@
-// RUN: mlir-translate -no-implicit-module -test-spirv-roundtrip %s | FileCheck %s
+// RUN: mlir-translate --no-implicit-module --split-input-file --test-spirv-roundtrip %s | FileCheck %s
-spirv.module Logical GLSL450 requires #spirv.vce<v1.0, [Shader], []> {
+spirv.module Logical GLSL450 requires #spirv.vce<v1.0, [Shader, ImageQuery], []> {
spirv.func @image(%arg0 : !spirv.sampled_image<!spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NeedSampler, Unknown>>, %arg1 : vector<4xf32>, %arg2 : f32) "None" {
// CHECK: {{%.*}} = spirv.Image {{%.*}} : !spirv.sampled_image<!spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NeedSampler, Unknown>>
%0 = spirv.Image %arg0 : !spirv.sampled_image<!spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NeedSampler, Unknown>>
@@ -13,4 +13,27 @@ spirv.module Logical GLSL450 requires #spirv.vce<v1.0, [Shader], []> {
%0 = spirv.ImageQuerySize %arg0 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Unknown> -> vector<2xi32>
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 %arg0 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Rgba8>, %arg1 : vector<2xsi32>, %arg2 : vector<4xf32>
+ spirv.ImageWrite %arg0 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Rgba8>, %arg1 : vector<2xsi32>, %arg2 : vector<4xf32>
+ spirv.Return
+ }
+ spirv.func @main() "None" {
+ spirv.Return
+ }
+ spirv.EntryPoint "GLCompute" @main
+}
+
+// -----
+
+spirv.module Logical GLSL450 requires #spirv.vce<v1.0, [Shader, StorageImageWriteWithoutFormat], []> {
+ spirv.func @image_write(%arg0 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Unknown>, %arg1 : vector<2xsi32>, %arg2 : vector<4xf32>) "None" {
+ // CHECK: spirv.ImageWrite %arg0 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Unknown>, %arg1 : vector<2xsi32>, %arg2 : vector<4xf32>
+ spirv.ImageWrite %arg0 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Unknown>, %arg1 : vector<2xsi32>, %arg2 : vector<4xf32>
+ spirv.Return
+ }
+ spirv.func @main() "None" {
+ spirv.Return
+ }
+ spirv.EntryPoint "GLCompute" @main
}
More information about the Mlir-commits
mailing list