1 // Copyright (c) 2017 Google Inc.
2 // Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights
3 // reserved.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16
17 // Validates correctness of image instructions.
18
19 #include <string>
20
21 #include "source/opcode.h"
22 #include "source/spirv_constant.h"
23 #include "source/spirv_target_env.h"
24 #include "source/util/bitutils.h"
25 #include "source/val/instruction.h"
26 #include "source/val/validate.h"
27 #include "source/val/validate_scopes.h"
28 #include "source/val/validation_state.h"
29
30 namespace spvtools {
31 namespace val {
32 namespace {
33
34 // Performs compile time check that all spv::ImageOperandsMask::XXX cases are
35 // handled in this module. If spv::ImageOperandsMask::XXX list changes, this
36 // function will fail the build. For all other purposes this is a placeholder
37 // function.
CheckAllImageOperandsHandled()38 bool CheckAllImageOperandsHandled() {
39 spv::ImageOperandsMask enum_val = spv::ImageOperandsMask::Bias;
40
41 // Some improvised code to prevent the compiler from considering enum_val
42 // constant and optimizing the switch away.
43 uint32_t stack_var = 0;
44 if (reinterpret_cast<uintptr_t>(&stack_var) % 256)
45 enum_val = spv::ImageOperandsMask::Lod;
46
47 switch (enum_val) {
48 // Please update the validation rules in this module if you are changing
49 // the list of image operands, and add new enum values to this switch.
50 case spv::ImageOperandsMask::MaskNone:
51 return false;
52 case spv::ImageOperandsMask::Bias:
53 case spv::ImageOperandsMask::Lod:
54 case spv::ImageOperandsMask::Grad:
55 case spv::ImageOperandsMask::ConstOffset:
56 case spv::ImageOperandsMask::Offset:
57 case spv::ImageOperandsMask::ConstOffsets:
58 case spv::ImageOperandsMask::Sample:
59 case spv::ImageOperandsMask::MinLod:
60
61 // TODO(dneto): Support image operands related to the Vulkan memory model.
62 // https://gitlab.khronos.org/spirv/spirv-tools/issues/32
63 case spv::ImageOperandsMask::MakeTexelAvailableKHR:
64 case spv::ImageOperandsMask::MakeTexelVisibleKHR:
65 case spv::ImageOperandsMask::NonPrivateTexelKHR:
66 case spv::ImageOperandsMask::VolatileTexelKHR:
67 case spv::ImageOperandsMask::SignExtend:
68 case spv::ImageOperandsMask::ZeroExtend:
69 // TODO(jaebaek): Move this line properly after handling image offsets
70 // operand. This line temporarily fixes CI failure that
71 // blocks other PRs.
72 // https://github.com/KhronosGroup/SPIRV-Tools/issues/4565
73 case spv::ImageOperandsMask::Offsets:
74 case spv::ImageOperandsMask::Nontemporal:
75 return true;
76 }
77 return false;
78 }
79
80 // Used by GetImageTypeInfo. See OpTypeImage spec for more information.
81 struct ImageTypeInfo {
82 uint32_t sampled_type = 0;
83 spv::Dim dim = spv::Dim::Max;
84 uint32_t depth = 0;
85 uint32_t arrayed = 0;
86 uint32_t multisampled = 0;
87 uint32_t sampled = 0;
88 spv::ImageFormat format = spv::ImageFormat::Max;
89 spv::AccessQualifier access_qualifier = spv::AccessQualifier::Max;
90 };
91
92 // Provides information on image type. |id| should be object of either
93 // OpTypeImage or OpTypeSampledImage type. Returns false in case of failure
94 // (not a valid id, failed to parse the instruction, etc).
GetImageTypeInfo(const ValidationState_t & _,uint32_t id,ImageTypeInfo * info)95 bool GetImageTypeInfo(const ValidationState_t& _, uint32_t id,
96 ImageTypeInfo* info) {
97 if (!id || !info) return false;
98
99 const Instruction* inst = _.FindDef(id);
100 assert(inst);
101
102 if (inst->opcode() == spv::Op::OpTypeSampledImage) {
103 inst = _.FindDef(inst->word(2));
104 assert(inst);
105 }
106
107 if (inst->opcode() != spv::Op::OpTypeImage) return false;
108
109 const size_t num_words = inst->words().size();
110 if (num_words != 9 && num_words != 10) return false;
111
112 info->sampled_type = inst->word(2);
113 info->dim = static_cast<spv::Dim>(inst->word(3));
114 info->depth = inst->word(4);
115 info->arrayed = inst->word(5);
116 info->multisampled = inst->word(6);
117 info->sampled = inst->word(7);
118 info->format = static_cast<spv::ImageFormat>(inst->word(8));
119 info->access_qualifier =
120 num_words < 10 ? spv::AccessQualifier::Max
121 : static_cast<spv::AccessQualifier>(inst->word(9));
122 return true;
123 }
124
IsImplicitLod(spv::Op opcode)125 bool IsImplicitLod(spv::Op opcode) {
126 switch (opcode) {
127 case spv::Op::OpImageSampleImplicitLod:
128 case spv::Op::OpImageSampleDrefImplicitLod:
129 case spv::Op::OpImageSampleProjImplicitLod:
130 case spv::Op::OpImageSampleProjDrefImplicitLod:
131 case spv::Op::OpImageSparseSampleImplicitLod:
132 case spv::Op::OpImageSparseSampleDrefImplicitLod:
133 case spv::Op::OpImageSparseSampleProjImplicitLod:
134 case spv::Op::OpImageSparseSampleProjDrefImplicitLod:
135 return true;
136 default:
137 break;
138 }
139 return false;
140 }
141
IsExplicitLod(spv::Op opcode)142 bool IsExplicitLod(spv::Op opcode) {
143 switch (opcode) {
144 case spv::Op::OpImageSampleExplicitLod:
145 case spv::Op::OpImageSampleDrefExplicitLod:
146 case spv::Op::OpImageSampleProjExplicitLod:
147 case spv::Op::OpImageSampleProjDrefExplicitLod:
148 case spv::Op::OpImageSparseSampleExplicitLod:
149 case spv::Op::OpImageSparseSampleDrefExplicitLod:
150 case spv::Op::OpImageSparseSampleProjExplicitLod:
151 case spv::Op::OpImageSparseSampleProjDrefExplicitLod:
152 return true;
153 default:
154 break;
155 }
156 return false;
157 }
158
IsValidLodOperand(const ValidationState_t & _,spv::Op opcode)159 bool IsValidLodOperand(const ValidationState_t& _, spv::Op opcode) {
160 switch (opcode) {
161 case spv::Op::OpImageRead:
162 case spv::Op::OpImageWrite:
163 case spv::Op::OpImageSparseRead:
164 return _.HasCapability(spv::Capability::ImageReadWriteLodAMD);
165 default:
166 return IsExplicitLod(opcode);
167 }
168 }
169
IsValidGatherLodBiasAMD(const ValidationState_t & _,spv::Op opcode)170 bool IsValidGatherLodBiasAMD(const ValidationState_t& _, spv::Op opcode) {
171 switch (opcode) {
172 case spv::Op::OpImageGather:
173 case spv::Op::OpImageSparseGather:
174 return _.HasCapability(spv::Capability::ImageGatherBiasLodAMD);
175 default:
176 break;
177 }
178 return false;
179 }
180
181 // Returns true if the opcode is a Image instruction which applies
182 // homogenous projection to the coordinates.
IsProj(spv::Op opcode)183 bool IsProj(spv::Op opcode) {
184 switch (opcode) {
185 case spv::Op::OpImageSampleProjImplicitLod:
186 case spv::Op::OpImageSampleProjDrefImplicitLod:
187 case spv::Op::OpImageSparseSampleProjImplicitLod:
188 case spv::Op::OpImageSparseSampleProjDrefImplicitLod:
189 case spv::Op::OpImageSampleProjExplicitLod:
190 case spv::Op::OpImageSampleProjDrefExplicitLod:
191 case spv::Op::OpImageSparseSampleProjExplicitLod:
192 case spv::Op::OpImageSparseSampleProjDrefExplicitLod:
193 return true;
194 default:
195 break;
196 }
197 return false;
198 }
199
200 // Returns the number of components in a coordinate used to access a texel in
201 // a single plane of an image with the given parameters.
GetPlaneCoordSize(const ImageTypeInfo & info)202 uint32_t GetPlaneCoordSize(const ImageTypeInfo& info) {
203 uint32_t plane_size = 0;
204 // If this switch breaks your build, please add new values below.
205 switch (info.dim) {
206 case spv::Dim::Dim1D:
207 case spv::Dim::Buffer:
208 plane_size = 1;
209 break;
210 case spv::Dim::Dim2D:
211 case spv::Dim::Rect:
212 case spv::Dim::SubpassData:
213 case spv::Dim::TileImageDataEXT:
214 plane_size = 2;
215 break;
216 case spv::Dim::Dim3D:
217 case spv::Dim::Cube:
218 // For Cube direction vector is used instead of UV.
219 plane_size = 3;
220 break;
221 case spv::Dim::Max:
222 default:
223 assert(0);
224 break;
225 }
226
227 return plane_size;
228 }
229
230 // Returns minimal number of coordinates based on image dim, arrayed and whether
231 // the instruction uses projection coordinates.
GetMinCoordSize(spv::Op opcode,const ImageTypeInfo & info)232 uint32_t GetMinCoordSize(spv::Op opcode, const ImageTypeInfo& info) {
233 if (info.dim == spv::Dim::Cube &&
234 (opcode == spv::Op::OpImageRead || opcode == spv::Op::OpImageWrite ||
235 opcode == spv::Op::OpImageSparseRead)) {
236 // These opcodes use UV for Cube, not direction vector.
237 return 3;
238 }
239
240 return GetPlaneCoordSize(info) + info.arrayed + (IsProj(opcode) ? 1 : 0);
241 }
242
243 // Checks ImageOperand bitfield and respective operands.
244 // word_index is the index of the first word after the image-operand mask word.
ValidateImageOperands(ValidationState_t & _,const Instruction * inst,const ImageTypeInfo & info,uint32_t word_index)245 spv_result_t ValidateImageOperands(ValidationState_t& _,
246 const Instruction* inst,
247 const ImageTypeInfo& info,
248 uint32_t word_index) {
249 static const bool kAllImageOperandsHandled = CheckAllImageOperandsHandled();
250 (void)kAllImageOperandsHandled;
251
252 const spv::Op opcode = inst->opcode();
253 const size_t num_words = inst->words().size();
254
255 const bool have_explicit_mask = (word_index - 1 < num_words);
256 const uint32_t mask = have_explicit_mask ? inst->word(word_index - 1) : 0u;
257
258 if (have_explicit_mask) {
259 // NonPrivate, Volatile, SignExtend, ZeroExtend take no operand words.
260 const uint32_t mask_bits_having_operands =
261 mask & ~uint32_t(spv::ImageOperandsMask::NonPrivateTexelKHR |
262 spv::ImageOperandsMask::VolatileTexelKHR |
263 spv::ImageOperandsMask::SignExtend |
264 spv::ImageOperandsMask::ZeroExtend |
265 spv::ImageOperandsMask::Nontemporal);
266 size_t expected_num_image_operand_words =
267 spvtools::utils::CountSetBits(mask_bits_having_operands);
268 if (mask & uint32_t(spv::ImageOperandsMask::Grad)) {
269 // Grad uses two words.
270 ++expected_num_image_operand_words;
271 }
272
273 if (expected_num_image_operand_words != num_words - word_index) {
274 return _.diag(SPV_ERROR_INVALID_DATA, inst)
275 << "Number of image operand ids doesn't correspond to the bit "
276 "mask";
277 }
278 } else if (num_words != word_index - 1) {
279 return _.diag(SPV_ERROR_INVALID_DATA, inst)
280 << "Number of image operand ids doesn't correspond to the bit mask";
281 }
282
283 if (info.multisampled &
284 (0 == (mask & uint32_t(spv::ImageOperandsMask::Sample)))) {
285 return _.diag(SPV_ERROR_INVALID_DATA, inst)
286 << "Image Operand Sample is required for operation on "
287 "multi-sampled image";
288 }
289
290 // After this point, only set bits in the image operands mask can cause
291 // the module to be invalid.
292 if (mask == 0) return SPV_SUCCESS;
293
294 if (spvtools::utils::CountSetBits(
295 mask & uint32_t(spv::ImageOperandsMask::Offset |
296 spv::ImageOperandsMask::ConstOffset |
297 spv::ImageOperandsMask::ConstOffsets |
298 spv::ImageOperandsMask::Offsets)) > 1) {
299 return _.diag(SPV_ERROR_INVALID_DATA, inst)
300 << "Image Operands Offset, ConstOffset, ConstOffsets, Offsets "
301 "cannot be used together";
302 }
303
304 const bool is_implicit_lod = IsImplicitLod(opcode);
305 const bool is_explicit_lod = IsExplicitLod(opcode);
306 const bool is_valid_lod_operand = IsValidLodOperand(_, opcode);
307 const bool is_valid_gather_lod_bias_amd = IsValidGatherLodBiasAMD(_, opcode);
308
309 // The checks should be done in the order of definition of OperandImage.
310
311 if (mask & uint32_t(spv::ImageOperandsMask::Bias)) {
312 if (!is_implicit_lod && !is_valid_gather_lod_bias_amd) {
313 return _.diag(SPV_ERROR_INVALID_DATA, inst)
314 << "Image Operand Bias can only be used with ImplicitLod opcodes";
315 }
316
317 const uint32_t type_id = _.GetTypeId(inst->word(word_index++));
318 if (!_.IsFloatScalarType(type_id)) {
319 return _.diag(SPV_ERROR_INVALID_DATA, inst)
320 << "Expected Image Operand Bias to be float scalar";
321 }
322
323 if (info.dim != spv::Dim::Dim1D && info.dim != spv::Dim::Dim2D &&
324 info.dim != spv::Dim::Dim3D && info.dim != spv::Dim::Cube) {
325 return _.diag(SPV_ERROR_INVALID_DATA, inst)
326 << "Image Operand Bias requires 'Dim' parameter to be 1D, 2D, 3D "
327 "or Cube";
328 }
329
330 // Multisampled is already checked.
331 }
332
333 if (mask & uint32_t(spv::ImageOperandsMask::Lod)) {
334 if (!is_valid_lod_operand && opcode != spv::Op::OpImageFetch &&
335 opcode != spv::Op::OpImageSparseFetch &&
336 !is_valid_gather_lod_bias_amd) {
337 return _.diag(SPV_ERROR_INVALID_DATA, inst)
338 << "Image Operand Lod can only be used with ExplicitLod opcodes "
339 << "and OpImageFetch";
340 }
341
342 if (mask & uint32_t(spv::ImageOperandsMask::Grad)) {
343 return _.diag(SPV_ERROR_INVALID_DATA, inst)
344 << "Image Operand bits Lod and Grad cannot be set at the same "
345 "time";
346 }
347
348 const uint32_t type_id = _.GetTypeId(inst->word(word_index++));
349 if (is_explicit_lod || is_valid_gather_lod_bias_amd) {
350 if (!_.IsFloatScalarType(type_id)) {
351 return _.diag(SPV_ERROR_INVALID_DATA, inst)
352 << "Expected Image Operand Lod to be float scalar when used "
353 << "with ExplicitLod";
354 }
355 } else {
356 if (!_.IsIntScalarType(type_id)) {
357 return _.diag(SPV_ERROR_INVALID_DATA, inst)
358 << "Expected Image Operand Lod to be int scalar when used with "
359 << "OpImageFetch";
360 }
361 }
362
363 if (info.dim != spv::Dim::Dim1D && info.dim != spv::Dim::Dim2D &&
364 info.dim != spv::Dim::Dim3D && info.dim != spv::Dim::Cube) {
365 return _.diag(SPV_ERROR_INVALID_DATA, inst)
366 << "Image Operand Lod requires 'Dim' parameter to be 1D, 2D, 3D "
367 "or Cube";
368 }
369
370 // Multisampled is already checked.
371 }
372
373 if (mask & uint32_t(spv::ImageOperandsMask::Grad)) {
374 if (!is_explicit_lod) {
375 return _.diag(SPV_ERROR_INVALID_DATA, inst)
376 << "Image Operand Grad can only be used with ExplicitLod opcodes";
377 }
378
379 const uint32_t dx_type_id = _.GetTypeId(inst->word(word_index++));
380 const uint32_t dy_type_id = _.GetTypeId(inst->word(word_index++));
381 if (!_.IsFloatScalarOrVectorType(dx_type_id) ||
382 !_.IsFloatScalarOrVectorType(dy_type_id)) {
383 return _.diag(SPV_ERROR_INVALID_DATA, inst)
384 << "Expected both Image Operand Grad ids to be float scalars or "
385 << "vectors";
386 }
387
388 const uint32_t plane_size = GetPlaneCoordSize(info);
389 const uint32_t dx_size = _.GetDimension(dx_type_id);
390 const uint32_t dy_size = _.GetDimension(dy_type_id);
391 if (plane_size != dx_size) {
392 return _.diag(SPV_ERROR_INVALID_DATA, inst)
393 << "Expected Image Operand Grad dx to have " << plane_size
394 << " components, but given " << dx_size;
395 }
396
397 if (plane_size != dy_size) {
398 return _.diag(SPV_ERROR_INVALID_DATA, inst)
399 << "Expected Image Operand Grad dy to have " << plane_size
400 << " components, but given " << dy_size;
401 }
402
403 // Multisampled is already checked.
404 }
405
406 if (mask & uint32_t(spv::ImageOperandsMask::ConstOffset)) {
407 if (info.dim == spv::Dim::Cube) {
408 return _.diag(SPV_ERROR_INVALID_DATA, inst)
409 << "Image Operand ConstOffset cannot be used with Cube Image "
410 "'Dim'";
411 }
412
413 const uint32_t id = inst->word(word_index++);
414 const uint32_t type_id = _.GetTypeId(id);
415 if (!_.IsIntScalarOrVectorType(type_id)) {
416 return _.diag(SPV_ERROR_INVALID_DATA, inst)
417 << "Expected Image Operand ConstOffset to be int scalar or "
418 << "vector";
419 }
420
421 if (!spvOpcodeIsConstant(_.GetIdOpcode(id))) {
422 return _.diag(SPV_ERROR_INVALID_DATA, inst)
423 << "Expected Image Operand ConstOffset to be a const object";
424 }
425
426 const uint32_t plane_size = GetPlaneCoordSize(info);
427 const uint32_t offset_size = _.GetDimension(type_id);
428 if (plane_size != offset_size) {
429 return _.diag(SPV_ERROR_INVALID_DATA, inst)
430 << "Expected Image Operand ConstOffset to have " << plane_size
431 << " components, but given " << offset_size;
432 }
433 }
434
435 if (mask & uint32_t(spv::ImageOperandsMask::Offset)) {
436 if (info.dim == spv::Dim::Cube) {
437 return _.diag(SPV_ERROR_INVALID_DATA, inst)
438 << "Image Operand Offset cannot be used with Cube Image 'Dim'";
439 }
440
441 const uint32_t id = inst->word(word_index++);
442 const uint32_t type_id = _.GetTypeId(id);
443 if (!_.IsIntScalarOrVectorType(type_id)) {
444 return _.diag(SPV_ERROR_INVALID_DATA, inst)
445 << "Expected Image Operand Offset to be int scalar or "
446 << "vector";
447 }
448
449 const uint32_t plane_size = GetPlaneCoordSize(info);
450 const uint32_t offset_size = _.GetDimension(type_id);
451 if (plane_size != offset_size) {
452 return _.diag(SPV_ERROR_INVALID_DATA, inst)
453 << "Expected Image Operand Offset to have " << plane_size
454 << " components, but given " << offset_size;
455 }
456
457 if (!_.options()->before_hlsl_legalization &&
458 spvIsVulkanEnv(_.context()->target_env)) {
459 if (opcode != spv::Op::OpImageGather &&
460 opcode != spv::Op::OpImageDrefGather &&
461 opcode != spv::Op::OpImageSparseGather &&
462 opcode != spv::Op::OpImageSparseDrefGather) {
463 return _.diag(SPV_ERROR_INVALID_DATA, inst)
464 << _.VkErrorID(4663)
465 << "Image Operand Offset can only be used with "
466 "OpImage*Gather operations";
467 }
468 }
469 }
470
471 if (mask & uint32_t(spv::ImageOperandsMask::ConstOffsets)) {
472 if (opcode != spv::Op::OpImageGather &&
473 opcode != spv::Op::OpImageDrefGather &&
474 opcode != spv::Op::OpImageSparseGather &&
475 opcode != spv::Op::OpImageSparseDrefGather) {
476 return _.diag(SPV_ERROR_INVALID_DATA, inst)
477 << "Image Operand ConstOffsets can only be used with "
478 "OpImageGather and OpImageDrefGather";
479 }
480
481 if (info.dim == spv::Dim::Cube) {
482 return _.diag(SPV_ERROR_INVALID_DATA, inst)
483 << "Image Operand ConstOffsets cannot be used with Cube Image "
484 "'Dim'";
485 }
486
487 const uint32_t id = inst->word(word_index++);
488 const uint32_t type_id = _.GetTypeId(id);
489 const Instruction* type_inst = _.FindDef(type_id);
490 assert(type_inst);
491
492 if (type_inst->opcode() != spv::Op::OpTypeArray) {
493 return _.diag(SPV_ERROR_INVALID_DATA, inst)
494 << "Expected Image Operand ConstOffsets to be an array of size 4";
495 }
496
497 uint64_t array_size = 0;
498 if (!_.EvalConstantValUint64(type_inst->word(3), &array_size)) {
499 assert(0 && "Array type definition is corrupt");
500 }
501
502 if (array_size != 4) {
503 return _.diag(SPV_ERROR_INVALID_DATA, inst)
504 << "Expected Image Operand ConstOffsets to be an array of size 4";
505 }
506
507 const uint32_t component_type = type_inst->word(2);
508 if (!_.IsIntVectorType(component_type) ||
509 _.GetDimension(component_type) != 2) {
510 return _.diag(SPV_ERROR_INVALID_DATA, inst)
511 << "Expected Image Operand ConstOffsets array components to be "
512 "int vectors of size 2";
513 }
514
515 if (!spvOpcodeIsConstant(_.GetIdOpcode(id))) {
516 return _.diag(SPV_ERROR_INVALID_DATA, inst)
517 << "Expected Image Operand ConstOffsets to be a const object";
518 }
519 }
520
521 if (mask & uint32_t(spv::ImageOperandsMask::Sample)) {
522 if (opcode != spv::Op::OpImageFetch && opcode != spv::Op::OpImageRead &&
523 opcode != spv::Op::OpImageWrite &&
524 opcode != spv::Op::OpImageSparseFetch &&
525 opcode != spv::Op::OpImageSparseRead) {
526 return _.diag(SPV_ERROR_INVALID_DATA, inst)
527 << "Image Operand Sample can only be used with OpImageFetch, "
528 << "OpImageRead, OpImageWrite, OpImageSparseFetch and "
529 << "OpImageSparseRead";
530 }
531
532 if (info.multisampled == 0) {
533 return _.diag(SPV_ERROR_INVALID_DATA, inst)
534 << "Image Operand Sample requires non-zero 'MS' parameter";
535 }
536
537 const uint32_t type_id = _.GetTypeId(inst->word(word_index++));
538 if (!_.IsIntScalarType(type_id)) {
539 return _.diag(SPV_ERROR_INVALID_DATA, inst)
540 << "Expected Image Operand Sample to be int scalar";
541 }
542 }
543
544 if (mask & uint32_t(spv::ImageOperandsMask::MinLod)) {
545 if (!is_implicit_lod && !(mask & uint32_t(spv::ImageOperandsMask::Grad))) {
546 return _.diag(SPV_ERROR_INVALID_DATA, inst)
547 << "Image Operand MinLod can only be used with ImplicitLod "
548 << "opcodes or together with Image Operand Grad";
549 }
550
551 const uint32_t type_id = _.GetTypeId(inst->word(word_index++));
552 if (!_.IsFloatScalarType(type_id)) {
553 return _.diag(SPV_ERROR_INVALID_DATA, inst)
554 << "Expected Image Operand MinLod to be float scalar";
555 }
556
557 if (info.dim != spv::Dim::Dim1D && info.dim != spv::Dim::Dim2D &&
558 info.dim != spv::Dim::Dim3D && info.dim != spv::Dim::Cube) {
559 return _.diag(SPV_ERROR_INVALID_DATA, inst)
560 << "Image Operand MinLod requires 'Dim' parameter to be 1D, 2D, "
561 "3D or Cube";
562 }
563
564 if (info.multisampled != 0) {
565 return _.diag(SPV_ERROR_INVALID_DATA, inst)
566 << "Image Operand MinLod requires 'MS' parameter to be 0";
567 }
568 }
569
570 if (mask & uint32_t(spv::ImageOperandsMask::MakeTexelAvailableKHR)) {
571 // Checked elsewhere: capability and memory model are correct.
572 if (opcode != spv::Op::OpImageWrite) {
573 return _.diag(SPV_ERROR_INVALID_DATA, inst)
574 << "Image Operand MakeTexelAvailableKHR can only be used with Op"
575 << spvOpcodeString(spv::Op::OpImageWrite) << ": Op"
576 << spvOpcodeString(opcode);
577 }
578
579 if (!(mask & uint32_t(spv::ImageOperandsMask::NonPrivateTexelKHR))) {
580 return _.diag(SPV_ERROR_INVALID_DATA, inst)
581 << "Image Operand MakeTexelAvailableKHR requires "
582 "NonPrivateTexelKHR is also specified: Op"
583 << spvOpcodeString(opcode);
584 }
585
586 const auto available_scope = inst->word(word_index++);
587 if (auto error = ValidateMemoryScope(_, inst, available_scope))
588 return error;
589 }
590
591 if (mask & uint32_t(spv::ImageOperandsMask::MakeTexelVisibleKHR)) {
592 // Checked elsewhere: capability and memory model are correct.
593 if (opcode != spv::Op::OpImageRead &&
594 opcode != spv::Op::OpImageSparseRead) {
595 return _.diag(SPV_ERROR_INVALID_DATA, inst)
596 << "Image Operand MakeTexelVisibleKHR can only be used with Op"
597 << spvOpcodeString(spv::Op::OpImageRead) << " or Op"
598 << spvOpcodeString(spv::Op::OpImageSparseRead) << ": Op"
599 << spvOpcodeString(opcode);
600 }
601
602 if (!(mask & uint32_t(spv::ImageOperandsMask::NonPrivateTexelKHR))) {
603 return _.diag(SPV_ERROR_INVALID_DATA, inst)
604 << "Image Operand MakeTexelVisibleKHR requires NonPrivateTexelKHR "
605 "is also specified: Op"
606 << spvOpcodeString(opcode);
607 }
608
609 const auto visible_scope = inst->word(word_index++);
610 if (auto error = ValidateMemoryScope(_, inst, visible_scope)) return error;
611 }
612
613 if (mask & uint32_t(spv::ImageOperandsMask::SignExtend)) {
614 // Checked elsewhere: SPIR-V 1.4 version or later.
615
616 // "The texel value is converted to the target value via sign extension.
617 // Only valid when the texel type is a scalar or vector of integer type."
618 //
619 // We don't have enough information to know what the texel type is.
620 // In OpenCL, knowledge is deferred until runtime: the image SampledType is
621 // void, and the Format is Unknown.
622 // In Vulkan, the texel type is only known in all cases by the pipeline
623 // setup.
624 }
625
626 if (mask & uint32_t(spv::ImageOperandsMask::ZeroExtend)) {
627 // Checked elsewhere: SPIR-V 1.4 version or later.
628
629 // "The texel value is converted to the target value via zero extension.
630 // Only valid when the texel type is a scalar or vector of integer type."
631 //
632 // We don't have enough information to know what the texel type is.
633 // In OpenCL, knowledge is deferred until runtime: the image SampledType is
634 // void, and the Format is Unknown.
635 // In Vulkan, the texel type is only known in all cases by the pipeline
636 // setup.
637 }
638
639 if (mask & uint32_t(spv::ImageOperandsMask::Offsets)) {
640 // TODO: add validation
641 }
642
643 if (mask & uint32_t(spv::ImageOperandsMask::Nontemporal)) {
644 // Checked elsewhere: SPIR-V 1.6 version or later.
645 }
646
647 return SPV_SUCCESS;
648 }
649
650 // Validate OpImage*Proj* instructions
ValidateImageProj(ValidationState_t & _,const Instruction * inst,const ImageTypeInfo & info)651 spv_result_t ValidateImageProj(ValidationState_t& _, const Instruction* inst,
652 const ImageTypeInfo& info) {
653 if (info.dim != spv::Dim::Dim1D && info.dim != spv::Dim::Dim2D &&
654 info.dim != spv::Dim::Dim3D && info.dim != spv::Dim::Rect) {
655 return _.diag(SPV_ERROR_INVALID_DATA, inst)
656 << "Expected Image 'Dim' parameter to be 1D, 2D, 3D or Rect";
657 }
658
659 if (info.multisampled != 0) {
660 return _.diag(SPV_ERROR_INVALID_DATA, inst)
661 << "Expected Image 'MS' parameter to be 0";
662 }
663
664 if (info.arrayed != 0) {
665 return _.diag(SPV_ERROR_INVALID_DATA, inst)
666 << "Expected Image 'arrayed' parameter to be 0";
667 }
668
669 return SPV_SUCCESS;
670 }
671
672 // Validate OpImage*Read and OpImage*Write instructions
ValidateImageReadWrite(ValidationState_t & _,const Instruction * inst,const ImageTypeInfo & info)673 spv_result_t ValidateImageReadWrite(ValidationState_t& _,
674 const Instruction* inst,
675 const ImageTypeInfo& info) {
676 if (info.sampled == 2) {
677 if (info.dim == spv::Dim::Dim1D &&
678 !_.HasCapability(spv::Capability::Image1D)) {
679 return _.diag(SPV_ERROR_INVALID_DATA, inst)
680 << "Capability Image1D is required to access storage image";
681 } else if (info.dim == spv::Dim::Rect &&
682 !_.HasCapability(spv::Capability::ImageRect)) {
683 return _.diag(SPV_ERROR_INVALID_DATA, inst)
684 << "Capability ImageRect is required to access storage image";
685 } else if (info.dim == spv::Dim::Buffer &&
686 !_.HasCapability(spv::Capability::ImageBuffer)) {
687 return _.diag(SPV_ERROR_INVALID_DATA, inst)
688 << "Capability ImageBuffer is required to access storage image";
689 } else if (info.dim == spv::Dim::Cube && info.arrayed == 1 &&
690 !_.HasCapability(spv::Capability::ImageCubeArray)) {
691 return _.diag(SPV_ERROR_INVALID_DATA, inst)
692 << "Capability ImageCubeArray is required to access "
693 << "storage image";
694 }
695
696 if (info.multisampled == 1 && info.arrayed == 1 && info.sampled == 2 &&
697 !_.HasCapability(spv::Capability::ImageMSArray)) {
698 return _.diag(SPV_ERROR_INVALID_DATA, inst)
699 << "Capability ImageMSArray is required to access storage "
700 << "image";
701 }
702 } else if (info.sampled != 0) {
703 return _.diag(SPV_ERROR_INVALID_DATA, inst)
704 << "Expected Image 'Sampled' parameter to be 0 or 2";
705 }
706
707 return SPV_SUCCESS;
708 }
709
710 // Returns true if opcode is *ImageSparse*, false otherwise.
IsSparse(spv::Op opcode)711 bool IsSparse(spv::Op opcode) {
712 switch (opcode) {
713 case spv::Op::OpImageSparseSampleImplicitLod:
714 case spv::Op::OpImageSparseSampleExplicitLod:
715 case spv::Op::OpImageSparseSampleDrefImplicitLod:
716 case spv::Op::OpImageSparseSampleDrefExplicitLod:
717 case spv::Op::OpImageSparseSampleProjImplicitLod:
718 case spv::Op::OpImageSparseSampleProjExplicitLod:
719 case spv::Op::OpImageSparseSampleProjDrefImplicitLod:
720 case spv::Op::OpImageSparseSampleProjDrefExplicitLod:
721 case spv::Op::OpImageSparseFetch:
722 case spv::Op::OpImageSparseGather:
723 case spv::Op::OpImageSparseDrefGather:
724 case spv::Op::OpImageSparseTexelsResident:
725 case spv::Op::OpImageSparseRead: {
726 return true;
727 }
728
729 default: { return false; }
730 }
731
732 return false;
733 }
734
735 // Checks sparse image opcode result type and returns the second struct member.
736 // Returns inst.type_id for non-sparse image opcodes.
737 // Not valid for sparse image opcodes which do not return a struct.
GetActualResultType(ValidationState_t & _,const Instruction * inst,uint32_t * actual_result_type)738 spv_result_t GetActualResultType(ValidationState_t& _, const Instruction* inst,
739 uint32_t* actual_result_type) {
740 const spv::Op opcode = inst->opcode();
741
742 if (IsSparse(opcode)) {
743 const Instruction* const type_inst = _.FindDef(inst->type_id());
744 assert(type_inst);
745
746 if (!type_inst || type_inst->opcode() != spv::Op::OpTypeStruct) {
747 return _.diag(SPV_ERROR_INVALID_DATA, inst)
748 << "Expected Result Type to be OpTypeStruct";
749 }
750
751 if (type_inst->words().size() != 4 ||
752 !_.IsIntScalarType(type_inst->word(2))) {
753 return _.diag(SPV_ERROR_INVALID_DATA, inst)
754 << "Expected Result Type to be a struct containing an int "
755 "scalar and a texel";
756 }
757
758 *actual_result_type = type_inst->word(3);
759 } else {
760 *actual_result_type = inst->type_id();
761 }
762
763 return SPV_SUCCESS;
764 }
765
766 // Returns a string describing actual result type of an opcode.
767 // Not valid for sparse image opcodes which do not return a struct.
GetActualResultTypeStr(spv::Op opcode)768 const char* GetActualResultTypeStr(spv::Op opcode) {
769 if (IsSparse(opcode)) return "Result Type's second member";
770 return "Result Type";
771 }
772
ValidateTypeImage(ValidationState_t & _,const Instruction * inst)773 spv_result_t ValidateTypeImage(ValidationState_t& _, const Instruction* inst) {
774 assert(inst->type_id() == 0);
775
776 ImageTypeInfo info;
777 if (!GetImageTypeInfo(_, inst->word(1), &info)) {
778 return _.diag(SPV_ERROR_INVALID_DATA, inst)
779 << "Corrupt image type definition";
780 }
781
782 if (_.IsIntScalarType(info.sampled_type) &&
783 (64 == _.GetBitWidth(info.sampled_type)) &&
784 !_.HasCapability(spv::Capability::Int64ImageEXT)) {
785 return _.diag(SPV_ERROR_INVALID_DATA, inst)
786 << "Capability Int64ImageEXT is required when using Sampled Type of "
787 "64-bit int";
788 }
789
790 const auto target_env = _.context()->target_env;
791 if (spvIsVulkanEnv(target_env)) {
792 if ((!_.IsFloatScalarType(info.sampled_type) &&
793 !_.IsIntScalarType(info.sampled_type)) ||
794 ((32 != _.GetBitWidth(info.sampled_type)) &&
795 (64 != _.GetBitWidth(info.sampled_type))) ||
796 ((64 == _.GetBitWidth(info.sampled_type)) &&
797 _.IsFloatScalarType(info.sampled_type))) {
798 return _.diag(SPV_ERROR_INVALID_DATA, inst)
799 << _.VkErrorID(4656)
800 << "Expected Sampled Type to be a 32-bit int, 64-bit int or "
801 "32-bit float scalar type for Vulkan environment";
802 }
803 } else if (spvIsOpenCLEnv(target_env)) {
804 if (!_.IsVoidType(info.sampled_type)) {
805 return _.diag(SPV_ERROR_INVALID_DATA, inst)
806 << "Sampled Type must be OpTypeVoid in the OpenCL environment.";
807 }
808 } else {
809 const spv::Op sampled_type_opcode = _.GetIdOpcode(info.sampled_type);
810 if (sampled_type_opcode != spv::Op::OpTypeVoid &&
811 sampled_type_opcode != spv::Op::OpTypeInt &&
812 sampled_type_opcode != spv::Op::OpTypeFloat) {
813 return _.diag(SPV_ERROR_INVALID_DATA, inst)
814 << "Expected Sampled Type to be either void or"
815 << " numerical scalar type";
816 }
817 }
818
819 // Universal checks on image type operands
820 // Dim and Format and Access Qualifier are checked elsewhere.
821
822 if (info.depth > 2) {
823 return _.diag(SPV_ERROR_INVALID_DATA, inst)
824 << "Invalid Depth " << info.depth << " (must be 0, 1 or 2)";
825 }
826
827 if (info.arrayed > 1) {
828 return _.diag(SPV_ERROR_INVALID_DATA, inst)
829 << "Invalid Arrayed " << info.arrayed << " (must be 0 or 1)";
830 }
831
832 if (info.multisampled > 1) {
833 return _.diag(SPV_ERROR_INVALID_DATA, inst)
834 << "Invalid MS " << info.multisampled << " (must be 0 or 1)";
835 }
836
837 if (info.sampled > 2) {
838 return _.diag(SPV_ERROR_INVALID_DATA, inst)
839 << "Invalid Sampled " << info.sampled << " (must be 0, 1 or 2)";
840 }
841
842 if (info.dim == spv::Dim::SubpassData) {
843 if (info.sampled != 2) {
844 return _.diag(SPV_ERROR_INVALID_DATA, inst)
845 << _.VkErrorID(6214) << "Dim SubpassData requires Sampled to be 2";
846 }
847
848 if (info.format != spv::ImageFormat::Unknown) {
849 return _.diag(SPV_ERROR_INVALID_DATA, inst)
850 << "Dim SubpassData requires format Unknown";
851 }
852 } else if (info.dim == spv::Dim::TileImageDataEXT) {
853 if (_.IsVoidType(info.sampled_type)) {
854 return _.diag(SPV_ERROR_INVALID_DATA, inst)
855 << "Dim TileImageDataEXT requires Sampled Type to be not "
856 "OpTypeVoid";
857 }
858 if (info.sampled != 2) {
859 return _.diag(SPV_ERROR_INVALID_DATA, inst)
860 << "Dim TileImageDataEXT requires Sampled to be 2";
861 }
862 if (info.format != spv::ImageFormat::Unknown) {
863 return _.diag(SPV_ERROR_INVALID_DATA, inst)
864 << "Dim TileImageDataEXT requires format Unknown";
865 }
866 if (info.depth != 0) {
867 return _.diag(SPV_ERROR_INVALID_DATA, inst)
868 << "Dim TileImageDataEXT requires Depth to be 0";
869 }
870 if (info.arrayed != 0) {
871 return _.diag(SPV_ERROR_INVALID_DATA, inst)
872 << "Dim TileImageDataEXT requires Arrayed to be 0";
873 }
874 } else {
875 if (info.multisampled && (info.sampled == 2) &&
876 !_.HasCapability(spv::Capability::StorageImageMultisample)) {
877 return _.diag(SPV_ERROR_INVALID_DATA, inst)
878 << "Capability StorageImageMultisample is required when using "
879 "multisampled storage image";
880 }
881 }
882
883 if (spvIsOpenCLEnv(target_env)) {
884 if ((info.arrayed == 1) && (info.dim != spv::Dim::Dim1D) &&
885 (info.dim != spv::Dim::Dim2D)) {
886 return _.diag(SPV_ERROR_INVALID_DATA, inst)
887 << "In the OpenCL environment, Arrayed may only be set to 1 "
888 << "when Dim is either 1D or 2D.";
889 }
890
891 if (info.multisampled != 0) {
892 return _.diag(SPV_ERROR_INVALID_DATA, inst)
893 << "MS must be 0 in the OpenCL environment.";
894 }
895
896 if (info.sampled != 0) {
897 return _.diag(SPV_ERROR_INVALID_DATA, inst)
898 << "Sampled must be 0 in the OpenCL environment.";
899 }
900
901 if (info.access_qualifier == spv::AccessQualifier::Max) {
902 return _.diag(SPV_ERROR_INVALID_DATA, inst)
903 << "In the OpenCL environment, the optional Access Qualifier"
904 << " must be present.";
905 }
906 }
907
908 if (spvIsVulkanEnv(target_env)) {
909 if (info.sampled == 0) {
910 return _.diag(SPV_ERROR_INVALID_DATA, inst)
911 << _.VkErrorID(4657)
912 << "Sampled must be 1 or 2 in the Vulkan environment.";
913 }
914
915 if (info.dim == spv::Dim::SubpassData && info.arrayed != 0) {
916 return _.diag(SPV_ERROR_INVALID_DATA, inst)
917 << _.VkErrorID(6214)
918 << "Dim SubpassData requires Arrayed to be 0 in the Vulkan "
919 "environment";
920 }
921
922 if (info.dim == spv::Dim::Rect) {
923 return _.diag(SPV_ERROR_INVALID_DATA, inst)
924 << _.VkErrorID(9638)
925 << "Dim must not be Rect in the Vulkan environment";
926 }
927 }
928
929 return SPV_SUCCESS;
930 }
931
ValidateTypeSampledImage(ValidationState_t & _,const Instruction * inst)932 spv_result_t ValidateTypeSampledImage(ValidationState_t& _,
933 const Instruction* inst) {
934 const uint32_t image_type = inst->word(2);
935 if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
936 return _.diag(SPV_ERROR_INVALID_DATA, inst)
937 << "Expected Image to be of type OpTypeImage";
938 }
939
940 ImageTypeInfo info;
941 if (!GetImageTypeInfo(_, image_type, &info)) {
942 return _.diag(SPV_ERROR_INVALID_DATA, inst)
943 << "Corrupt image type definition";
944 }
945 // OpenCL requires Sampled=0, checked elsewhere.
946 // Vulkan uses the Sampled=1 case.
947 // If Dim is TileImageDataEXT, Sampled must be 2 and this is validated
948 // elsewhere.
949 if ((info.sampled != 0) && (info.sampled != 1)) {
950 return _.diag(SPV_ERROR_INVALID_DATA, inst)
951 << _.VkErrorID(4657)
952 << "Sampled image type requires an image type with \"Sampled\" "
953 "operand set to 0 or 1";
954 }
955
956 // This covers both OpTypeSampledImage and OpSampledImage.
957 if (_.version() >= SPV_SPIRV_VERSION_WORD(1, 6) &&
958 info.dim == spv::Dim::Buffer) {
959 return _.diag(SPV_ERROR_INVALID_ID, inst)
960 << "In SPIR-V 1.6 or later, sampled image dimension must not be "
961 "Buffer";
962 }
963
964 return SPV_SUCCESS;
965 }
966
IsAllowedSampledImageOperand(spv::Op opcode,ValidationState_t & _)967 bool IsAllowedSampledImageOperand(spv::Op opcode, ValidationState_t& _) {
968 switch (opcode) {
969 case spv::Op::OpSampledImage:
970 case spv::Op::OpImageSampleImplicitLod:
971 case spv::Op::OpImageSampleExplicitLod:
972 case spv::Op::OpImageSampleDrefImplicitLod:
973 case spv::Op::OpImageSampleDrefExplicitLod:
974 case spv::Op::OpImageSampleProjImplicitLod:
975 case spv::Op::OpImageSampleProjExplicitLod:
976 case spv::Op::OpImageSampleProjDrefImplicitLod:
977 case spv::Op::OpImageSampleProjDrefExplicitLod:
978 case spv::Op::OpImageGather:
979 case spv::Op::OpImageDrefGather:
980 case spv::Op::OpImage:
981 case spv::Op::OpImageQueryLod:
982 case spv::Op::OpImageSparseSampleImplicitLod:
983 case spv::Op::OpImageSparseSampleExplicitLod:
984 case spv::Op::OpImageSparseSampleDrefImplicitLod:
985 case spv::Op::OpImageSparseSampleDrefExplicitLod:
986 case spv::Op::OpImageSparseGather:
987 case spv::Op::OpImageSparseDrefGather:
988 case spv::Op::OpCopyObject:
989 case spv::Op::OpImageSampleWeightedQCOM:
990 case spv::Op::OpImageBoxFilterQCOM:
991 case spv::Op::OpImageBlockMatchSSDQCOM:
992 case spv::Op::OpImageBlockMatchSADQCOM:
993 case spv::Op::OpImageBlockMatchWindowSADQCOM:
994 case spv::Op::OpImageBlockMatchWindowSSDQCOM:
995 case spv::Op::OpImageBlockMatchGatherSADQCOM:
996 case spv::Op::OpImageBlockMatchGatherSSDQCOM:
997 return true;
998 case spv::Op::OpStore:
999 if (_.HasCapability(spv::Capability::BindlessTextureNV)) return true;
1000 return false;
1001 default:
1002 return false;
1003 }
1004 }
1005
ValidateSampledImage(ValidationState_t & _,const Instruction * inst)1006 spv_result_t ValidateSampledImage(ValidationState_t& _,
1007 const Instruction* inst) {
1008 if (_.GetIdOpcode(inst->type_id()) != spv::Op::OpTypeSampledImage) {
1009 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1010 << "Expected Result Type to be OpTypeSampledImage.";
1011 }
1012
1013 const uint32_t image_type = _.GetOperandTypeId(inst, 2);
1014 if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
1015 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1016 << "Expected Image to be of type OpTypeImage.";
1017 }
1018
1019 ImageTypeInfo info;
1020 if (!GetImageTypeInfo(_, image_type, &info)) {
1021 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1022 << "Corrupt image type definition";
1023 }
1024
1025 // TODO(atgoo@github.com) Check compatibility of result type and received
1026 // image.
1027
1028 if (spvIsVulkanEnv(_.context()->target_env)) {
1029 if (info.sampled != 1) {
1030 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1031 << _.VkErrorID(6671)
1032 << "Expected Image 'Sampled' parameter to be 1 for Vulkan "
1033 "environment.";
1034 }
1035 } else {
1036 if (info.sampled != 0 && info.sampled != 1) {
1037 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1038 << "Expected Image 'Sampled' parameter to be 0 or 1";
1039 }
1040 }
1041
1042 if (info.dim == spv::Dim::SubpassData) {
1043 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1044 << "Expected Image 'Dim' parameter to be not SubpassData.";
1045 }
1046
1047 if (_.GetIdOpcode(_.GetOperandTypeId(inst, 3)) != spv::Op::OpTypeSampler) {
1048 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1049 << "Expected Sampler to be of type OpTypeSampler";
1050 }
1051
1052 // We need to validate 2 things:
1053 // * All OpSampledImage instructions must be in the same block in which their
1054 // Result <id> are consumed.
1055 // * Result <id> from OpSampledImage instructions must not appear as operands
1056 // to OpPhi instructions or OpSelect instructions, or any instructions other
1057 // than the image lookup and query instructions specified to take an operand
1058 // whose type is OpTypeSampledImage.
1059 std::vector<Instruction*> consumers = _.getSampledImageConsumers(inst->id());
1060 if (!consumers.empty()) {
1061 for (auto consumer_instr : consumers) {
1062 const auto consumer_opcode = consumer_instr->opcode();
1063 if (consumer_instr->block() != inst->block()) {
1064 return _.diag(SPV_ERROR_INVALID_ID, inst)
1065 << "All OpSampledImage instructions must be in the same block "
1066 "in "
1067 "which their Result <id> are consumed. OpSampledImage Result "
1068 "Type <id> "
1069 << _.getIdName(inst->id())
1070 << " has a consumer in a different basic "
1071 "block. The consumer instruction <id> is "
1072 << _.getIdName(consumer_instr->id()) << ".";
1073 }
1074
1075 if (consumer_opcode == spv::Op::OpPhi ||
1076 consumer_opcode == spv::Op::OpSelect) {
1077 return _.diag(SPV_ERROR_INVALID_ID, inst)
1078 << "Result <id> from OpSampledImage instruction must not appear "
1079 "as "
1080 "operands of Op"
1081 << spvOpcodeString(static_cast<spv::Op>(consumer_opcode)) << "."
1082 << " Found result <id> " << _.getIdName(inst->id())
1083 << " as an operand of <id> " << _.getIdName(consumer_instr->id())
1084 << ".";
1085 }
1086
1087 if (!IsAllowedSampledImageOperand(consumer_opcode, _)) {
1088 return _.diag(SPV_ERROR_INVALID_ID, inst)
1089 << "Result <id> from OpSampledImage instruction must not appear "
1090 "as operand for Op"
1091 << spvOpcodeString(static_cast<spv::Op>(consumer_opcode))
1092 << ", since it is not specified as taking an "
1093 << "OpTypeSampledImage."
1094 << " Found result <id> " << _.getIdName(inst->id())
1095 << " as an operand of <id> " << _.getIdName(consumer_instr->id())
1096 << ".";
1097 }
1098 }
1099 }
1100
1101 const Instruction* ld_inst;
1102 {
1103 int t_idx = inst->GetOperandAs<int>(2);
1104 ld_inst = _.FindDef(t_idx);
1105 }
1106
1107 if (ld_inst->opcode() == spv::Op::OpLoad) {
1108 int texture_id = ld_inst->GetOperandAs<int>(2); // variable to load
1109 _.RegisterQCOMImageProcessingTextureConsumer(texture_id, ld_inst, inst);
1110 }
1111
1112 return SPV_SUCCESS;
1113 }
1114
ValidateImageTexelPointer(ValidationState_t & _,const Instruction * inst)1115 spv_result_t ValidateImageTexelPointer(ValidationState_t& _,
1116 const Instruction* inst) {
1117 const auto result_type = _.FindDef(inst->type_id());
1118 if (result_type->opcode() != spv::Op::OpTypePointer) {
1119 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1120 << "Expected Result Type to be OpTypePointer";
1121 }
1122
1123 const auto storage_class = result_type->GetOperandAs<spv::StorageClass>(1);
1124 if (storage_class != spv::StorageClass::Image) {
1125 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1126 << "Expected Result Type to be OpTypePointer whose Storage Class "
1127 "operand is Image";
1128 }
1129
1130 const auto ptr_type = result_type->GetOperandAs<uint32_t>(2);
1131 const auto ptr_opcode = _.GetIdOpcode(ptr_type);
1132 if (ptr_opcode != spv::Op::OpTypeInt && ptr_opcode != spv::Op::OpTypeFloat &&
1133 ptr_opcode != spv::Op::OpTypeVoid &&
1134 !(ptr_opcode == spv::Op::OpTypeVector &&
1135 _.HasCapability(spv::Capability::AtomicFloat16VectorNV) &&
1136 _.IsFloat16Vector2Or4Type(ptr_type))) {
1137 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1138 << "Expected Result Type to be OpTypePointer whose Type operand "
1139 "must be a scalar numerical type or OpTypeVoid";
1140 }
1141
1142 const auto image_ptr = _.FindDef(_.GetOperandTypeId(inst, 2));
1143 if (!image_ptr || image_ptr->opcode() != spv::Op::OpTypePointer) {
1144 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1145 << "Expected Image to be OpTypePointer";
1146 }
1147
1148 const auto image_type = image_ptr->GetOperandAs<uint32_t>(2);
1149 if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
1150 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1151 << "Expected Image to be OpTypePointer with Type OpTypeImage";
1152 }
1153
1154 ImageTypeInfo info;
1155 if (!GetImageTypeInfo(_, image_type, &info)) {
1156 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1157 << "Corrupt image type definition";
1158 }
1159
1160 if (info.sampled_type != ptr_type &&
1161 !(_.HasCapability(spv::Capability::AtomicFloat16VectorNV) &&
1162 _.IsFloat16Vector2Or4Type(ptr_type) &&
1163 _.GetIdOpcode(info.sampled_type) == spv::Op::OpTypeFloat &&
1164 ((_.GetDimension(ptr_type) == 2 &&
1165 info.format == spv::ImageFormat::Rg16f) ||
1166 (_.GetDimension(ptr_type) == 4 &&
1167 info.format == spv::ImageFormat::Rgba16f)))) {
1168 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1169 << "Expected Image 'Sampled Type' to be the same as the Type "
1170 "pointed to by Result Type";
1171 }
1172
1173 if (info.dim == spv::Dim::SubpassData) {
1174 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1175 << "Image Dim SubpassData cannot be used with OpImageTexelPointer";
1176 }
1177
1178 if (info.dim == spv::Dim::TileImageDataEXT) {
1179 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1180 << "Image Dim TileImageDataEXT cannot be used with "
1181 "OpImageTexelPointer";
1182 }
1183
1184 const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
1185 if (!coord_type || !_.IsIntScalarOrVectorType(coord_type)) {
1186 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1187 << "Expected Coordinate to be integer scalar or vector";
1188 }
1189
1190 uint32_t expected_coord_size = 0;
1191 if (info.arrayed == 0) {
1192 expected_coord_size = GetPlaneCoordSize(info);
1193 } else if (info.arrayed == 1) {
1194 switch (info.dim) {
1195 case spv::Dim::Dim1D:
1196 expected_coord_size = 2;
1197 break;
1198 case spv::Dim::Cube:
1199 case spv::Dim::Dim2D:
1200 expected_coord_size = 3;
1201 break;
1202 default:
1203 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1204 << "Expected Image 'Dim' must be one of 1D, 2D, or Cube when "
1205 "Arrayed is 1";
1206 break;
1207 }
1208 }
1209
1210 const uint32_t actual_coord_size = _.GetDimension(coord_type);
1211 if (expected_coord_size != actual_coord_size) {
1212 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1213 << "Expected Coordinate to have " << expected_coord_size
1214 << " components, but given " << actual_coord_size;
1215 }
1216
1217 const uint32_t sample_type = _.GetOperandTypeId(inst, 4);
1218 if (!sample_type || !_.IsIntScalarType(sample_type)) {
1219 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1220 << "Expected Sample to be integer scalar";
1221 }
1222
1223 if (info.multisampled == 0) {
1224 uint64_t ms = 0;
1225 if (!_.EvalConstantValUint64(inst->GetOperandAs<uint32_t>(4), &ms) ||
1226 ms != 0) {
1227 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1228 << "Expected Sample for Image with MS 0 to be a valid <id> for "
1229 "the value 0";
1230 }
1231 }
1232
1233 if (spvIsVulkanEnv(_.context()->target_env)) {
1234 if ((info.format != spv::ImageFormat::R64i) &&
1235 (info.format != spv::ImageFormat::R64ui) &&
1236 (info.format != spv::ImageFormat::R32f) &&
1237 (info.format != spv::ImageFormat::R32i) &&
1238 (info.format != spv::ImageFormat::R32ui) &&
1239 !((info.format == spv::ImageFormat::Rg16f ||
1240 info.format == spv::ImageFormat::Rgba16f) &&
1241 _.HasCapability(spv::Capability::AtomicFloat16VectorNV))) {
1242 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1243 << _.VkErrorID(4658)
1244 << "Expected the Image Format in Image to be R64i, R64ui, R32f, "
1245 "R32i, or R32ui for Vulkan environment";
1246 }
1247 }
1248
1249 return SPV_SUCCESS;
1250 }
1251
ValidateImageLod(ValidationState_t & _,const Instruction * inst)1252 spv_result_t ValidateImageLod(ValidationState_t& _, const Instruction* inst) {
1253 const spv::Op opcode = inst->opcode();
1254 uint32_t actual_result_type = 0;
1255 if (spv_result_t error = GetActualResultType(_, inst, &actual_result_type)) {
1256 return error;
1257 }
1258
1259 if (!_.IsIntVectorType(actual_result_type) &&
1260 !_.IsFloatVectorType(actual_result_type)) {
1261 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1262 << "Expected " << GetActualResultTypeStr(opcode)
1263 << " to be int or float vector type";
1264 }
1265
1266 if (_.GetDimension(actual_result_type) != 4) {
1267 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1268 << "Expected " << GetActualResultTypeStr(opcode)
1269 << " to have 4 components";
1270 }
1271
1272 const uint32_t image_type = _.GetOperandTypeId(inst, 2);
1273 if (_.GetIdOpcode(image_type) != spv::Op::OpTypeSampledImage) {
1274 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1275 << "Expected Sampled Image to be of type OpTypeSampledImage";
1276 }
1277
1278 ImageTypeInfo info;
1279 if (!GetImageTypeInfo(_, image_type, &info)) {
1280 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1281 << "Corrupt image type definition";
1282 }
1283
1284 if (IsProj(opcode)) {
1285 if (spv_result_t result = ValidateImageProj(_, inst, info)) return result;
1286 }
1287
1288 if (info.multisampled) {
1289 // When using image operands, the Sample image operand is required if and
1290 // only if the image is multisampled (MS=1). The Sample image operand is
1291 // only allowed for fetch, read, and write.
1292 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1293 << "Sampling operation is invalid for multisample image";
1294 }
1295
1296 if (_.GetIdOpcode(info.sampled_type) != spv::Op::OpTypeVoid) {
1297 const uint32_t texel_component_type =
1298 _.GetComponentType(actual_result_type);
1299 if (texel_component_type != info.sampled_type) {
1300 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1301 << "Expected Image 'Sampled Type' to be the same as "
1302 << GetActualResultTypeStr(opcode) << " components";
1303 }
1304 }
1305
1306 const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
1307 if ((opcode == spv::Op::OpImageSampleExplicitLod ||
1308 opcode == spv::Op::OpImageSparseSampleExplicitLod) &&
1309 _.HasCapability(spv::Capability::Kernel)) {
1310 if (!_.IsFloatScalarOrVectorType(coord_type) &&
1311 !_.IsIntScalarOrVectorType(coord_type)) {
1312 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1313 << "Expected Coordinate to be int or float scalar or vector";
1314 }
1315 } else {
1316 if (!_.IsFloatScalarOrVectorType(coord_type)) {
1317 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1318 << "Expected Coordinate to be float scalar or vector";
1319 }
1320 }
1321
1322 const uint32_t min_coord_size = GetMinCoordSize(opcode, info);
1323 const uint32_t actual_coord_size = _.GetDimension(coord_type);
1324 if (min_coord_size > actual_coord_size) {
1325 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1326 << "Expected Coordinate to have at least " << min_coord_size
1327 << " components, but given only " << actual_coord_size;
1328 }
1329
1330 const uint32_t mask = inst->words().size() <= 5 ? 0 : inst->word(5);
1331
1332 if (mask & uint32_t(spv::ImageOperandsMask::ConstOffset)) {
1333 if (spvIsOpenCLEnv(_.context()->target_env)) {
1334 if (opcode == spv::Op::OpImageSampleExplicitLod) {
1335 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1336 << "ConstOffset image operand not allowed "
1337 << "in the OpenCL environment.";
1338 }
1339 }
1340 }
1341
1342 if (spv_result_t result =
1343 ValidateImageOperands(_, inst, info, /* word_index = */ 6))
1344 return result;
1345
1346 return SPV_SUCCESS;
1347 }
1348
1349 // Validates anything OpImage*Dref* instruction
ValidateImageDref(ValidationState_t & _,const Instruction * inst,const ImageTypeInfo & info)1350 spv_result_t ValidateImageDref(ValidationState_t& _, const Instruction* inst,
1351 const ImageTypeInfo& info) {
1352 const uint32_t dref_type = _.GetOperandTypeId(inst, 4);
1353 if (!_.IsFloatScalarType(dref_type) || _.GetBitWidth(dref_type) != 32) {
1354 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1355 << "Expected Dref to be of 32-bit float type";
1356 }
1357
1358 if (spvIsVulkanEnv(_.context()->target_env)) {
1359 if (info.dim == spv::Dim::Dim3D) {
1360 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1361 << _.VkErrorID(4777)
1362 << "In Vulkan, OpImage*Dref* instructions must not use images "
1363 "with a 3D Dim";
1364 }
1365 }
1366
1367 return SPV_SUCCESS;
1368 }
1369
ValidateImageDrefLod(ValidationState_t & _,const Instruction * inst)1370 spv_result_t ValidateImageDrefLod(ValidationState_t& _,
1371 const Instruction* inst) {
1372 const spv::Op opcode = inst->opcode();
1373 uint32_t actual_result_type = 0;
1374 if (spv_result_t error = GetActualResultType(_, inst, &actual_result_type)) {
1375 return error;
1376 }
1377
1378 if (!_.IsIntScalarType(actual_result_type) &&
1379 !_.IsFloatScalarType(actual_result_type)) {
1380 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1381 << "Expected " << GetActualResultTypeStr(opcode)
1382 << " to be int or float scalar type";
1383 }
1384
1385 const uint32_t image_type = _.GetOperandTypeId(inst, 2);
1386 if (_.GetIdOpcode(image_type) != spv::Op::OpTypeSampledImage) {
1387 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1388 << "Expected Sampled Image to be of type OpTypeSampledImage";
1389 }
1390
1391 ImageTypeInfo info;
1392 if (!GetImageTypeInfo(_, image_type, &info)) {
1393 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1394 << "Corrupt image type definition";
1395 }
1396
1397 if (IsProj(opcode)) {
1398 if (spv_result_t result = ValidateImageProj(_, inst, info)) return result;
1399 }
1400
1401 if (info.multisampled) {
1402 // When using image operands, the Sample image operand is required if and
1403 // only if the image is multisampled (MS=1). The Sample image operand is
1404 // only allowed for fetch, read, and write.
1405 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1406 << "Dref sampling operation is invalid for multisample image";
1407 }
1408
1409 if (actual_result_type != info.sampled_type) {
1410 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1411 << "Expected Image 'Sampled Type' to be the same as "
1412 << GetActualResultTypeStr(opcode);
1413 }
1414
1415 const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
1416 if (!_.IsFloatScalarOrVectorType(coord_type)) {
1417 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1418 << "Expected Coordinate to be float scalar or vector";
1419 }
1420
1421 const uint32_t min_coord_size = GetMinCoordSize(opcode, info);
1422 const uint32_t actual_coord_size = _.GetDimension(coord_type);
1423 if (min_coord_size > actual_coord_size) {
1424 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1425 << "Expected Coordinate to have at least " << min_coord_size
1426 << " components, but given only " << actual_coord_size;
1427 }
1428
1429 if (spv_result_t result = ValidateImageDref(_, inst, info)) return result;
1430
1431 if (spv_result_t result =
1432 ValidateImageOperands(_, inst, info, /* word_index = */ 7))
1433 return result;
1434
1435 return SPV_SUCCESS;
1436 }
1437
ValidateImageFetch(ValidationState_t & _,const Instruction * inst)1438 spv_result_t ValidateImageFetch(ValidationState_t& _, const Instruction* inst) {
1439 uint32_t actual_result_type = 0;
1440 if (spv_result_t error = GetActualResultType(_, inst, &actual_result_type)) {
1441 return error;
1442 }
1443
1444 const spv::Op opcode = inst->opcode();
1445 if (!_.IsIntVectorType(actual_result_type) &&
1446 !_.IsFloatVectorType(actual_result_type)) {
1447 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1448 << "Expected " << GetActualResultTypeStr(opcode)
1449 << " to be int or float vector type";
1450 }
1451
1452 if (_.GetDimension(actual_result_type) != 4) {
1453 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1454 << "Expected " << GetActualResultTypeStr(opcode)
1455 << " to have 4 components";
1456 }
1457
1458 const uint32_t image_type = _.GetOperandTypeId(inst, 2);
1459 if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
1460 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1461 << "Expected Image to be of type OpTypeImage";
1462 }
1463
1464 ImageTypeInfo info;
1465 if (!GetImageTypeInfo(_, image_type, &info)) {
1466 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1467 << "Corrupt image type definition";
1468 }
1469
1470 if (_.GetIdOpcode(info.sampled_type) != spv::Op::OpTypeVoid) {
1471 const uint32_t result_component_type =
1472 _.GetComponentType(actual_result_type);
1473 if (result_component_type != info.sampled_type) {
1474 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1475 << "Expected Image 'Sampled Type' to be the same as "
1476 << GetActualResultTypeStr(opcode) << " components";
1477 }
1478 }
1479
1480 if (info.dim == spv::Dim::Cube) {
1481 return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Image 'Dim' cannot be Cube";
1482 }
1483
1484 if (info.sampled != 1) {
1485 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1486 << "Expected Image 'Sampled' parameter to be 1";
1487 }
1488
1489 const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
1490 if (!_.IsIntScalarOrVectorType(coord_type)) {
1491 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1492 << "Expected Coordinate to be int scalar or vector";
1493 }
1494
1495 const uint32_t min_coord_size = GetMinCoordSize(opcode, info);
1496 const uint32_t actual_coord_size = _.GetDimension(coord_type);
1497 if (min_coord_size > actual_coord_size) {
1498 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1499 << "Expected Coordinate to have at least " << min_coord_size
1500 << " components, but given only " << actual_coord_size;
1501 }
1502
1503 if (spv_result_t result =
1504 ValidateImageOperands(_, inst, info, /* word_index = */ 6))
1505 return result;
1506
1507 return SPV_SUCCESS;
1508 }
1509
ValidateImageGather(ValidationState_t & _,const Instruction * inst)1510 spv_result_t ValidateImageGather(ValidationState_t& _,
1511 const Instruction* inst) {
1512 uint32_t actual_result_type = 0;
1513 if (spv_result_t error = GetActualResultType(_, inst, &actual_result_type))
1514 return error;
1515
1516 const spv::Op opcode = inst->opcode();
1517 if (!_.IsIntVectorType(actual_result_type) &&
1518 !_.IsFloatVectorType(actual_result_type)) {
1519 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1520 << "Expected " << GetActualResultTypeStr(opcode)
1521 << " to be int or float vector type";
1522 }
1523
1524 if (_.GetDimension(actual_result_type) != 4) {
1525 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1526 << "Expected " << GetActualResultTypeStr(opcode)
1527 << " to have 4 components";
1528 }
1529
1530 const uint32_t image_type = _.GetOperandTypeId(inst, 2);
1531 if (_.GetIdOpcode(image_type) != spv::Op::OpTypeSampledImage) {
1532 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1533 << "Expected Sampled Image to be of type OpTypeSampledImage";
1534 }
1535
1536 ImageTypeInfo info;
1537 if (!GetImageTypeInfo(_, image_type, &info)) {
1538 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1539 << "Corrupt image type definition";
1540 }
1541
1542 if (info.multisampled) {
1543 // When using image operands, the Sample image operand is required if and
1544 // only if the image is multisampled (MS=1). The Sample image operand is
1545 // only allowed for fetch, read, and write.
1546 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1547 << "Gather operation is invalid for multisample image";
1548 }
1549
1550 if (opcode == spv::Op::OpImageDrefGather ||
1551 opcode == spv::Op::OpImageSparseDrefGather ||
1552 _.GetIdOpcode(info.sampled_type) != spv::Op::OpTypeVoid) {
1553 const uint32_t result_component_type =
1554 _.GetComponentType(actual_result_type);
1555 if (result_component_type != info.sampled_type) {
1556 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1557 << "Expected Image 'Sampled Type' to be the same as "
1558 << GetActualResultTypeStr(opcode) << " components";
1559 }
1560 }
1561
1562 if (info.dim != spv::Dim::Dim2D && info.dim != spv::Dim::Cube &&
1563 info.dim != spv::Dim::Rect) {
1564 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1565 << _.VkErrorID(4777)
1566 << "Expected Image 'Dim' to be 2D, Cube, or Rect";
1567 }
1568
1569 const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
1570 if (!_.IsFloatScalarOrVectorType(coord_type)) {
1571 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1572 << "Expected Coordinate to be float scalar or vector";
1573 }
1574
1575 const uint32_t min_coord_size = GetMinCoordSize(opcode, info);
1576 const uint32_t actual_coord_size = _.GetDimension(coord_type);
1577 if (min_coord_size > actual_coord_size) {
1578 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1579 << "Expected Coordinate to have at least " << min_coord_size
1580 << " components, but given only " << actual_coord_size;
1581 }
1582
1583 if (opcode == spv::Op::OpImageGather ||
1584 opcode == spv::Op::OpImageSparseGather) {
1585 const uint32_t component = inst->GetOperandAs<uint32_t>(4);
1586 const uint32_t component_index_type = _.GetTypeId(component);
1587 if (!_.IsIntScalarType(component_index_type) ||
1588 _.GetBitWidth(component_index_type) != 32) {
1589 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1590 << "Expected Component to be 32-bit int scalar";
1591 }
1592 if (spvIsVulkanEnv(_.context()->target_env)) {
1593 if (!spvOpcodeIsConstant(_.GetIdOpcode(component))) {
1594 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1595 << _.VkErrorID(4664)
1596 << "Expected Component Operand to be a const object for Vulkan "
1597 "environment";
1598 }
1599 }
1600 } else {
1601 assert(opcode == spv::Op::OpImageDrefGather ||
1602 opcode == spv::Op::OpImageSparseDrefGather);
1603 if (spv_result_t result = ValidateImageDref(_, inst, info)) return result;
1604 }
1605
1606 if (spv_result_t result =
1607 ValidateImageOperands(_, inst, info, /* word_index = */ 7))
1608 return result;
1609
1610 return SPV_SUCCESS;
1611 }
1612
ValidateImageRead(ValidationState_t & _,const Instruction * inst)1613 spv_result_t ValidateImageRead(ValidationState_t& _, const Instruction* inst) {
1614 const spv::Op opcode = inst->opcode();
1615 uint32_t actual_result_type = 0;
1616 if (spv_result_t error = GetActualResultType(_, inst, &actual_result_type)) {
1617 return error;
1618 }
1619
1620 if (!_.IsIntScalarOrVectorType(actual_result_type) &&
1621 !_.IsFloatScalarOrVectorType(actual_result_type)) {
1622 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1623 << "Expected " << GetActualResultTypeStr(opcode)
1624 << " to be int or float scalar or vector type";
1625 }
1626
1627 const auto target_env = _.context()->target_env;
1628 // Vulkan requires the result to be a 4-element int or float
1629 // vector.
1630 if (spvIsVulkanEnv(target_env)) {
1631 if (_.GetDimension(actual_result_type) != 4) {
1632 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1633 << _.VkErrorID(4780) << "Expected "
1634 << GetActualResultTypeStr(opcode) << " to have 4 components";
1635 }
1636 } // Check OpenCL below, after we get the image info.
1637
1638 const uint32_t image_type = _.GetOperandTypeId(inst, 2);
1639 if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
1640 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1641 << "Expected Image to be of type OpTypeImage";
1642 }
1643
1644 ImageTypeInfo info;
1645 if (!GetImageTypeInfo(_, image_type, &info)) {
1646 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1647 << "Corrupt image type definition";
1648 }
1649
1650 if (spvIsOpenCLEnv(target_env)) {
1651 // In OpenCL, a read from a depth image returns a scalar float. In other
1652 // cases, the result is always a 4-element vector.
1653 // https://www.khronos.org/registry/OpenCL/specs/3.0-unified/html/OpenCL_Env.html#_data_format_for_reading_and_writing_images
1654 // https://www.khronos.org/registry/OpenCL/specs/3.0-unified/html/OpenCL_C.html#image-read-and-write-functions
1655 // The builtins for reading depth images are:
1656 // float read_imagef(aQual image2d_depth_t image, int2 coord)
1657 // float read_imagef(aQual image2d_array_depth_t image, int4 coord)
1658 if (info.depth) {
1659 if (!_.IsFloatScalarType(actual_result_type)) {
1660 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1661 << "Expected " << GetActualResultTypeStr(opcode)
1662 << " from a depth image read to result in a scalar float value";
1663 }
1664 } else {
1665 if (_.GetDimension(actual_result_type) != 4) {
1666 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1667 << "Expected " << GetActualResultTypeStr(opcode)
1668 << " to have 4 components";
1669 }
1670 }
1671
1672 const uint32_t mask = inst->words().size() <= 5 ? 0 : inst->word(5);
1673 if (mask & uint32_t(spv::ImageOperandsMask::ConstOffset)) {
1674 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1675 << "ConstOffset image operand not allowed "
1676 << "in the OpenCL environment.";
1677 }
1678 }
1679
1680 if (info.dim == spv::Dim::SubpassData) {
1681 if (opcode == spv::Op::OpImageSparseRead) {
1682 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1683 << "Image Dim SubpassData cannot be used with ImageSparseRead";
1684 }
1685
1686 _.function(inst->function()->id())
1687 ->RegisterExecutionModelLimitation(
1688 spv::ExecutionModel::Fragment,
1689 std::string("Dim SubpassData requires Fragment execution model: ") +
1690 spvOpcodeString(opcode));
1691 }
1692
1693 if (info.dim == spv::Dim::TileImageDataEXT) {
1694 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1695 << "Image Dim TileImageDataEXT cannot be used with "
1696 << spvOpcodeString(opcode);
1697 }
1698
1699 if (_.GetIdOpcode(info.sampled_type) != spv::Op::OpTypeVoid) {
1700 const uint32_t result_component_type =
1701 _.GetComponentType(actual_result_type);
1702 if (result_component_type != info.sampled_type) {
1703 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1704 << "Expected Image 'Sampled Type' to be the same as "
1705 << GetActualResultTypeStr(opcode) << " components";
1706 }
1707 }
1708
1709 if (spv_result_t result = ValidateImageReadWrite(_, inst, info))
1710 return result;
1711
1712 const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
1713 if (!_.IsIntScalarOrVectorType(coord_type)) {
1714 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1715 << "Expected Coordinate to be int scalar or vector";
1716 }
1717
1718 const uint32_t min_coord_size = GetMinCoordSize(opcode, info);
1719 const uint32_t actual_coord_size = _.GetDimension(coord_type);
1720 if (min_coord_size > actual_coord_size) {
1721 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1722 << "Expected Coordinate to have at least " << min_coord_size
1723 << " components, but given only " << actual_coord_size;
1724 }
1725
1726 if (spvIsVulkanEnv(_.context()->target_env)) {
1727 if (info.format == spv::ImageFormat::Unknown &&
1728 info.dim != spv::Dim::SubpassData &&
1729 !_.HasCapability(spv::Capability::StorageImageReadWithoutFormat)) {
1730 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1731 << "Capability StorageImageReadWithoutFormat is required to "
1732 << "read storage image";
1733 }
1734 }
1735
1736 if (spv_result_t result =
1737 ValidateImageOperands(_, inst, info, /* word_index = */ 6))
1738 return result;
1739
1740 return SPV_SUCCESS;
1741 }
1742
ValidateImageWrite(ValidationState_t & _,const Instruction * inst)1743 spv_result_t ValidateImageWrite(ValidationState_t& _, const Instruction* inst) {
1744 const uint32_t image_type = _.GetOperandTypeId(inst, 0);
1745 if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
1746 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1747 << "Expected Image to be of type OpTypeImage";
1748 }
1749
1750 ImageTypeInfo info;
1751 if (!GetImageTypeInfo(_, image_type, &info)) {
1752 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1753 << "Corrupt image type definition";
1754 }
1755
1756 if (info.dim == spv::Dim::SubpassData) {
1757 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1758 << "Image 'Dim' cannot be SubpassData";
1759 }
1760
1761 if (info.dim == spv::Dim::TileImageDataEXT) {
1762 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1763 << "Image 'Dim' cannot be TileImageDataEXT";
1764 }
1765
1766 if (spv_result_t result = ValidateImageReadWrite(_, inst, info))
1767 return result;
1768
1769 const uint32_t coord_type = _.GetOperandTypeId(inst, 1);
1770 if (!_.IsIntScalarOrVectorType(coord_type)) {
1771 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1772 << "Expected Coordinate to be int scalar or vector";
1773 }
1774
1775 const uint32_t min_coord_size = GetMinCoordSize(inst->opcode(), info);
1776 const uint32_t actual_coord_size = _.GetDimension(coord_type);
1777 if (min_coord_size > actual_coord_size) {
1778 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1779 << "Expected Coordinate to have at least " << min_coord_size
1780 << " components, but given only " << actual_coord_size;
1781 }
1782
1783 // because it needs to match with 'Sampled Type' the Texel can't be a boolean
1784 const uint32_t texel_type = _.GetOperandTypeId(inst, 2);
1785 if (!_.IsIntScalarOrVectorType(texel_type) &&
1786 !_.IsFloatScalarOrVectorType(texel_type)) {
1787 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1788 << "Expected Texel to be int or float vector or scalar";
1789 }
1790
1791 if (_.GetIdOpcode(info.sampled_type) != spv::Op::OpTypeVoid) {
1792 const uint32_t texel_component_type = _.GetComponentType(texel_type);
1793 if (texel_component_type != info.sampled_type) {
1794 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1795 << "Expected Image 'Sampled Type' to be the same as Texel "
1796 << "components";
1797 }
1798 }
1799
1800 if (spvIsVulkanEnv(_.context()->target_env)) {
1801 if (info.format == spv::ImageFormat::Unknown &&
1802 info.dim != spv::Dim::SubpassData &&
1803 !_.HasCapability(spv::Capability::StorageImageWriteWithoutFormat)) {
1804 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1805 << "Capability StorageImageWriteWithoutFormat is required to "
1806 "write "
1807 << "to storage image";
1808 }
1809 }
1810
1811 if (inst->words().size() > 4) {
1812 if (spvIsOpenCLEnv(_.context()->target_env)) {
1813 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1814 << "Optional Image Operands are not allowed in the OpenCL "
1815 << "environment.";
1816 }
1817 }
1818
1819 if (spv_result_t result =
1820 ValidateImageOperands(_, inst, info, /* word_index = */ 5))
1821 return result;
1822
1823 return SPV_SUCCESS;
1824 }
1825
ValidateImage(ValidationState_t & _,const Instruction * inst)1826 spv_result_t ValidateImage(ValidationState_t& _, const Instruction* inst) {
1827 const uint32_t result_type = inst->type_id();
1828 if (_.GetIdOpcode(result_type) != spv::Op::OpTypeImage) {
1829 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1830 << "Expected Result Type to be OpTypeImage";
1831 }
1832
1833 const uint32_t sampled_image_type = _.GetOperandTypeId(inst, 2);
1834 const Instruction* sampled_image_type_inst = _.FindDef(sampled_image_type);
1835 assert(sampled_image_type_inst);
1836
1837 if (sampled_image_type_inst->opcode() != spv::Op::OpTypeSampledImage) {
1838 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1839 << "Expected Sample Image to be of type OpTypeSampleImage";
1840 }
1841
1842 if (sampled_image_type_inst->word(2) != result_type) {
1843 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1844 << "Expected Sample Image image type to be equal to Result Type";
1845 }
1846
1847 return SPV_SUCCESS;
1848 }
1849
ValidateImageQuerySizeLod(ValidationState_t & _,const Instruction * inst)1850 spv_result_t ValidateImageQuerySizeLod(ValidationState_t& _,
1851 const Instruction* inst) {
1852 const uint32_t result_type = inst->type_id();
1853 if (!_.IsIntScalarOrVectorType(result_type)) {
1854 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1855 << "Expected Result Type to be int scalar or vector type";
1856 }
1857
1858 const uint32_t image_type = _.GetOperandTypeId(inst, 2);
1859 if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
1860 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1861 << "Expected Image to be of type OpTypeImage";
1862 }
1863
1864 ImageTypeInfo info;
1865 if (!GetImageTypeInfo(_, image_type, &info)) {
1866 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1867 << "Corrupt image type definition";
1868 }
1869
1870 uint32_t expected_num_components = info.arrayed;
1871 switch (info.dim) {
1872 case spv::Dim::Dim1D:
1873 expected_num_components += 1;
1874 break;
1875 case spv::Dim::Dim2D:
1876 case spv::Dim::Cube:
1877 expected_num_components += 2;
1878 break;
1879 case spv::Dim::Dim3D:
1880 expected_num_components += 3;
1881 break;
1882 default:
1883 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1884 << "Image 'Dim' must be 1D, 2D, 3D or Cube";
1885 }
1886
1887 if (info.multisampled != 0) {
1888 return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Image 'MS' must be 0";
1889 }
1890
1891 const auto target_env = _.context()->target_env;
1892 if (spvIsVulkanEnv(target_env)) {
1893 if (info.sampled != 1) {
1894 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1895 << _.VkErrorID(4659)
1896 << "OpImageQuerySizeLod must only consume an \"Image\" operand "
1897 "whose type has its \"Sampled\" operand set to 1";
1898 }
1899 }
1900
1901 uint32_t result_num_components = _.GetDimension(result_type);
1902 if (result_num_components != expected_num_components) {
1903 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1904 << "Result Type has " << result_num_components << " components, "
1905 << "but " << expected_num_components << " expected";
1906 }
1907
1908 const uint32_t lod_type = _.GetOperandTypeId(inst, 3);
1909 if (!_.IsIntScalarType(lod_type)) {
1910 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1911 << "Expected Level of Detail to be int scalar";
1912 }
1913 return SPV_SUCCESS;
1914 }
1915
ValidateImageQuerySize(ValidationState_t & _,const Instruction * inst)1916 spv_result_t ValidateImageQuerySize(ValidationState_t& _,
1917 const Instruction* inst) {
1918 const uint32_t result_type = inst->type_id();
1919 if (!_.IsIntScalarOrVectorType(result_type)) {
1920 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1921 << "Expected Result Type to be int scalar or vector type";
1922 }
1923
1924 const uint32_t image_type = _.GetOperandTypeId(inst, 2);
1925 if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
1926 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1927 << "Expected Image to be of type OpTypeImage";
1928 }
1929
1930 ImageTypeInfo info;
1931 if (!GetImageTypeInfo(_, image_type, &info)) {
1932 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1933 << "Corrupt image type definition";
1934 }
1935
1936 uint32_t expected_num_components = info.arrayed;
1937 switch (info.dim) {
1938 case spv::Dim::Dim1D:
1939 case spv::Dim::Buffer:
1940 expected_num_components += 1;
1941 break;
1942 case spv::Dim::Dim2D:
1943 case spv::Dim::Cube:
1944 case spv::Dim::Rect:
1945 expected_num_components += 2;
1946 break;
1947 case spv::Dim::Dim3D:
1948 expected_num_components += 3;
1949 break;
1950 default:
1951 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1952 << "Image 'Dim' must be 1D, Buffer, 2D, Cube, 3D or Rect";
1953 }
1954
1955 if (info.dim == spv::Dim::Dim1D || info.dim == spv::Dim::Dim2D ||
1956 info.dim == spv::Dim::Dim3D || info.dim == spv::Dim::Cube) {
1957 if (info.multisampled != 1 && info.sampled != 0 && info.sampled != 2) {
1958 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1959 << "Image must have either 'MS'=1 or 'Sampled'=0 or 'Sampled'=2";
1960 }
1961 }
1962
1963 uint32_t result_num_components = _.GetDimension(result_type);
1964 if (result_num_components != expected_num_components) {
1965 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1966 << "Result Type has " << result_num_components << " components, "
1967 << "but " << expected_num_components << " expected";
1968 }
1969
1970 return SPV_SUCCESS;
1971 }
1972
ValidateImageQueryFormatOrOrder(ValidationState_t & _,const Instruction * inst)1973 spv_result_t ValidateImageQueryFormatOrOrder(ValidationState_t& _,
1974 const Instruction* inst) {
1975 if (!_.IsIntScalarType(inst->type_id())) {
1976 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1977 << "Expected Result Type to be int scalar type";
1978 }
1979
1980 const uint32_t image_type = _.GetOperandTypeId(inst, 2);
1981 if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
1982 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1983 << "Expected operand to be of type OpTypeImage";
1984 }
1985
1986 ImageTypeInfo info;
1987 if (!GetImageTypeInfo(_, image_type, &info)) {
1988 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1989 << "Corrupt image type definition";
1990 }
1991
1992 if (info.dim == spv::Dim::TileImageDataEXT) {
1993 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1994 << "Image 'Dim' cannot be TileImageDataEXT";
1995 }
1996 return SPV_SUCCESS;
1997 }
1998
ValidateImageQueryLod(ValidationState_t & _,const Instruction * inst)1999 spv_result_t ValidateImageQueryLod(ValidationState_t& _,
2000 const Instruction* inst) {
2001 _.function(inst->function()->id())
2002 ->RegisterExecutionModelLimitation(
2003 [&](spv::ExecutionModel model, std::string* message) {
2004 if (model != spv::ExecutionModel::Fragment &&
2005 model != spv::ExecutionModel::GLCompute) {
2006 if (message) {
2007 *message = std::string(
2008 "OpImageQueryLod requires Fragment or GLCompute execution "
2009 "model");
2010 }
2011 return false;
2012 }
2013 return true;
2014 });
2015 _.function(inst->function()->id())
2016 ->RegisterLimitation([](const ValidationState_t& state,
2017 const Function* entry_point,
2018 std::string* message) {
2019 const auto* models = state.GetExecutionModels(entry_point->id());
2020 const auto* modes = state.GetExecutionModes(entry_point->id());
2021 if (models->find(spv::ExecutionModel::GLCompute) != models->end() &&
2022 modes->find(spv::ExecutionMode::DerivativeGroupLinearNV) ==
2023 modes->end() &&
2024 modes->find(spv::ExecutionMode::DerivativeGroupQuadsNV) ==
2025 modes->end()) {
2026 if (message) {
2027 *message = std::string(
2028 "OpImageQueryLod requires DerivativeGroupQuadsNV "
2029 "or DerivativeGroupLinearNV execution mode for GLCompute "
2030 "execution model");
2031 }
2032 return false;
2033 }
2034 return true;
2035 });
2036
2037 const uint32_t result_type = inst->type_id();
2038 if (!_.IsFloatVectorType(result_type)) {
2039 return _.diag(SPV_ERROR_INVALID_DATA, inst)
2040 << "Expected Result Type to be float vector type";
2041 }
2042
2043 if (_.GetDimension(result_type) != 2) {
2044 return _.diag(SPV_ERROR_INVALID_DATA, inst)
2045 << "Expected Result Type to have 2 components";
2046 }
2047
2048 const uint32_t image_type = _.GetOperandTypeId(inst, 2);
2049 if (_.GetIdOpcode(image_type) != spv::Op::OpTypeSampledImage) {
2050 return _.diag(SPV_ERROR_INVALID_DATA, inst)
2051 << "Expected Image operand to be of type OpTypeSampledImage";
2052 }
2053
2054 ImageTypeInfo info;
2055 if (!GetImageTypeInfo(_, image_type, &info)) {
2056 return _.diag(SPV_ERROR_INVALID_DATA, inst)
2057 << "Corrupt image type definition";
2058 }
2059
2060 if (info.dim != spv::Dim::Dim1D && info.dim != spv::Dim::Dim2D &&
2061 info.dim != spv::Dim::Dim3D && info.dim != spv::Dim::Cube) {
2062 return _.diag(SPV_ERROR_INVALID_DATA, inst)
2063 << "Image 'Dim' must be 1D, 2D, 3D or Cube";
2064 }
2065
2066 const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
2067 if (_.HasCapability(spv::Capability::Kernel)) {
2068 if (!_.IsFloatScalarOrVectorType(coord_type) &&
2069 !_.IsIntScalarOrVectorType(coord_type)) {
2070 return _.diag(SPV_ERROR_INVALID_DATA, inst)
2071 << "Expected Coordinate to be int or float scalar or vector";
2072 }
2073 } else {
2074 if (!_.IsFloatScalarOrVectorType(coord_type)) {
2075 return _.diag(SPV_ERROR_INVALID_DATA, inst)
2076 << "Expected Coordinate to be float scalar or vector";
2077 }
2078 }
2079
2080 const uint32_t min_coord_size = GetPlaneCoordSize(info);
2081 const uint32_t actual_coord_size = _.GetDimension(coord_type);
2082 if (min_coord_size > actual_coord_size) {
2083 return _.diag(SPV_ERROR_INVALID_DATA, inst)
2084 << "Expected Coordinate to have at least " << min_coord_size
2085 << " components, but given only " << actual_coord_size;
2086 }
2087
2088 // The operand is a sampled image.
2089 // The sampled image type is already checked to be parameterized by an image
2090 // type with Sampled=0 or Sampled=1. Vulkan bans Sampled=0, and so we have
2091 // Sampled=1. So the validator already enforces Vulkan VUID 4659:
2092 // OpImageQuerySizeLod must only consume an "Image" operand whose type has
2093 // its "Sampled" operand set to 1
2094 return SPV_SUCCESS;
2095 }
2096
ValidateImageSparseLod(ValidationState_t & _,const Instruction * inst)2097 spv_result_t ValidateImageSparseLod(ValidationState_t& _,
2098 const Instruction* inst) {
2099 return _.diag(SPV_ERROR_INVALID_DATA, inst)
2100 << "Instruction reserved for future use, use of this instruction "
2101 << "is invalid";
2102 }
2103
ValidateImageQueryLevelsOrSamples(ValidationState_t & _,const Instruction * inst)2104 spv_result_t ValidateImageQueryLevelsOrSamples(ValidationState_t& _,
2105 const Instruction* inst) {
2106 if (!_.IsIntScalarType(inst->type_id())) {
2107 return _.diag(SPV_ERROR_INVALID_DATA, inst)
2108 << "Expected Result Type to be int scalar type";
2109 }
2110
2111 const uint32_t image_type = _.GetOperandTypeId(inst, 2);
2112 if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
2113 return _.diag(SPV_ERROR_INVALID_DATA, inst)
2114 << "Expected Image to be of type OpTypeImage";
2115 }
2116
2117 ImageTypeInfo info;
2118 if (!GetImageTypeInfo(_, image_type, &info)) {
2119 return _.diag(SPV_ERROR_INVALID_DATA, inst)
2120 << "Corrupt image type definition";
2121 }
2122
2123 const spv::Op opcode = inst->opcode();
2124 if (opcode == spv::Op::OpImageQueryLevels) {
2125 if (info.dim != spv::Dim::Dim1D && info.dim != spv::Dim::Dim2D &&
2126 info.dim != spv::Dim::Dim3D && info.dim != spv::Dim::Cube) {
2127 return _.diag(SPV_ERROR_INVALID_DATA, inst)
2128 << "Image 'Dim' must be 1D, 2D, 3D or Cube";
2129 }
2130 const auto target_env = _.context()->target_env;
2131 if (spvIsVulkanEnv(target_env)) {
2132 if (info.sampled != 1) {
2133 return _.diag(SPV_ERROR_INVALID_DATA, inst)
2134 << _.VkErrorID(4659)
2135 << "OpImageQueryLevels must only consume an \"Image\" operand "
2136 "whose type has its \"Sampled\" operand set to 1";
2137 }
2138 }
2139 } else {
2140 assert(opcode == spv::Op::OpImageQuerySamples);
2141 if (info.dim != spv::Dim::Dim2D) {
2142 return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Image 'Dim' must be 2D";
2143 }
2144
2145 if (info.multisampled != 1) {
2146 return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Image 'MS' must be 1";
2147 }
2148 }
2149 return SPV_SUCCESS;
2150 }
2151
ValidateImageSparseTexelsResident(ValidationState_t & _,const Instruction * inst)2152 spv_result_t ValidateImageSparseTexelsResident(ValidationState_t& _,
2153 const Instruction* inst) {
2154 if (!_.IsBoolScalarType(inst->type_id())) {
2155 return _.diag(SPV_ERROR_INVALID_DATA, inst)
2156 << "Expected Result Type to be bool scalar type";
2157 }
2158
2159 const uint32_t resident_code_type = _.GetOperandTypeId(inst, 2);
2160 if (!_.IsIntScalarType(resident_code_type)) {
2161 return _.diag(SPV_ERROR_INVALID_DATA, inst)
2162 << "Expected Resident Code to be int scalar";
2163 }
2164
2165 return SPV_SUCCESS;
2166 }
2167
ValidateImageProcessingQCOMDecoration(ValidationState_t & _,int id,spv::Decoration decor)2168 spv_result_t ValidateImageProcessingQCOMDecoration(ValidationState_t& _, int id,
2169 spv::Decoration decor) {
2170 const Instruction* si_inst = nullptr;
2171 const Instruction* ld_inst = _.FindDef(id);
2172 bool is_intf_obj = (ld_inst->opcode() == spv::Op::OpSampledImage);
2173 if (is_intf_obj == true) {
2174 si_inst = ld_inst;
2175 int t_idx = si_inst->GetOperandAs<int>(2); // texture
2176 ld_inst = _.FindDef(t_idx);
2177 }
2178 if (ld_inst->opcode() != spv::Op::OpLoad) {
2179 return _.diag(SPV_ERROR_INVALID_DATA, ld_inst) << "Expect to see OpLoad";
2180 }
2181 int texture_id = ld_inst->GetOperandAs<int>(2); // variable to load
2182 if (!_.HasDecoration(texture_id, decor)) {
2183 return _.diag(SPV_ERROR_INVALID_DATA, ld_inst)
2184 << "Missing decoration " << _.SpvDecorationString(decor);
2185 }
2186
2187 return SPV_SUCCESS;
2188 }
2189
ValidateImageProcessing2QCOMWindowDecoration(ValidationState_t & _,int id)2190 spv_result_t ValidateImageProcessing2QCOMWindowDecoration(ValidationState_t& _,
2191 int id) {
2192 const Instruction* ld_inst = _.FindDef(id);
2193 bool is_intf_obj = (ld_inst->opcode() != spv::Op::OpSampledImage);
2194 if (is_intf_obj == true) {
2195 if (ld_inst->opcode() != spv::Op::OpLoad) {
2196 return _.diag(SPV_ERROR_INVALID_DATA, ld_inst) << "Expect to see OpLoad";
2197 }
2198 int texture_id = ld_inst->GetOperandAs<int>(2); // variable to load
2199 spv::Decoration decor = spv::Decoration::BlockMatchTextureQCOM;
2200 if (!_.HasDecoration(texture_id, decor)) {
2201 return _.diag(SPV_ERROR_INVALID_DATA, ld_inst)
2202 << "Missing decoration " << _.SpvDecorationString(decor);
2203 }
2204 decor = spv::Decoration::BlockMatchSamplerQCOM;
2205 if (!_.HasDecoration(texture_id, decor)) {
2206 return _.diag(SPV_ERROR_INVALID_DATA, ld_inst)
2207 << "Missing decoration " << _.SpvDecorationString(decor);
2208 }
2209 } else {
2210 const Instruction* si_inst = ld_inst;
2211 int t_idx = si_inst->GetOperandAs<int>(2); // texture
2212 const Instruction* t_ld_inst = _.FindDef(t_idx);
2213 if (t_ld_inst->opcode() != spv::Op::OpLoad) {
2214 return _.diag(SPV_ERROR_INVALID_DATA, t_ld_inst)
2215 << "Expect to see OpLoad";
2216 }
2217 int texture_id = t_ld_inst->GetOperandAs<int>(2); // variable to load
2218 spv::Decoration decor = spv::Decoration::BlockMatchTextureQCOM;
2219 if (!_.HasDecoration(texture_id, decor)) {
2220 return _.diag(SPV_ERROR_INVALID_DATA, ld_inst)
2221 << "Missing decoration " << _.SpvDecorationString(decor);
2222 }
2223 int s_idx = si_inst->GetOperandAs<int>(3); // sampler
2224 const Instruction* s_ld_inst = _.FindDef(s_idx);
2225 if (s_ld_inst->opcode() != spv::Op::OpLoad) {
2226 return _.diag(SPV_ERROR_INVALID_DATA, s_ld_inst)
2227 << "Expect to see OpLoad";
2228 }
2229 int sampler_id = s_ld_inst->GetOperandAs<int>(2); // variable to load
2230 decor = spv::Decoration::BlockMatchSamplerQCOM;
2231 if (!_.HasDecoration(sampler_id, decor)) {
2232 return _.diag(SPV_ERROR_INVALID_DATA, ld_inst)
2233 << "Missing decoration " << _.SpvDecorationString(decor);
2234 }
2235 }
2236
2237 return SPV_SUCCESS;
2238 }
2239
ValidateImageProcessingQCOM(ValidationState_t & _,const Instruction * inst)2240 spv_result_t ValidateImageProcessingQCOM(ValidationState_t& _,
2241 const Instruction* inst) {
2242 spv_result_t res = SPV_SUCCESS;
2243 const spv::Op opcode = inst->opcode();
2244 switch (opcode) {
2245 case spv::Op::OpImageSampleWeightedQCOM: {
2246 int wi_idx = inst->GetOperandAs<int>(4); // weight
2247 res = ValidateImageProcessingQCOMDecoration(
2248 _, wi_idx, spv::Decoration::WeightTextureQCOM);
2249 break;
2250 }
2251 case spv::Op::OpImageBlockMatchSSDQCOM:
2252 case spv::Op::OpImageBlockMatchSADQCOM: {
2253 int tgt_idx = inst->GetOperandAs<int>(2); // target
2254 res = ValidateImageProcessingQCOMDecoration(
2255 _, tgt_idx, spv::Decoration::BlockMatchTextureQCOM);
2256 if (res != SPV_SUCCESS) break;
2257 int ref_idx = inst->GetOperandAs<int>(4); // reference
2258 res = ValidateImageProcessingQCOMDecoration(
2259 _, ref_idx, spv::Decoration::BlockMatchTextureQCOM);
2260 break;
2261 }
2262 case spv::Op::OpImageBlockMatchWindowSSDQCOM:
2263 case spv::Op::OpImageBlockMatchWindowSADQCOM: {
2264 int tgt_idx = inst->GetOperandAs<int>(2); // target
2265 res = ValidateImageProcessing2QCOMWindowDecoration(_, tgt_idx);
2266 if (res != SPV_SUCCESS) break;
2267 int ref_idx = inst->GetOperandAs<int>(4); // reference
2268 res = ValidateImageProcessing2QCOMWindowDecoration(_, ref_idx);
2269 break;
2270 }
2271 case spv::Op::OpImageBlockMatchGatherSSDQCOM:
2272 case spv::Op::OpImageBlockMatchGatherSADQCOM: {
2273 int tgt_idx = inst->GetOperandAs<int>(2); // target
2274 res = ValidateImageProcessingQCOMDecoration(
2275 _, tgt_idx, spv::Decoration::BlockMatchTextureQCOM);
2276 if (res != SPV_SUCCESS) break;
2277 int ref_idx = inst->GetOperandAs<int>(4); // reference
2278 res = ValidateImageProcessingQCOMDecoration(
2279 _, ref_idx, spv::Decoration::BlockMatchTextureQCOM);
2280 break;
2281 }
2282 default:
2283 break;
2284 }
2285
2286 return res;
2287 }
2288
2289 } // namespace
2290
2291 // Validates correctness of image instructions.
ImagePass(ValidationState_t & _,const Instruction * inst)2292 spv_result_t ImagePass(ValidationState_t& _, const Instruction* inst) {
2293 const spv::Op opcode = inst->opcode();
2294 if (IsImplicitLod(opcode)) {
2295 _.function(inst->function()->id())
2296 ->RegisterExecutionModelLimitation([opcode](spv::ExecutionModel model,
2297 std::string* message) {
2298 if (model != spv::ExecutionModel::Fragment &&
2299 model != spv::ExecutionModel::GLCompute) {
2300 if (message) {
2301 *message =
2302 std::string(
2303 "ImplicitLod instructions require Fragment or GLCompute "
2304 "execution model: ") +
2305 spvOpcodeString(opcode);
2306 }
2307 return false;
2308 }
2309 return true;
2310 });
2311 _.function(inst->function()->id())
2312 ->RegisterLimitation([opcode](const ValidationState_t& state,
2313 const Function* entry_point,
2314 std::string* message) {
2315 const auto* models = state.GetExecutionModels(entry_point->id());
2316 const auto* modes = state.GetExecutionModes(entry_point->id());
2317 if (models &&
2318 models->find(spv::ExecutionModel::GLCompute) != models->end() &&
2319 (!modes ||
2320 (modes->find(spv::ExecutionMode::DerivativeGroupLinearNV) ==
2321 modes->end() &&
2322 modes->find(spv::ExecutionMode::DerivativeGroupQuadsNV) ==
2323 modes->end()))) {
2324 if (message) {
2325 *message =
2326 std::string(
2327 "ImplicitLod instructions require DerivativeGroupQuadsNV "
2328 "or DerivativeGroupLinearNV execution mode for GLCompute "
2329 "execution model: ") +
2330 spvOpcodeString(opcode);
2331 }
2332 return false;
2333 }
2334 return true;
2335 });
2336 }
2337
2338 switch (opcode) {
2339 case spv::Op::OpTypeImage:
2340 return ValidateTypeImage(_, inst);
2341 case spv::Op::OpTypeSampledImage:
2342 return ValidateTypeSampledImage(_, inst);
2343 case spv::Op::OpSampledImage:
2344 return ValidateSampledImage(_, inst);
2345 case spv::Op::OpImageTexelPointer:
2346 return ValidateImageTexelPointer(_, inst);
2347
2348 case spv::Op::OpImageSampleImplicitLod:
2349 case spv::Op::OpImageSampleExplicitLod:
2350 case spv::Op::OpImageSampleProjImplicitLod:
2351 case spv::Op::OpImageSampleProjExplicitLod:
2352 case spv::Op::OpImageSparseSampleImplicitLod:
2353 case spv::Op::OpImageSparseSampleExplicitLod:
2354 return ValidateImageLod(_, inst);
2355
2356 case spv::Op::OpImageSampleDrefImplicitLod:
2357 case spv::Op::OpImageSampleDrefExplicitLod:
2358 case spv::Op::OpImageSampleProjDrefImplicitLod:
2359 case spv::Op::OpImageSampleProjDrefExplicitLod:
2360 case spv::Op::OpImageSparseSampleDrefImplicitLod:
2361 case spv::Op::OpImageSparseSampleDrefExplicitLod:
2362 return ValidateImageDrefLod(_, inst);
2363
2364 case spv::Op::OpImageFetch:
2365 case spv::Op::OpImageSparseFetch:
2366 return ValidateImageFetch(_, inst);
2367
2368 case spv::Op::OpImageGather:
2369 case spv::Op::OpImageDrefGather:
2370 case spv::Op::OpImageSparseGather:
2371 case spv::Op::OpImageSparseDrefGather:
2372 return ValidateImageGather(_, inst);
2373
2374 case spv::Op::OpImageRead:
2375 case spv::Op::OpImageSparseRead:
2376 return ValidateImageRead(_, inst);
2377
2378 case spv::Op::OpImageWrite:
2379 return ValidateImageWrite(_, inst);
2380
2381 case spv::Op::OpImage:
2382 return ValidateImage(_, inst);
2383
2384 case spv::Op::OpImageQueryFormat:
2385 case spv::Op::OpImageQueryOrder:
2386 return ValidateImageQueryFormatOrOrder(_, inst);
2387
2388 case spv::Op::OpImageQuerySizeLod:
2389 return ValidateImageQuerySizeLod(_, inst);
2390 case spv::Op::OpImageQuerySize:
2391 return ValidateImageQuerySize(_, inst);
2392 case spv::Op::OpImageQueryLod:
2393 return ValidateImageQueryLod(_, inst);
2394
2395 case spv::Op::OpImageQueryLevels:
2396 case spv::Op::OpImageQuerySamples:
2397 return ValidateImageQueryLevelsOrSamples(_, inst);
2398
2399 case spv::Op::OpImageSparseSampleProjImplicitLod:
2400 case spv::Op::OpImageSparseSampleProjExplicitLod:
2401 case spv::Op::OpImageSparseSampleProjDrefImplicitLod:
2402 case spv::Op::OpImageSparseSampleProjDrefExplicitLod:
2403 return ValidateImageSparseLod(_, inst);
2404
2405 case spv::Op::OpImageSparseTexelsResident:
2406 return ValidateImageSparseTexelsResident(_, inst);
2407
2408 case spv::Op::OpImageSampleWeightedQCOM:
2409 case spv::Op::OpImageBoxFilterQCOM:
2410 case spv::Op::OpImageBlockMatchSSDQCOM:
2411 case spv::Op::OpImageBlockMatchSADQCOM:
2412 case spv::Op::OpImageBlockMatchWindowSADQCOM:
2413 case spv::Op::OpImageBlockMatchWindowSSDQCOM:
2414 case spv::Op::OpImageBlockMatchGatherSADQCOM:
2415 case spv::Op::OpImageBlockMatchGatherSSDQCOM:
2416 return ValidateImageProcessingQCOM(_, inst);
2417
2418 default:
2419 break;
2420 }
2421
2422 return SPV_SUCCESS;
2423 }
2424
IsImageInstruction(const spv::Op opcode)2425 bool IsImageInstruction(const spv::Op opcode) {
2426 switch (opcode) {
2427 case spv::Op::OpImageSampleImplicitLod:
2428 case spv::Op::OpImageSampleDrefImplicitLod:
2429 case spv::Op::OpImageSampleProjImplicitLod:
2430 case spv::Op::OpImageSampleProjDrefImplicitLod:
2431 case spv::Op::OpImageSparseSampleImplicitLod:
2432 case spv::Op::OpImageSparseSampleDrefImplicitLod:
2433 case spv::Op::OpImageSparseSampleProjImplicitLod:
2434 case spv::Op::OpImageSparseSampleProjDrefImplicitLod:
2435
2436 case spv::Op::OpImageSampleExplicitLod:
2437 case spv::Op::OpImageSampleDrefExplicitLod:
2438 case spv::Op::OpImageSampleProjExplicitLod:
2439 case spv::Op::OpImageSampleProjDrefExplicitLod:
2440 case spv::Op::OpImageSparseSampleExplicitLod:
2441 case spv::Op::OpImageSparseSampleDrefExplicitLod:
2442 case spv::Op::OpImageSparseSampleProjExplicitLod:
2443 case spv::Op::OpImageSparseSampleProjDrefExplicitLod:
2444
2445 case spv::Op::OpImage:
2446 case spv::Op::OpImageFetch:
2447 case spv::Op::OpImageSparseFetch:
2448 case spv::Op::OpImageGather:
2449 case spv::Op::OpImageDrefGather:
2450 case spv::Op::OpImageSparseGather:
2451 case spv::Op::OpImageSparseDrefGather:
2452 case spv::Op::OpImageRead:
2453 case spv::Op::OpImageSparseRead:
2454 case spv::Op::OpImageWrite:
2455
2456 case spv::Op::OpImageQueryFormat:
2457 case spv::Op::OpImageQueryOrder:
2458 case spv::Op::OpImageQuerySizeLod:
2459 case spv::Op::OpImageQuerySize:
2460 case spv::Op::OpImageQueryLod:
2461 case spv::Op::OpImageQueryLevels:
2462 case spv::Op::OpImageQuerySamples:
2463
2464 case spv::Op::OpImageSampleWeightedQCOM:
2465 case spv::Op::OpImageBoxFilterQCOM:
2466 case spv::Op::OpImageBlockMatchSSDQCOM:
2467 case spv::Op::OpImageBlockMatchSADQCOM:
2468 case spv::Op::OpImageBlockMatchWindowSADQCOM:
2469 case spv::Op::OpImageBlockMatchWindowSSDQCOM:
2470 case spv::Op::OpImageBlockMatchGatherSADQCOM:
2471 case spv::Op::OpImageBlockMatchGatherSSDQCOM:
2472 return true;
2473 default:
2474 break;
2475 }
2476 return false;
2477 }
2478
ValidateQCOMImageProcessingTextureUsages(ValidationState_t & _,const Instruction * inst)2479 spv_result_t ValidateQCOMImageProcessingTextureUsages(ValidationState_t& _,
2480 const Instruction* inst) {
2481 const spv::Op opcode = inst->opcode();
2482 if (!IsImageInstruction(opcode)) return SPV_SUCCESS;
2483
2484 switch (opcode) {
2485 case spv::Op::OpImageSampleWeightedQCOM:
2486 case spv::Op::OpImageBoxFilterQCOM:
2487 case spv::Op::OpImageBlockMatchSSDQCOM:
2488 case spv::Op::OpImageBlockMatchSADQCOM:
2489 break;
2490 case spv::Op::OpImageBlockMatchWindowSADQCOM:
2491 case spv::Op::OpImageBlockMatchWindowSSDQCOM:
2492 case spv::Op::OpImageBlockMatchGatherSADQCOM:
2493 case spv::Op::OpImageBlockMatchGatherSSDQCOM:
2494 break;
2495 default:
2496 for (size_t i = 0; i < inst->operands().size(); ++i) {
2497 int id = inst->GetOperandAs<int>(i);
2498 const Instruction* operand_inst = _.FindDef(id);
2499 if (operand_inst == nullptr) continue;
2500 if (operand_inst->opcode() == spv::Op::OpLoad) {
2501 if (_.IsQCOMImageProcessingTextureConsumer(id)) {
2502 return _.diag(SPV_ERROR_INVALID_DATA, inst)
2503 << "Illegal use of QCOM image processing decorated texture";
2504 }
2505 }
2506 if (operand_inst->opcode() == spv::Op::OpSampledImage) {
2507 if (_.IsQCOMImageProcessingTextureConsumer(id)) {
2508 return _.diag(SPV_ERROR_INVALID_DATA, inst)
2509 << "Illegal use of QCOM image processing decorated texture";
2510 }
2511 }
2512 }
2513 break;
2514 }
2515 return SPV_SUCCESS;
2516 }
2517
2518 } // namespace val
2519 } // namespace spvtools
2520