Skip to content

Commit 46c73f5

Browse files
committed
[mlir][spirv] Add definition for ImageWriteOp
1 parent 8388040 commit 46c73f5

File tree

5 files changed

+183
-6
lines changed

5 files changed

+183
-6
lines changed

mlir/include/mlir/Dialect/SPIRV/IR/SPIRVBase.td

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4354,6 +4354,7 @@ def SPIRV_OC_OpCompositeExtract : I32EnumAttrCase<"OpCompositeExtrac
43544354
def SPIRV_OC_OpCompositeInsert : I32EnumAttrCase<"OpCompositeInsert", 82>;
43554355
def SPIRV_OC_OpTranspose : I32EnumAttrCase<"OpTranspose", 84>;
43564356
def SPIRV_OC_OpImageDrefGather : I32EnumAttrCase<"OpImageDrefGather", 97>;
4357+
def SPIRV_OC_OpImageWrite : I32EnumAttrCase<"OpImageWrite", 99>;
43574358
def SPIRV_OC_OpImage : I32EnumAttrCase<"OpImage", 100>;
43584359
def SPIRV_OC_OpImageQuerySize : I32EnumAttrCase<"OpImageQuerySize", 104>;
43594360
def SPIRV_OC_OpConvertFToU : I32EnumAttrCase<"OpConvertFToU", 109>;
@@ -4549,10 +4550,10 @@ def SPIRV_OpcodeAttr :
45494550
SPIRV_OC_OpVectorInsertDynamic, SPIRV_OC_OpVectorShuffle,
45504551
SPIRV_OC_OpCompositeConstruct, SPIRV_OC_OpCompositeExtract,
45514552
SPIRV_OC_OpCompositeInsert, SPIRV_OC_OpTranspose, SPIRV_OC_OpImageDrefGather,
4552-
SPIRV_OC_OpImage, SPIRV_OC_OpImageQuerySize, SPIRV_OC_OpConvertFToU,
4553-
SPIRV_OC_OpConvertFToS, SPIRV_OC_OpConvertSToF, SPIRV_OC_OpConvertUToF,
4554-
SPIRV_OC_OpUConvert, SPIRV_OC_OpSConvert, SPIRV_OC_OpFConvert,
4555-
SPIRV_OC_OpConvertPtrToU, SPIRV_OC_OpConvertUToPtr,
4553+
SPIRV_OC_OpImageWrite, SPIRV_OC_OpImage, SPIRV_OC_OpImageQuerySize,
4554+
SPIRV_OC_OpConvertFToU, SPIRV_OC_OpConvertFToS, SPIRV_OC_OpConvertSToF,
4555+
SPIRV_OC_OpConvertUToF, SPIRV_OC_OpUConvert, SPIRV_OC_OpSConvert,
4556+
SPIRV_OC_OpFConvert, SPIRV_OC_OpConvertPtrToU, SPIRV_OC_OpConvertUToPtr,
45564557
SPIRV_OC_OpPtrCastToGeneric, SPIRV_OC_OpGenericCastToPtr,
45574558
SPIRV_OC_OpGenericCastToPtrExplicit, SPIRV_OC_OpBitcast, SPIRV_OC_OpSNegate,
45584559
SPIRV_OC_OpFNegate, SPIRV_OC_OpIAdd, SPIRV_OC_OpFAdd, SPIRV_OC_OpISub,

