diff --git a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVBase.td b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVBase.td index ff738fc255573..e9922b6fedb12 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 755d26de9d472..b7d6ec70ce141 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. + + + + #### Example: + + ```mlir + spirv.ImageWrite %0 : !spirv.image, %1 : vector<2xsi32>, %2 : vector<4xf32> + ``` + }]; + + let arguments = (ins + SPIRV_AnyImage:$image, + AnyTypeOf<[SPIRV_ScalarOrVectorOf, SPIRV_ScalarOrVectorOf]>:$coordinate, + AnyTypeOf<[SPIRV_ScalarOrVectorOf, SPIRV_ScalarOrVectorOf]>:$texel, + OptionalAttr:$image_operands, + Variadic:$operand_arguments + ); + + let results = (outs); + + let assemblyFormat = [{$image `:` type($image) `,` + $coordinate `:` type($coordinate) `,` + $texel `:` type($texel) + custom($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 160c264fc32d9..dc414339ae7b8 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(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(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 ab674ee0809ae..1161f85563ae6 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, %arg1 : vector<2xsi32>, %arg2 : vector<4xf32>) -> () { + // CHECK: spirv.ImageWrite %arg0 : !spirv.image, %arg1 : vector<2xsi32>, %arg2 : vector<4xf32> + spirv.ImageWrite %arg0 : !spirv.image, %arg1 : vector<2xsi32>, %arg2 : vector<4xf32> + spirv.Return +} + +// ----- + +func.func @image_write_scalar_texel(%arg0 : !spirv.image, %arg1 : vector<2xsi32>, %arg2 : f32) -> () { + // CHECK: spirv.ImageWrite %arg0 : !spirv.image, %arg1 : vector<2xsi32>, %arg2 : f32 + spirv.ImageWrite %arg0 : !spirv.image, %arg1 : vector<2xsi32>, %arg2 : f32 + spirv.Return +} + +// ----- + +func.func @image_write_need_sampler(%arg0 : !spirv.image, %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, %arg1 : vector<2xsi32>, %arg2 : vector<4xf32> + spirv.Return +} + +// ----- + +func.func @image_write_subpass_data(%arg0 : !spirv.image, %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, %arg1 : vector<2xsi32>, %arg2 : vector<4xf32> + spirv.Return +} + +// ----- + +func.func @image_write_texel_type_mismatch(%arg0 : !spirv.image, %arg1 : vector<2xsi32>, %arg2 : vector<4xi32>) -> () { + // expected-error @+1 {{the texel component type must match the image sampled type}} + spirv.ImageWrite %arg0 : !spirv.image, %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 92429fc8023d2..6b52a84ba82f7 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 { +spirv.module Logical GLSL450 requires #spirv.vce { spirv.func @image(%arg0 : !spirv.sampled_image>, %arg1 : vector<4xf32>, %arg2 : f32) "None" { // CHECK: {{%.*}} = spirv.Image {{%.*}} : !spirv.sampled_image> %0 = spirv.Image %arg0 : !spirv.sampled_image> @@ -13,4 +13,27 @@ spirv.module Logical GLSL450 requires #spirv.vce { %0 = spirv.ImageQuerySize %arg0 : !spirv.image -> vector<2xi32> spirv.Return } + spirv.func @image_write(%arg0 : !spirv.image, %arg1 : vector<2xsi32>, %arg2 : vector<4xf32>) "None" { + // CHECK: spirv.ImageWrite %arg0 : !spirv.image, %arg1 : vector<2xsi32>, %arg2 : vector<4xf32> + spirv.ImageWrite %arg0 : !spirv.image, %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 { + spirv.func @image_write(%arg0 : !spirv.image, %arg1 : vector<2xsi32>, %arg2 : vector<4xf32>) "None" { + // CHECK: spirv.ImageWrite %arg0 : !spirv.image, %arg1 : vector<2xsi32>, %arg2 : vector<4xf32> + spirv.ImageWrite %arg0 : !spirv.image, %arg1 : vector<2xsi32>, %arg2 : vector<4xf32> + spirv.Return + } + spirv.func @main() "None" { + spirv.Return + } + spirv.EntryPoint "GLCompute" @main }