mlir/include/mlir/Dialect/SPIRV/IR/SPIRVImageOps.td

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,58 @@ def SPIRV_ImageQuerySizeOp : SPIRV_Op<"ImageQuerySize", [Pure]> {
135135

136136
// -----
137137

138+
def SPIRV_ImageWriteOp : SPIRV_Op<"ImageWrite", []> {
139+
let summary = "Write a texel to an image without a sampler.";
140+
141+
let description = [{
142+
Image must be an object whose type is OpTypeImage with a Sampled operand
143+
of 0 or 2. If the Arrayed operand is 1, then additional capabilities may
144+
be required; e.g., ImageCubeArray, or ImageMSArray. Its Dim operand
145+
must not be SubpassData.
146+
147+
Coordinate must be a scalar or vector of floating-point type or integer
148+
type. It contains non-normalized texel coordinates (u[, v] ... [, array
149+
layer]) as needed by the definition of Image. See the client API
150+
specification for handling of coordinates outside the image.
151+
152+
Texel is the data to write. It must be a scalar or vector with component
153+
type the same as Sampled Type of the OpTypeImage (unless that Sampled
154+
Type is OpTypeVoid).
155+
156+
The Image Format must not be Unknown, unless the
157+
StorageImageWriteWithoutFormat Capability was declared.
158+
159+
Image Operands encodes what operands follow, as per Image Operands.
160+
161+
<!-- End of AutoGen section -->
162+
163+
#### Example:
164+
165+
```mlir
166+
spirv.ImageWrite %0 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Rgba16>, %1 : vector<2xsi32>, %2 : vector<4xf32>
167+
```
168+
}];
169+
170+
let arguments = (ins
171+
SPIRV_AnyImage:$image,
172+
AnyTypeOf<[SPIRV_ScalarOrVectorOf<SPIRV_Float>, SPIRV_ScalarOrVectorOf<SPIRV_Integer>]>:$coordinate,
173+
AnyTypeOf<[SPIRV_ScalarOrVectorOf<SPIRV_Float>, SPIRV_ScalarOrVectorOf<SPIRV_Integer>]>:$texel,
174+
OptionalAttr<SPIRV_ImageOperandsAttr>:$imageoperands,
175+
Variadic<SPIRV_Type>:$operand_arguments
176+
);
177+
178+
let results = (outs);
179+
180+
let assemblyFormat = [{$image `:` type($image) `,`
181+
$coordinate `:` type($coordinate) `,`
182+
$texel `:` type($texel)
183+
custom<ImageOperands>($imageoperands)
184+
( `(` $operand_arguments^ `:` type($operand_arguments) `)`)?
185+
attr-dict}];
186+
}
187+
188+
// -----
189+
138190
def SPIRV_ImageOp : SPIRV_Op<"Image",
139191
[Pure,
140192
TypesMatchWith<"type of 'result' matches image type of 'sampledimage'",

mlir/lib/Dialect/SPIRV/IR/SPIRVOps.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2020,6 +2020,65 @@ LogicalResult spirv::ImageDrefGatherOp::verify() {
20202020
return verifyImageOperands(*this, attr, operandArguments);
20212021
}
20222022

2023+
//===----------------------------------------------------------------------===//
2024+
// spirv.ImageWriteOp
2025+
//===----------------------------------------------------------------------===//
2026+
2027+
LogicalResult spirv::ImageWriteOp::verify() {
2028+
ImageType imageType = llvm::cast<ImageType>(getImage().getType());
2029+
Type sampledType = imageType.getElementType();
2030+
ImageSamplerUseInfo samplerInfo = imageType.getSamplerUseInfo();
2031+
2032+
if (samplerInfo != spirv::ImageSamplerUseInfo::SamplerUnknown &&
2033+
samplerInfo != spirv::ImageSamplerUseInfo::NoSampler) {
2034+
return emitOpError(
2035+
"the sampled operand of the underlying image must be 0 or 2");
2036+
}
2037+
2038+
// TODO: Do we need check for: "If the Arrayed operand is 1, then additional
2039+
// capabilities may be required; e.g., ImageCubeArray, or ImageMSArray.".
2040+
2041+
if (imageType.getDim() == spirv::Dim::SubpassData) {
2042+
return emitOpError(
2043+
"the Dim operand of the underlying image must not be SubpassData");
2044+
}
2045+
2046+
Type texelType = getTexel().getType();
2047+
if (llvm::isa<VectorType>(texelType)) {
2048+
texelType = llvm::cast<VectorType>(texelType).getElementType();
2049+
}
2050+
2051+
if (!llvm::isa<NoneType>(sampledType) && texelType != sampledType) {
2052+
return emitOpError(
2053+
"the texel component type must match the image sampled type");
2054+
}
2055+
2056+
if (imageType.getImageFormat() == spirv::ImageFormat::Unknown) {
2057+
auto module = this->getOperation()->getParentOfType<spirv::ModuleOp>();
2058+
if (module) {
2059+
if (std::optional<spirv::VerCapExtAttr> triple = module.getVceTriple()) {
2060+
auto capabilities = (*triple).getCapabilities();
2061+
auto requiredCapability =
2062+
std::find(capabilities.begin(), capabilities.end(),
2063+
spirv::Capability::StorageImageWriteWithoutFormat);
2064+
// It is allowed for the format to be unknown if
2065+
// StorageImageWriteWithoutFormat capability is present.
2066+
if (requiredCapability == capabilities.end())
2067+
return emitOpError("the image format operand of the underlying image "
2068+
"must not be unknown");
2069+
} else {
2070+
return emitOpError("the image format operand of the underlying image "
2071+
"must not be unknown");
2072+
}
2073+
}
2074+
}
2075+
2076+
spirv::ImageOperandsAttr attr = getImageoperandsAttr();
2077+
auto operandArguments = getOperandArguments();
2078+
2079+
return verifyImageOperands(*this, attr, operandArguments);
2080+
}
2081+
20232082
//===----------------------------------------------------------------------===//
20242083
// spirv.ShiftLeftLogicalOp
20252084
//===----------------------------------------------------------------------===//

mlir/test/Dialect/SPIRV/IR/image-ops.mlir

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,3 +115,45 @@ func.func @image_query_size_error_result2(%arg0 : !spirv.image<f32, Buffer, NoDe
115115
}
116116

117117
// -----
118+
119+
//===----------------------------------------------------------------------===//
120+
// spirv.ImageWrite
121+
//===----------------------------------------------------------------------===//
122+
123+
func.func @image_write(%arg0 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Rgba16>, %arg1 : vector<2xsi32>, %arg2 : vector<4xf32>) -> () {
124+
// CHECK: spirv.ImageWrite %arg0 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Rgba16>, %arg1 : vector<2xsi32>, %arg2 : vector<4xf32>
125+
spirv.ImageWrite %arg0 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Rgba16>, %arg1 : vector<2xsi32>, %arg2 : vector<4xf32>
126+
spirv.Return
127+
}
128+
129+
// -----
130+
131+
func.func @image_write_scalar_texel(%arg0 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Rgba16>, %arg1 : vector<2xsi32>, %arg2 : f32) -> () {
132+
// CHECK: spirv.ImageWrite %arg0 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Rgba16>, %arg1 : vector<2xsi32>, %arg2 : f32
133+
spirv.ImageWrite %arg0 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Rgba16>, %arg1 : vector<2xsi32>, %arg2 : f32
134+
spirv.Return
135+
}
136+
137+
// -----
138+
139+
func.func @image_write_need_sampler(%arg0 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NeedSampler, Rgba16>, %arg1 : vector<2xsi32>, %arg2 : vector<4xf32>) -> () {
140+
// expected-error @+1 {{the sampled operand of the underlying image must be 0 or 2}}
141+
spirv.ImageWrite %arg0 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NeedSampler, Rgba16>, %arg1 : vector<2xsi32>, %arg2 : vector<4xf32>
142+
spirv.Return
143+
}
144+
145+
// -----
146+
147+
func.func @image_write_subpass_data(%arg0 : !spirv.image<f32, SubpassData, NoDepth, NonArrayed, SingleSampled, NoSampler, Rgba16>, %arg1 : vector<2xsi32>, %arg2 : vector<4xf32>) -> () {
148+
// expected-error @+1 {{the Dim operand of the underlying image must not be SubpassData}}
149+
spirv.ImageWrite %arg0 : !spirv.image<f32, SubpassData, NoDepth, NonArrayed, SingleSampled, NoSampler, Rgba16>, %arg1 : vector<2xsi32>, %arg2 : vector<4xf32>
150+
spirv.Return
151+
}
152+
153+
// -----
154+
155+
func.func @image_write_texel_type_mismatch(%arg0 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Rgba16>, %arg1 : vector<2xsi32>, %arg2 : vector<4xi32>) -> () {
156+
// expected-error @+1 {{the texel component type must match the image sampled type}}
157+
spirv.ImageWrite %arg0 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Rgba16>, %arg1 : vector<2xsi32>, %arg2 : vector<4xi32>
158+
spirv.Return
159+
}

mlir/test/Target/SPIRV/image-ops.mlir

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
// RUN: mlir-translate -no-implicit-module -test-spirv-roundtrip %s | FileCheck %s
1+
// RUN: mlir-translate --no-implicit-module --split-input-file --test-spirv-roundtrip %s | FileCheck %s
22

3-
spirv.module Logical GLSL450 requires #spirv.vce<v1.0, [Shader], []> {
3+
spirv.module Logical GLSL450 requires #spirv.vce<v1.0, [Shader, ImageQuery], []> {
44
spirv.func @image(%arg0 : !spirv.sampled_image<!spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NeedSampler, Unknown>>, %arg1 : vector<4xf32>, %arg2 : f32) "None" {
55
// CHECK: {{%.*}} = spirv.Image {{%.*}} : !spirv.sampled_image<!spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NeedSampler, Unknown>>
66
%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], []> {
1313
%0 = spirv.ImageQuerySize %arg0 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Unknown> -> vector<2xi32>
1414
spirv.Return
1515
}
16+
spirv.func @image_write(%arg0 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Rgba8>, %arg1 : vector<2xsi32>, %arg2 : vector<4xf32>) "None" {
17+
// CHECK: spirv.ImageWrite %arg0 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Rgba8>, %arg1 : vector<2xsi32>, %arg2 : vector<4xf32>
18+
spirv.ImageWrite %arg0 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Rgba8>, %arg1 : vector<2xsi32>, %arg2 : vector<4xf32>
19+
spirv.Return
20+
}
21+
spirv.func @main() "None" {
22+
spirv.Return
23+
}
24+
spirv.EntryPoint "GLCompute" @main
25+
}
26+
27+
// -----
28+
29+
spirv.module Logical GLSL450 requires #spirv.vce<v1.0, [Shader, StorageImageWriteWithoutFormat], []> {
30+
spirv.func @image_write(%arg0 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Unknown>, %arg1 : vector<2xsi32>, %arg2 : vector<4xf32>) "None" {
31+
// CHECK: spirv.ImageWrite %arg0 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Unknown>, %arg1 : vector<2xsi32>, %arg2 : vector<4xf32>
32+
spirv.ImageWrite %arg0 : !spirv.image<f32, Dim2D, NoDepth, NonArrayed, SingleSampled, NoSampler, Unknown>, %arg1 : vector<2xsi32>, %arg2 : vector<4xf32>
33+
spirv.Return
34+
}
35+
spirv.func @main() "None" {
36+
spirv.Return
37+
}
38+
spirv.EntryPoint "GLCompute" @main
1639
}

0 commit comments

Comments
 (0)