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 auto type_inst = _.FindDef(inst->type_id());
1009 if (type_inst->opcode() != spv::Op::OpTypeSampledImage) {
1010 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1011 << "Expected Result Type to be OpTypeSampledImage.";
1012 }
1013
1014 const uint32_t image_type = _.GetOperandTypeId(inst, 2);
1015 if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
1016 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1017 << "Expected Image to be of type OpTypeImage.";
1018 }
1019
1020 if (type_inst->GetOperandAs<uint32_t>(1) != image_type) {
1021 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1022 << "Expected Image to have the same type as Result Type Image";
1023 }
1024
1025 ImageTypeInfo info;
1026 if (!GetImageTypeInfo(_, image_type, &info)) {
1027 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1028 << "Corrupt image type definition";
1029 }
1030
1031 // TODO(atgoo@github.com) Check compatibility of result type and received
1032 // image.
1033
1034 if (spvIsVulkanEnv(_.context()->target_env)) {
1035 if (info.sampled != 1) {
1036 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1037 << _.VkErrorID(6671)
1038 << "Expected Image 'Sampled' parameter to be 1 for Vulkan "
1039 "environment.";
1040 }
1041 } else {
1042 if (info.sampled != 0 && info.sampled != 1) {
1043 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1044 << "Expected Image 'Sampled' parameter to be 0 or 1";
1045 }
1046 }
1047
1048 if (info.dim == spv::Dim::SubpassData) {
1049 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1050 << "Expected Image 'Dim' parameter to be not SubpassData.";
1051 }
1052
1053 if (_.GetIdOpcode(_.GetOperandTypeId(inst, 3)) != spv::Op::OpTypeSampler) {
1054 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1055 << "Expected Sampler to be of type OpTypeSampler";
1056 }
1057
1058 // We need to validate 2 things:
1059 // * All OpSampledImage instructions must be in the same block in which their
1060 // Result <id> are consumed.
1061 // * Result <id> from OpSampledImage instructions must not appear as operands
1062 // to OpPhi instructions or OpSelect instructions, or any instructions other
1063 // than the image lookup and query instructions specified to take an operand
1064 // whose type is OpTypeSampledImage.
1065 std::vector<Instruction*> consumers = _.getSampledImageConsumers(inst->id());
1066 if (!consumers.empty()) {
1067 for (auto consumer_instr : consumers) {
1068 const auto consumer_opcode = consumer_instr->opcode();
1069 if (consumer_instr->block() != inst->block()) {
1070 return _.diag(SPV_ERROR_INVALID_ID, inst)
1071 << "All OpSampledImage instructions must be in the same block "
1072 "in "
1073 "which their Result <id> are consumed. OpSampledImage Result "
1074 "Type <id> "
1075 << _.getIdName(inst->id())
1076 << " has a consumer in a different basic "
1077 "block. The consumer instruction <id> is "
1078 << _.getIdName(consumer_instr->id()) << ".";
1079 }
1080
1081 if (consumer_opcode == spv::Op::OpPhi ||
1082 consumer_opcode == spv::Op::OpSelect) {
1083 return _.diag(SPV_ERROR_INVALID_ID, inst)
1084 << "Result <id> from OpSampledImage instruction must not appear "
1085 "as "
1086 "operands of Op"
1087 << spvOpcodeString(static_cast<spv::Op>(consumer_opcode)) << "."
1088 << " Found result <id> " << _.getIdName(inst->id())
1089 << " as an operand of <id> " << _.getIdName(consumer_instr->id())
1090 << ".";
1091 }
1092
1093 if (!IsAllowedSampledImageOperand(consumer_opcode, _)) {
1094 return _.diag(SPV_ERROR_INVALID_ID, inst)
1095 << "Result <id> from OpSampledImage instruction must not appear "
1096 "as operand for Op"
1097 << spvOpcodeString(static_cast<spv::Op>(consumer_opcode))
1098 << ", since it is not specified as taking an "
1099 << "OpTypeSampledImage."
1100 << " Found result <id> " << _.getIdName(inst->id())
1101 << " as an operand of <id> " << _.getIdName(consumer_instr->id())
1102 << ".";
1103 }
1104 }
1105 }
1106
1107 const Instruction* ld_inst;
1108 {
1109 int t_idx = inst->GetOperandAs<int>(2);
1110 ld_inst = _.FindDef(t_idx);
1111 }
1112
1113 if (ld_inst->opcode() == spv::Op::OpLoad) {
1114 int texture_id = ld_inst->GetOperandAs<int>(2); // variable to load
1115 _.RegisterQCOMImageProcessingTextureConsumer(texture_id, ld_inst, inst);
1116 }
1117
1118 return SPV_SUCCESS;
1119 }
1120
ValidateImageTexelPointer(ValidationState_t & _,const Instruction * inst)1121 spv_result_t ValidateImageTexelPointer(ValidationState_t& _,
1122 const Instruction* inst) {
1123 const auto result_type = _.FindDef(inst->type_id());
1124 if (result_type->opcode() != spv::Op::OpTypePointer) {
1125 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1126 << "Expected Result Type to be OpTypePointer";
1127 }
1128
1129 const auto storage_class = result_type->GetOperandAs<spv::StorageClass>(1);
1130 if (storage_class != spv::StorageClass::Image) {
1131 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1132 << "Expected Result Type to be OpTypePointer whose Storage Class "
1133 "operand is Image";
1134 }
1135
1136 const auto ptr_type = result_type->GetOperandAs<uint32_t>(2);
1137 const auto ptr_opcode = _.GetIdOpcode(ptr_type);
1138 if (ptr_opcode != spv::Op::OpTypeInt && ptr_opcode != spv::Op::OpTypeFloat &&
1139 ptr_opcode != spv::Op::OpTypeVoid &&
1140 !(ptr_opcode == spv::Op::OpTypeVector &&
1141 _.HasCapability(spv::Capability::AtomicFloat16VectorNV) &&
1142 _.IsFloat16Vector2Or4Type(ptr_type))) {
1143 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1144 << "Expected Result Type to be OpTypePointer whose Type operand "
1145 "must be a scalar numerical type or OpTypeVoid";
1146 }
1147
1148 const auto image_ptr = _.FindDef(_.GetOperandTypeId(inst, 2));
1149 if (!image_ptr || image_ptr->opcode() != spv::Op::OpTypePointer) {
1150 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1151 << "Expected Image to be OpTypePointer";
1152 }
1153
1154 const auto image_type = image_ptr->GetOperandAs<uint32_t>(2);
1155 if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
1156 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1157 << "Expected Image to be OpTypePointer with Type OpTypeImage";
1158 }
1159
1160 ImageTypeInfo info;
1161 if (!GetImageTypeInfo(_, image_type, &info)) {
1162 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1163 << "Corrupt image type definition";
1164 }
1165
1166 if (info.sampled_type != ptr_type &&
1167 !(_.HasCapability(spv::Capability::AtomicFloat16VectorNV) &&
1168 _.IsFloat16Vector2Or4Type(ptr_type) &&
1169 _.GetIdOpcode(info.sampled_type) == spv::Op::OpTypeFloat &&
1170 ((_.GetDimension(ptr_type) == 2 &&
1171 info.format == spv::ImageFormat::Rg16f) ||
1172 (_.GetDimension(ptr_type) == 4 &&
1173 info.format == spv::ImageFormat::Rgba16f)))) {
1174 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1175 << "Expected Image 'Sampled Type' to be the same as the Type "
1176 "pointed to by Result Type";
1177 }
1178
1179 if (info.dim == spv::Dim::SubpassData) {
1180 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1181 << "Image Dim SubpassData cannot be used with OpImageTexelPointer";
1182 }
1183
1184 if (info.dim == spv::Dim::TileImageDataEXT) {
1185 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1186 << "Image Dim TileImageDataEXT cannot be used with "
1187 "OpImageTexelPointer";
1188 }
1189
1190 const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
1191 if (!coord_type || !_.IsIntScalarOrVectorType(coord_type)) {
1192 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1193 << "Expected Coordinate to be integer scalar or vector";
1194 }
1195
1196 uint32_t expected_coord_size = 0;
1197 if (info.arrayed == 0) {
1198 expected_coord_size = GetPlaneCoordSize(info);
1199 } else if (info.arrayed == 1) {
1200 switch (info.dim) {
1201 case spv::Dim::Dim1D:
1202 expected_coord_size = 2;
1203 break;
1204 case spv::Dim::Cube:
1205 case spv::Dim::Dim2D:
1206 expected_coord_size = 3;
1207 break;
1208 default:
1209 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1210 << "Expected Image 'Dim' must be one of 1D, 2D, or Cube when "
1211 "Arrayed is 1";
1212 break;
1213 }
1214 }
1215
1216 const uint32_t actual_coord_size = _.GetDimension(coord_type);
1217 if (expected_coord_size != actual_coord_size) {
1218 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1219 << "Expected Coordinate to have " << expected_coord_size
1220 << " components, but given " << actual_coord_size;
1221 }
1222
1223 const uint32_t sample_type = _.GetOperandTypeId(inst, 4);
1224 if (!sample_type || !_.IsIntScalarType(sample_type)) {
1225 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1226 << "Expected Sample to be integer scalar";
1227 }
1228
1229 if (info.multisampled == 0) {
1230 uint64_t ms = 0;
1231 if (!_.EvalConstantValUint64(inst->GetOperandAs<uint32_t>(4), &ms) ||
1232 ms != 0) {
1233 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1234 << "Expected Sample for Image with MS 0 to be a valid <id> for "
1235 "the value 0";
1236 }
1237 }
1238
1239 if (spvIsVulkanEnv(_.context()->target_env)) {
1240 if ((info.format != spv::ImageFormat::R64i) &&
1241 (info.format != spv::ImageFormat::R64ui) &&
1242 (info.format != spv::ImageFormat::R32f) &&
1243 (info.format != spv::ImageFormat::R32i) &&
1244 (info.format != spv::ImageFormat::R32ui) &&
1245 !((info.format == spv::ImageFormat::Rg16f ||
1246 info.format == spv::ImageFormat::Rgba16f) &&
1247 _.HasCapability(spv::Capability::AtomicFloat16VectorNV))) {
1248 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1249 << _.VkErrorID(4658)
1250 << "Expected the Image Format in Image to be R64i, R64ui, R32f, "
1251 "R32i, or R32ui for Vulkan environment";
1252 }
1253 }
1254
1255 return SPV_SUCCESS;
1256 }
1257
ValidateImageLod(ValidationState_t & _,const Instruction * inst)1258 spv_result_t ValidateImageLod(ValidationState_t& _, const Instruction* inst) {
1259 const spv::Op opcode = inst->opcode();
1260 uint32_t actual_result_type = 0;
1261 if (spv_result_t error = GetActualResultType(_, inst, &actual_result_type)) {
1262 return error;
1263 }
1264
1265 if (!_.IsIntVectorType(actual_result_type) &&
1266 !_.IsFloatVectorType(actual_result_type)) {
1267 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1268 << "Expected " << GetActualResultTypeStr(opcode)
1269 << " to be int or float vector type";
1270 }
1271
1272 if (_.GetDimension(actual_result_type) != 4) {
1273 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1274 << "Expected " << GetActualResultTypeStr(opcode)
1275 << " to have 4 components";
1276 }
1277
1278 const uint32_t image_type = _.GetOperandTypeId(inst, 2);
1279 if (_.GetIdOpcode(image_type) != spv::Op::OpTypeSampledImage) {
1280 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1281 << "Expected Sampled Image to be of type OpTypeSampledImage";
1282 }
1283
1284 ImageTypeInfo info;
1285 if (!GetImageTypeInfo(_, image_type, &info)) {
1286 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1287 << "Corrupt image type definition";
1288 }
1289
1290 if (IsProj(opcode)) {
1291 if (spv_result_t result = ValidateImageProj(_, inst, info)) return result;
1292 }
1293
1294 if (info.multisampled) {
1295 // When using image operands, the Sample image operand is required if and
1296 // only if the image is multisampled (MS=1). The Sample image operand is
1297 // only allowed for fetch, read, and write.
1298 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1299 << "Sampling operation is invalid for multisample image";
1300 }
1301
1302 if (_.GetIdOpcode(info.sampled_type) != spv::Op::OpTypeVoid) {
1303 const uint32_t texel_component_type =
1304 _.GetComponentType(actual_result_type);
1305 if (texel_component_type != info.sampled_type) {
1306 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1307 << "Expected Image 'Sampled Type' to be the same as "
1308 << GetActualResultTypeStr(opcode) << " components";
1309 }
1310 }
1311
1312 const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
1313 if ((opcode == spv::Op::OpImageSampleExplicitLod ||
1314 opcode == spv::Op::OpImageSparseSampleExplicitLod) &&
1315 _.HasCapability(spv::Capability::Kernel)) {
1316 if (!_.IsFloatScalarOrVectorType(coord_type) &&
1317 !_.IsIntScalarOrVectorType(coord_type)) {
1318 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1319 << "Expected Coordinate to be int or float scalar or vector";
1320 }
1321 } else {
1322 if (!_.IsFloatScalarOrVectorType(coord_type)) {
1323 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1324 << "Expected Coordinate to be float scalar or vector";
1325 }
1326 }
1327
1328 const uint32_t min_coord_size = GetMinCoordSize(opcode, info);
1329 const uint32_t actual_coord_size = _.GetDimension(coord_type);
1330 if (min_coord_size > actual_coord_size) {
1331 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1332 << "Expected Coordinate to have at least " << min_coord_size
1333 << " components, but given only " << actual_coord_size;
1334 }
1335
1336 const uint32_t mask = inst->words().size() <= 5 ? 0 : inst->word(5);
1337
1338 if (mask & uint32_t(spv::ImageOperandsMask::ConstOffset)) {
1339 if (spvIsOpenCLEnv(_.context()->target_env)) {
1340 if (opcode == spv::Op::OpImageSampleExplicitLod) {
1341 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1342 << "ConstOffset image operand not allowed "
1343 << "in the OpenCL environment.";
1344 }
1345 }
1346 }
1347
1348 if (spv_result_t result =
1349 ValidateImageOperands(_, inst, info, /* word_index = */ 6))
1350 return result;
1351
1352 return SPV_SUCCESS;
1353 }
1354
1355 // Validates anything OpImage*Dref* instruction
ValidateImageDref(ValidationState_t & _,const Instruction * inst,const ImageTypeInfo & info)1356 spv_result_t ValidateImageDref(ValidationState_t& _, const Instruction* inst,
1357 const ImageTypeInfo& info) {
1358 const uint32_t dref_type = _.GetOperandTypeId(inst, 4);
1359 if (!_.IsFloatScalarType(dref_type) || _.GetBitWidth(dref_type) != 32) {
1360 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1361 << "Expected Dref to be of 32-bit float type";
1362 }
1363
1364 if (spvIsVulkanEnv(_.context()->target_env)) {
1365 if (info.dim == spv::Dim::Dim3D) {
1366 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1367 << _.VkErrorID(4777)
1368 << "In Vulkan, OpImage*Dref* instructions must not use images "
1369 "with a 3D Dim";
1370 }
1371 }
1372
1373 return SPV_SUCCESS;
1374 }
1375
ValidateImageDrefLod(ValidationState_t & _,const Instruction * inst)1376 spv_result_t ValidateImageDrefLod(ValidationState_t& _,
1377 const Instruction* inst) {
1378 const spv::Op opcode = inst->opcode();
1379 uint32_t actual_result_type = 0;
1380 if (spv_result_t error = GetActualResultType(_, inst, &actual_result_type)) {
1381 return error;
1382 }
1383
1384 if (!_.IsIntScalarType(actual_result_type) &&
1385 !_.IsFloatScalarType(actual_result_type)) {
1386 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1387 << "Expected " << GetActualResultTypeStr(opcode)
1388 << " to be int or float scalar type";
1389 }
1390
1391 const uint32_t image_type = _.GetOperandTypeId(inst, 2);
1392 if (_.GetIdOpcode(image_type) != spv::Op::OpTypeSampledImage) {
1393 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1394 << "Expected Sampled Image to be of type OpTypeSampledImage";
1395 }
1396
1397 ImageTypeInfo info;
1398 if (!GetImageTypeInfo(_, image_type, &info)) {
1399 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1400 << "Corrupt image type definition";
1401 }
1402
1403 if (IsProj(opcode)) {
1404 if (spv_result_t result = ValidateImageProj(_, inst, info)) return result;
1405 }
1406
1407 if (info.multisampled) {
1408 // When using image operands, the Sample image operand is required if and
1409 // only if the image is multisampled (MS=1). The Sample image operand is
1410 // only allowed for fetch, read, and write.
1411 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1412 << "Dref sampling operation is invalid for multisample image";
1413 }
1414
1415 if (actual_result_type != info.sampled_type) {
1416 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1417 << "Expected Image 'Sampled Type' to be the same as "
1418 << GetActualResultTypeStr(opcode);
1419 }
1420
1421 const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
1422 if (!_.IsFloatScalarOrVectorType(coord_type)) {
1423 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1424 << "Expected Coordinate to be float scalar or vector";
1425 }
1426
1427 const uint32_t min_coord_size = GetMinCoordSize(opcode, info);
1428 const uint32_t actual_coord_size = _.GetDimension(coord_type);
1429 if (min_coord_size > actual_coord_size) {
1430 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1431 << "Expected Coordinate to have at least " << min_coord_size
1432 << " components, but given only " << actual_coord_size;
1433 }
1434
1435 if (spv_result_t result = ValidateImageDref(_, inst, info)) return result;
1436
1437 if (spv_result_t result =
1438 ValidateImageOperands(_, inst, info, /* word_index = */ 7))
1439 return result;
1440
1441 return SPV_SUCCESS;
1442 }
1443
ValidateImageFetch(ValidationState_t & _,const Instruction * inst)1444 spv_result_t ValidateImageFetch(ValidationState_t& _, const Instruction* inst) {
1445 uint32_t actual_result_type = 0;
1446 if (spv_result_t error = GetActualResultType(_, inst, &actual_result_type)) {
1447 return error;
1448 }
1449
1450 const spv::Op opcode = inst->opcode();
1451 if (!_.IsIntVectorType(actual_result_type) &&
1452 !_.IsFloatVectorType(actual_result_type)) {
1453 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1454 << "Expected " << GetActualResultTypeStr(opcode)
1455 << " to be int or float vector type";
1456 }
1457
1458 if (_.GetDimension(actual_result_type) != 4) {
1459 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1460 << "Expected " << GetActualResultTypeStr(opcode)
1461 << " to have 4 components";
1462 }
1463
1464 const uint32_t image_type = _.GetOperandTypeId(inst, 2);
1465 if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
1466 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1467 << "Expected Image to be of type OpTypeImage";
1468 }
1469
1470 ImageTypeInfo info;
1471 if (!GetImageTypeInfo(_, image_type, &info)) {
1472 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1473 << "Corrupt image type definition";
1474 }
1475
1476 if (_.GetIdOpcode(info.sampled_type) != spv::Op::OpTypeVoid) {
1477 const uint32_t result_component_type =
1478 _.GetComponentType(actual_result_type);
1479 if (result_component_type != info.sampled_type) {
1480 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1481 << "Expected Image 'Sampled Type' to be the same as "
1482 << GetActualResultTypeStr(opcode) << " components";
1483 }
1484 }
1485
1486 if (info.dim == spv::Dim::Cube) {
1487 return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Image 'Dim' cannot be Cube";
1488 }
1489
1490 if (info.sampled != 1) {
1491 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1492 << "Expected Image 'Sampled' parameter to be 1";
1493 }
1494
1495 const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
1496 if (!_.IsIntScalarOrVectorType(coord_type)) {
1497 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1498 << "Expected Coordinate to be int scalar or vector";
1499 }
1500
1501 const uint32_t min_coord_size = GetMinCoordSize(opcode, info);
1502 const uint32_t actual_coord_size = _.GetDimension(coord_type);
1503 if (min_coord_size > actual_coord_size) {
1504 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1505 << "Expected Coordinate to have at least " << min_coord_size
1506 << " components, but given only " << actual_coord_size;
1507 }
1508
1509 if (spv_result_t result =
1510 ValidateImageOperands(_, inst, info, /* word_index = */ 6))
1511 return result;
1512
1513 return SPV_SUCCESS;
1514 }
1515
ValidateImageGather(ValidationState_t & _,const Instruction * inst)1516 spv_result_t ValidateImageGather(ValidationState_t& _,
1517 const Instruction* inst) {
1518 uint32_t actual_result_type = 0;
1519 if (spv_result_t error = GetActualResultType(_, inst, &actual_result_type))
1520 return error;
1521
1522 const spv::Op opcode = inst->opcode();
1523 if (!_.IsIntVectorType(actual_result_type) &&
1524 !_.IsFloatVectorType(actual_result_type)) {
1525 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1526 << "Expected " << GetActualResultTypeStr(opcode)
1527 << " to be int or float vector type";
1528 }
1529
1530 if (_.GetDimension(actual_result_type) != 4) {
1531 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1532 << "Expected " << GetActualResultTypeStr(opcode)
1533 << " to have 4 components";
1534 }
1535
1536 const uint32_t image_type = _.GetOperandTypeId(inst, 2);
1537 if (_.GetIdOpcode(image_type) != spv::Op::OpTypeSampledImage) {
1538 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1539 << "Expected Sampled Image to be of type OpTypeSampledImage";
1540 }
1541
1542 ImageTypeInfo info;
1543 if (!GetImageTypeInfo(_, image_type, &info)) {
1544 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1545 << "Corrupt image type definition";
1546 }
1547
1548 if (info.multisampled) {
1549 // When using image operands, the Sample image operand is required if and
1550 // only if the image is multisampled (MS=1). The Sample image operand is
1551 // only allowed for fetch, read, and write.
1552 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1553 << "Gather operation is invalid for multisample image";
1554 }
1555
1556 if (opcode == spv::Op::OpImageDrefGather ||
1557 opcode == spv::Op::OpImageSparseDrefGather ||
1558 _.GetIdOpcode(info.sampled_type) != spv::Op::OpTypeVoid) {
1559 const uint32_t result_component_type =
1560 _.GetComponentType(actual_result_type);
1561 if (result_component_type != info.sampled_type) {
1562 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1563 << "Expected Image 'Sampled Type' to be the same as "
1564 << GetActualResultTypeStr(opcode) << " components";
1565 }
1566 }
1567
1568 if (info.dim != spv::Dim::Dim2D && info.dim != spv::Dim::Cube &&
1569 info.dim != spv::Dim::Rect) {
1570 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1571 << _.VkErrorID(4777)
1572 << "Expected Image 'Dim' to be 2D, Cube, or Rect";
1573 }
1574
1575 const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
1576 if (!_.IsFloatScalarOrVectorType(coord_type)) {
1577 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1578 << "Expected Coordinate to be float scalar or vector";
1579 }
1580
1581 const uint32_t min_coord_size = GetMinCoordSize(opcode, info);
1582 const uint32_t actual_coord_size = _.GetDimension(coord_type);
1583 if (min_coord_size > actual_coord_size) {
1584 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1585 << "Expected Coordinate to have at least " << min_coord_size
1586 << " components, but given only " << actual_coord_size;
1587 }
1588
1589 if (opcode == spv::Op::OpImageGather ||
1590 opcode == spv::Op::OpImageSparseGather) {
1591 const uint32_t component = inst->GetOperandAs<uint32_t>(4);
1592 const uint32_t component_index_type = _.GetTypeId(component);
1593 if (!_.IsIntScalarType(component_index_type) ||
1594 _.GetBitWidth(component_index_type) != 32) {
1595 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1596 << "Expected Component to be 32-bit int scalar";
1597 }
1598 if (spvIsVulkanEnv(_.context()->target_env)) {
1599 if (!spvOpcodeIsConstant(_.GetIdOpcode(component))) {
1600 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1601 << _.VkErrorID(4664)
1602 << "Expected Component Operand to be a const object for Vulkan "
1603 "environment";
1604 }
1605 }
1606 } else {
1607 assert(opcode == spv::Op::OpImageDrefGather ||
1608 opcode == spv::Op::OpImageSparseDrefGather);
1609 if (spv_result_t result = ValidateImageDref(_, inst, info)) return result;
1610 }
1611
1612 if (spv_result_t result =
1613 ValidateImageOperands(_, inst, info, /* word_index = */ 7))
1614 return result;
1615
1616 return SPV_SUCCESS;
1617 }
1618
ValidateImageRead(ValidationState_t & _,const Instruction * inst)1619 spv_result_t ValidateImageRead(ValidationState_t& _, const Instruction* inst) {
1620 const spv::Op opcode = inst->opcode();
1621 uint32_t actual_result_type = 0;
1622 if (spv_result_t error = GetActualResultType(_, inst, &actual_result_type)) {
1623 return error;
1624 }
1625
1626 if (!_.IsIntScalarOrVectorType(actual_result_type) &&
1627 !_.IsFloatScalarOrVectorType(actual_result_type)) {
1628 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1629 << "Expected " << GetActualResultTypeStr(opcode)
1630 << " to be int or float scalar or vector type";
1631 }
1632
1633 const auto target_env = _.context()->target_env;
1634 // Vulkan requires the result to be a 4-element int or float
1635 // vector.
1636 if (spvIsVulkanEnv(target_env)) {
1637 if (_.GetDimension(actual_result_type) != 4) {
1638 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1639 << _.VkErrorID(4780) << "Expected "
1640 << GetActualResultTypeStr(opcode) << " to have 4 components";
1641 }
1642 } // Check OpenCL below, after we get the image info.
1643
1644 const uint32_t image_type = _.GetOperandTypeId(inst, 2);
1645 if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
1646 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1647 << "Expected Image to be of type OpTypeImage";
1648 }
1649
1650 ImageTypeInfo info;
1651 if (!GetImageTypeInfo(_, image_type, &info)) {
1652 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1653 << "Corrupt image type definition";
1654 }
1655
1656 if (spvIsOpenCLEnv(target_env)) {
1657 // In OpenCL, a read from a depth image returns a scalar float. In other
1658 // cases, the result is always a 4-element vector.
1659 // https://www.khronos.org/registry/OpenCL/specs/3.0-unified/html/OpenCL_Env.html#_data_format_for_reading_and_writing_images
1660 // https://www.khronos.org/registry/OpenCL/specs/3.0-unified/html/OpenCL_C.html#image-read-and-write-functions
1661 // The builtins for reading depth images are:
1662 // float read_imagef(aQual image2d_depth_t image, int2 coord)
1663 // float read_imagef(aQual image2d_array_depth_t image, int4 coord)
1664 if (info.depth) {
1665 if (!_.IsFloatScalarType(actual_result_type)) {
1666 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1667 << "Expected " << GetActualResultTypeStr(opcode)
1668 << " from a depth image read to result in a scalar float value";
1669 }
1670 } else {
1671 if (_.GetDimension(actual_result_type) != 4) {
1672 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1673 << "Expected " << GetActualResultTypeStr(opcode)
1674 << " to have 4 components";
1675 }
1676 }
1677
1678 const uint32_t mask = inst->words().size() <= 5 ? 0 : inst->word(5);
1679 if (mask & uint32_t(spv::ImageOperandsMask::ConstOffset)) {
1680 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1681 << "ConstOffset image operand not allowed "
1682 << "in the OpenCL environment.";
1683 }
1684 }
1685
1686 if (info.dim == spv::Dim::SubpassData) {
1687 if (opcode == spv::Op::OpImageSparseRead) {
1688 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1689 << "Image Dim SubpassData cannot be used with ImageSparseRead";
1690 }
1691
1692 _.function(inst->function()->id())
1693 ->RegisterExecutionModelLimitation(
1694 spv::ExecutionModel::Fragment,
1695 std::string("Dim SubpassData requires Fragment execution model: ") +
1696 spvOpcodeString(opcode));
1697 }
1698
1699 if (info.dim == spv::Dim::TileImageDataEXT) {
1700 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1701 << "Image Dim TileImageDataEXT cannot be used with "
1702 << spvOpcodeString(opcode);
1703 }
1704
1705 if (_.GetIdOpcode(info.sampled_type) != spv::Op::OpTypeVoid) {
1706 const uint32_t result_component_type =
1707 _.GetComponentType(actual_result_type);
1708 if (result_component_type != info.sampled_type) {
1709 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1710 << "Expected Image 'Sampled Type' to be the same as "
1711 << GetActualResultTypeStr(opcode) << " components";
1712 }
1713 }
1714
1715 if (spv_result_t result = ValidateImageReadWrite(_, inst, info))
1716 return result;
1717
1718 const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
1719 if (!_.IsIntScalarOrVectorType(coord_type)) {
1720 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1721 << "Expected Coordinate to be int scalar or vector";
1722 }
1723
1724 const uint32_t min_coord_size = GetMinCoordSize(opcode, info);
1725 const uint32_t actual_coord_size = _.GetDimension(coord_type);
1726 if (min_coord_size > actual_coord_size) {
1727 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1728 << "Expected Coordinate to have at least " << min_coord_size
1729 << " components, but given only " << actual_coord_size;
1730 }
1731
1732 if (spvIsVulkanEnv(_.context()->target_env)) {
1733 if (info.format == spv::ImageFormat::Unknown &&
1734 info.dim != spv::Dim::SubpassData &&
1735 !_.HasCapability(spv::Capability::StorageImageReadWithoutFormat)) {
1736 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1737 << "Capability StorageImageReadWithoutFormat is required to "
1738 << "read storage image";
1739 }
1740 }
1741
1742 if (spv_result_t result =
1743 ValidateImageOperands(_, inst, info, /* word_index = */ 6))
1744 return result;
1745
1746 return SPV_SUCCESS;
1747 }
1748
ValidateImageWrite(ValidationState_t & _,const Instruction * inst)1749 spv_result_t ValidateImageWrite(ValidationState_t& _, const Instruction* inst) {
1750 const uint32_t image_type = _.GetOperandTypeId(inst, 0);
1751 if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
1752 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1753 << "Expected Image to be of type OpTypeImage";
1754 }
1755
1756 ImageTypeInfo info;
1757 if (!GetImageTypeInfo(_, image_type, &info)) {
1758 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1759 << "Corrupt image type definition";
1760 }
1761
1762 if (info.dim == spv::Dim::SubpassData) {
1763 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1764 << "Image 'Dim' cannot be SubpassData";
1765 }
1766
1767 if (info.dim == spv::Dim::TileImageDataEXT) {
1768 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1769 << "Image 'Dim' cannot be TileImageDataEXT";
1770 }
1771
1772 if (spv_result_t result = ValidateImageReadWrite(_, inst, info))
1773 return result;
1774
1775 const uint32_t coord_type = _.GetOperandTypeId(inst, 1);
1776 if (!_.IsIntScalarOrVectorType(coord_type)) {
1777 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1778 << "Expected Coordinate to be int scalar or vector";
1779 }
1780
1781 const uint32_t min_coord_size = GetMinCoordSize(inst->opcode(), info);
1782 const uint32_t actual_coord_size = _.GetDimension(coord_type);
1783 if (min_coord_size > actual_coord_size) {
1784 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1785 << "Expected Coordinate to have at least " << min_coord_size
1786 << " components, but given only " << actual_coord_size;
1787 }
1788
1789 // because it needs to match with 'Sampled Type' the Texel can't be a boolean
1790 const uint32_t texel_type = _.GetOperandTypeId(inst, 2);
1791 if (!_.IsIntScalarOrVectorType(texel_type) &&
1792 !_.IsFloatScalarOrVectorType(texel_type)) {
1793 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1794 << "Expected Texel to be int or float vector or scalar";
1795 }
1796
1797 if (_.GetIdOpcode(info.sampled_type) != spv::Op::OpTypeVoid) {
1798 const uint32_t texel_component_type = _.GetComponentType(texel_type);
1799 if (texel_component_type != info.sampled_type) {
1800 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1801 << "Expected Image 'Sampled Type' to be the same as Texel "
1802 << "components";
1803 }
1804 }
1805
1806 if (spvIsVulkanEnv(_.context()->target_env)) {
1807 if (info.format == spv::ImageFormat::Unknown &&
1808 info.dim != spv::Dim::SubpassData &&
1809 !_.HasCapability(spv::Capability::StorageImageWriteWithoutFormat)) {
1810 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1811 << "Capability StorageImageWriteWithoutFormat is required to "
1812 "write "
1813 << "to storage image";
1814 }
1815 }
1816
1817 if (inst->words().size() > 4) {
1818 if (spvIsOpenCLEnv(_.context()->target_env)) {
1819 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1820 << "Optional Image Operands are not allowed in the OpenCL "
1821 << "environment.";
1822 }
1823 }
1824
1825 if (spv_result_t result =
1826 ValidateImageOperands(_, inst, info, /* word_index = */ 5))
1827 return result;
1828
1829 return SPV_SUCCESS;
1830 }
1831
ValidateImage(ValidationState_t & _,const Instruction * inst)1832 spv_result_t ValidateImage(ValidationState_t& _, const Instruction* inst) {
1833 const uint32_t result_type = inst->type_id();
1834 if (_.GetIdOpcode(result_type) != spv::Op::OpTypeImage) {
1835 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1836 << "Expected Result Type to be OpTypeImage";
1837 }
1838
1839 const uint32_t sampled_image_type = _.GetOperandTypeId(inst, 2);
1840 const Instruction* sampled_image_type_inst = _.FindDef(sampled_image_type);
1841 assert(sampled_image_type_inst);
1842
1843 if (sampled_image_type_inst->opcode() != spv::Op::OpTypeSampledImage) {
1844 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1845 << "Expected Sample Image to be of type OpTypeSampleImage";
1846 }
1847
1848 if (sampled_image_type_inst->word(2) != result_type) {
1849 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1850 << "Expected Sample Image image type to be equal to Result Type";
1851 }
1852
1853 return SPV_SUCCESS;
1854 }
1855
ValidateImageQuerySizeLod(ValidationState_t & _,const Instruction * inst)1856 spv_result_t ValidateImageQuerySizeLod(ValidationState_t& _,
1857 const Instruction* inst) {
1858 const uint32_t result_type = inst->type_id();
1859 if (!_.IsIntScalarOrVectorType(result_type)) {
1860 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1861 << "Expected Result Type to be int scalar or vector type";
1862 }
1863
1864 const uint32_t image_type = _.GetOperandTypeId(inst, 2);
1865 if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
1866 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1867 << "Expected Image to be of type OpTypeImage";
1868 }
1869
1870 ImageTypeInfo info;
1871 if (!GetImageTypeInfo(_, image_type, &info)) {
1872 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1873 << "Corrupt image type definition";
1874 }
1875
1876 uint32_t expected_num_components = info.arrayed;
1877 switch (info.dim) {
1878 case spv::Dim::Dim1D:
1879 expected_num_components += 1;
1880 break;
1881 case spv::Dim::Dim2D:
1882 case spv::Dim::Cube:
1883 expected_num_components += 2;
1884 break;
1885 case spv::Dim::Dim3D:
1886 expected_num_components += 3;
1887 break;
1888 default:
1889 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1890 << "Image 'Dim' must be 1D, 2D, 3D or Cube";
1891 }
1892
1893 if (info.multisampled != 0) {
1894 return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Image 'MS' must be 0";
1895 }
1896
1897 const auto target_env = _.context()->target_env;
1898 if (spvIsVulkanEnv(target_env)) {
1899 if (info.sampled != 1) {
1900 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1901 << _.VkErrorID(4659)
1902 << "OpImageQuerySizeLod must only consume an \"Image\" operand "
1903 "whose type has its \"Sampled\" operand set to 1";
1904 }
1905 }
1906
1907 uint32_t result_num_components = _.GetDimension(result_type);
1908 if (result_num_components != expected_num_components) {
1909 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1910 << "Result Type has " << result_num_components << " components, "
1911 << "but " << expected_num_components << " expected";
1912 }
1913
1914 const uint32_t lod_type = _.GetOperandTypeId(inst, 3);
1915 if (!_.IsIntScalarType(lod_type)) {
1916 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1917 << "Expected Level of Detail to be int scalar";
1918 }
1919 return SPV_SUCCESS;
1920 }
1921
ValidateImageQuerySize(ValidationState_t & _,const Instruction * inst)1922 spv_result_t ValidateImageQuerySize(ValidationState_t& _,
1923 const Instruction* inst) {
1924 const uint32_t result_type = inst->type_id();
1925 if (!_.IsIntScalarOrVectorType(result_type)) {
1926 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1927 << "Expected Result Type to be int scalar or vector type";
1928 }
1929
1930 const uint32_t image_type = _.GetOperandTypeId(inst, 2);
1931 if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
1932 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1933 << "Expected Image to be of type OpTypeImage";
1934 }
1935
1936 ImageTypeInfo info;
1937 if (!GetImageTypeInfo(_, image_type, &info)) {
1938 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1939 << "Corrupt image type definition";
1940 }
1941
1942 uint32_t expected_num_components = info.arrayed;
1943 switch (info.dim) {
1944 case spv::Dim::Dim1D:
1945 case spv::Dim::Buffer:
1946 expected_num_components += 1;
1947 break;
1948 case spv::Dim::Dim2D:
1949 case spv::Dim::Cube:
1950 case spv::Dim::Rect:
1951 expected_num_components += 2;
1952 break;
1953 case spv::Dim::Dim3D:
1954 expected_num_components += 3;
1955 break;
1956 default:
1957 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1958 << "Image 'Dim' must be 1D, Buffer, 2D, Cube, 3D or Rect";
1959 }
1960
1961 if (info.dim == spv::Dim::Dim1D || info.dim == spv::Dim::Dim2D ||
1962 info.dim == spv::Dim::Dim3D || info.dim == spv::Dim::Cube) {
1963 if (info.multisampled != 1 && info.sampled != 0 && info.sampled != 2) {
1964 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1965 << "Image must have either 'MS'=1 or 'Sampled'=0 or 'Sampled'=2";
1966 }
1967 }
1968
1969 uint32_t result_num_components = _.GetDimension(result_type);
1970 if (result_num_components != expected_num_components) {
1971 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1972 << "Result Type has " << result_num_components << " components, "
1973 << "but " << expected_num_components << " expected";
1974 }
1975
1976 return SPV_SUCCESS;
1977 }
1978
ValidateImageQueryFormatOrOrder(ValidationState_t & _,const Instruction * inst)1979 spv_result_t ValidateImageQueryFormatOrOrder(ValidationState_t& _,
1980 const Instruction* inst) {
1981 if (!_.IsIntScalarType(inst->type_id())) {
1982 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1983 << "Expected Result Type to be int scalar type";
1984 }
1985
1986 const uint32_t image_type = _.GetOperandTypeId(inst, 2);
1987 if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
1988 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1989 << "Expected operand to be of type OpTypeImage";
1990 }
1991
1992 ImageTypeInfo info;
1993 if (!GetImageTypeInfo(_, image_type, &info)) {
1994 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1995 << "Corrupt image type definition";
1996 }
1997
1998 if (info.dim == spv::Dim::TileImageDataEXT) {
1999 return _.diag(SPV_ERROR_INVALID_DATA, inst)
2000 << "Image 'Dim' cannot be TileImageDataEXT";
2001 }
2002 return SPV_SUCCESS;
2003 }
2004
ValidateImageQueryLod(ValidationState_t & _,const Instruction * inst)2005 spv_result_t ValidateImageQueryLod(ValidationState_t& _,
2006 const Instruction* inst) {
2007 _.function(inst->function()->id())
2008 ->RegisterExecutionModelLimitation(
2009 [&](spv::ExecutionModel model, std::string* message) {
2010 if (model != spv::ExecutionModel::Fragment &&
2011 model != spv::ExecutionModel::GLCompute) {
2012 if (message) {
2013 *message = std::string(
2014 "OpImageQueryLod requires Fragment or GLCompute execution "
2015 "model");
2016 }
2017 return false;
2018 }
2019 return true;
2020 });
2021 _.function(inst->function()->id())
2022 ->RegisterLimitation([](const ValidationState_t& state,
2023 const Function* entry_point,
2024 std::string* message) {
2025 const auto* models = state.GetExecutionModels(entry_point->id());
2026 const auto* modes = state.GetExecutionModes(entry_point->id());
2027 if (models->find(spv::ExecutionModel::GLCompute) != models->end() &&
2028 modes->find(spv::ExecutionMode::DerivativeGroupLinearNV) ==
2029 modes->end() &&
2030 modes->find(spv::ExecutionMode::DerivativeGroupQuadsNV) ==
2031 modes->end()) {
2032 if (message) {
2033 *message = std::string(
2034 "OpImageQueryLod requires DerivativeGroupQuadsNV "
2035 "or DerivativeGroupLinearNV execution mode for GLCompute "
2036 "execution model");
2037 }
2038 return false;
2039 }
2040 return true;
2041 });
2042
2043 const uint32_t result_type = inst->type_id();
2044 if (!_.IsFloatVectorType(result_type)) {
2045 return _.diag(SPV_ERROR_INVALID_DATA, inst)
2046 << "Expected Result Type to be float vector type";
2047 }
2048
2049 if (_.GetDimension(result_type) != 2) {
2050 return _.diag(SPV_ERROR_INVALID_DATA, inst)
2051 << "Expected Result Type to have 2 components";
2052 }
2053
2054 const uint32_t image_type = _.GetOperandTypeId(inst, 2);
2055 if (_.GetIdOpcode(image_type) != spv::Op::OpTypeSampledImage) {
2056 return _.diag(SPV_ERROR_INVALID_DATA, inst)
2057 << "Expected Image operand to be of type OpTypeSampledImage";
2058 }
2059
2060 ImageTypeInfo info;
2061 if (!GetImageTypeInfo(_, image_type, &info)) {
2062 return _.diag(SPV_ERROR_INVALID_DATA, inst)
2063 << "Corrupt image type definition";
2064 }
2065
2066 if (info.dim != spv::Dim::Dim1D && info.dim != spv::Dim::Dim2D &&
2067 info.dim != spv::Dim::Dim3D && info.dim != spv::Dim::Cube) {
2068 return _.diag(SPV_ERROR_INVALID_DATA, inst)
2069 << "Image 'Dim' must be 1D, 2D, 3D or Cube";
2070 }
2071
2072 const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
2073 if (_.HasCapability(spv::Capability::Kernel)) {
2074 if (!_.IsFloatScalarOrVectorType(coord_type) &&
2075 !_.IsIntScalarOrVectorType(coord_type)) {
2076 return _.diag(SPV_ERROR_INVALID_DATA, inst)
2077 << "Expected Coordinate to be int or float scalar or vector";
2078 }
2079 } else {
2080 if (!_.IsFloatScalarOrVectorType(coord_type)) {
2081 return _.diag(SPV_ERROR_INVALID_DATA, inst)
2082 << "Expected Coordinate to be float scalar or vector";
2083 }
2084 }
2085
2086 const uint32_t min_coord_size = GetPlaneCoordSize(info);
2087 const uint32_t actual_coord_size = _.GetDimension(coord_type);
2088 if (min_coord_size > actual_coord_size) {
2089 return _.diag(SPV_ERROR_INVALID_DATA, inst)
2090 << "Expected Coordinate to have at least " << min_coord_size
2091 << " components, but given only " << actual_coord_size;
2092 }
2093
2094 // The operand is a sampled image.
2095 // The sampled image type is already checked to be parameterized by an image
2096 // type with Sampled=0 or Sampled=1. Vulkan bans Sampled=0, and so we have
2097 // Sampled=1. So the validator already enforces Vulkan VUID 4659:
2098 // OpImageQuerySizeLod must only consume an "Image" operand whose type has
2099 // its "Sampled" operand set to 1
2100 return SPV_SUCCESS;
2101 }
2102
ValidateImageSparseLod(ValidationState_t & _,const Instruction * inst)2103 spv_result_t ValidateImageSparseLod(ValidationState_t& _,
2104 const Instruction* inst) {
2105 return _.diag(SPV_ERROR_INVALID_DATA, inst)
2106 << "Instruction reserved for future use, use of this instruction "
2107 << "is invalid";
2108 }
2109
ValidateImageQueryLevelsOrSamples(ValidationState_t & _,const Instruction * inst)2110 spv_result_t ValidateImageQueryLevelsOrSamples(ValidationState_t& _,
2111 const Instruction* inst) {
2112 if (!_.IsIntScalarType(inst->type_id())) {
2113 return _.diag(SPV_ERROR_INVALID_DATA, inst)
2114 << "Expected Result Type to be int scalar type";
2115 }
2116
2117 const uint32_t image_type = _.GetOperandTypeId(inst, 2);
2118 if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
2119 return _.diag(SPV_ERROR_INVALID_DATA, inst)
2120 << "Expected Image to be of type OpTypeImage";
2121 }
2122
2123 ImageTypeInfo info;
2124 if (!GetImageTypeInfo(_, image_type, &info)) {
2125 return _.diag(SPV_ERROR_INVALID_DATA, inst)
2126 << "Corrupt image type definition";
2127 }
2128
2129 const spv::Op opcode = inst->opcode();
2130 if (opcode == spv::Op::OpImageQueryLevels) {
2131 if (info.dim != spv::Dim::Dim1D && info.dim != spv::Dim::Dim2D &&
2132 info.dim != spv::Dim::Dim3D && info.dim != spv::Dim::Cube) {
2133 return _.diag(SPV_ERROR_INVALID_DATA, inst)
2134 << "Image 'Dim' must be 1D, 2D, 3D or Cube";
2135 }
2136 const auto target_env = _.context()->target_env;
2137 if (spvIsVulkanEnv(target_env)) {
2138 if (info.sampled != 1) {
2139 return _.diag(SPV_ERROR_INVALID_DATA, inst)
2140 << _.VkErrorID(4659)
2141 << "OpImageQueryLevels must only consume an \"Image\" operand "
2142 "whose type has its \"Sampled\" operand set to 1";
2143 }
2144 }
2145 } else {
2146 assert(opcode == spv::Op::OpImageQuerySamples);
2147 if (info.dim != spv::Dim::Dim2D) {
2148 return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Image 'Dim' must be 2D";
2149 }
2150
2151 if (info.multisampled != 1) {
2152 return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Image 'MS' must be 1";
2153 }
2154 }
2155 return SPV_SUCCESS;
2156 }
2157
ValidateImageSparseTexelsResident(ValidationState_t & _,const Instruction * inst)2158 spv_result_t ValidateImageSparseTexelsResident(ValidationState_t& _,
2159 const Instruction* inst) {
2160 if (!_.IsBoolScalarType(inst->type_id())) {
2161 return _.diag(SPV_ERROR_INVALID_DATA, inst)
2162 << "Expected Result Type to be bool scalar type";
2163 }
2164
2165 const uint32_t resident_code_type = _.GetOperandTypeId(inst, 2);
2166 if (!_.IsIntScalarType(resident_code_type)) {
2167 return _.diag(SPV_ERROR_INVALID_DATA, inst)
2168 << "Expected Resident Code to be int scalar";
2169 }
2170
2171 return SPV_SUCCESS;
2172 }
2173
ValidateImageProcessingQCOMDecoration(ValidationState_t & _,int id,spv::Decoration decor)2174 spv_result_t ValidateImageProcessingQCOMDecoration(ValidationState_t& _, int id,
2175 spv::Decoration decor) {
2176 const Instruction* si_inst = nullptr;
2177 const Instruction* ld_inst = _.FindDef(id);
2178 bool is_intf_obj = (ld_inst->opcode() == spv::Op::OpSampledImage);
2179 if (is_intf_obj == true) {
2180 si_inst = ld_inst;
2181 int t_idx = si_inst->GetOperandAs<int>(2); // texture
2182 ld_inst = _.FindDef(t_idx);
2183 }
2184 if (ld_inst->opcode() != spv::Op::OpLoad) {
2185 return _.diag(SPV_ERROR_INVALID_DATA, ld_inst) << "Expect to see OpLoad";
2186 }
2187 int texture_id = ld_inst->GetOperandAs<int>(2); // variable to load
2188 if (!_.HasDecoration(texture_id, decor)) {
2189 return _.diag(SPV_ERROR_INVALID_DATA, ld_inst)
2190 << "Missing decoration " << _.SpvDecorationString(decor);
2191 }
2192
2193 return SPV_SUCCESS;
2194 }
2195
ValidateImageProcessing2QCOMWindowDecoration(ValidationState_t & _,int id)2196 spv_result_t ValidateImageProcessing2QCOMWindowDecoration(ValidationState_t& _,
2197 int id) {
2198 const Instruction* ld_inst = _.FindDef(id);
2199 bool is_intf_obj = (ld_inst->opcode() != spv::Op::OpSampledImage);
2200 if (is_intf_obj == true) {
2201 if (ld_inst->opcode() != spv::Op::OpLoad) {
2202 return _.diag(SPV_ERROR_INVALID_DATA, ld_inst) << "Expect to see OpLoad";
2203 }
2204 int texture_id = ld_inst->GetOperandAs<int>(2); // variable to load
2205 spv::Decoration decor = spv::Decoration::BlockMatchTextureQCOM;
2206 if (!_.HasDecoration(texture_id, decor)) {
2207 return _.diag(SPV_ERROR_INVALID_DATA, ld_inst)
2208 << "Missing decoration " << _.SpvDecorationString(decor);
2209 }
2210 decor = spv::Decoration::BlockMatchSamplerQCOM;
2211 if (!_.HasDecoration(texture_id, decor)) {
2212 return _.diag(SPV_ERROR_INVALID_DATA, ld_inst)
2213 << "Missing decoration " << _.SpvDecorationString(decor);
2214 }
2215 } else {
2216 const Instruction* si_inst = ld_inst;
2217 int t_idx = si_inst->GetOperandAs<int>(2); // texture
2218 const Instruction* t_ld_inst = _.FindDef(t_idx);
2219 if (t_ld_inst->opcode() != spv::Op::OpLoad) {
2220 return _.diag(SPV_ERROR_INVALID_DATA, t_ld_inst)
2221 << "Expect to see OpLoad";
2222 }
2223 int texture_id = t_ld_inst->GetOperandAs<int>(2); // variable to load
2224 spv::Decoration decor = spv::Decoration::BlockMatchTextureQCOM;
2225 if (!_.HasDecoration(texture_id, decor)) {
2226 return _.diag(SPV_ERROR_INVALID_DATA, ld_inst)
2227 << "Missing decoration " << _.SpvDecorationString(decor);
2228 }
2229 int s_idx = si_inst->GetOperandAs<int>(3); // sampler
2230 const Instruction* s_ld_inst = _.FindDef(s_idx);
2231 if (s_ld_inst->opcode() != spv::Op::OpLoad) {
2232 return _.diag(SPV_ERROR_INVALID_DATA, s_ld_inst)
2233 << "Expect to see OpLoad";
2234 }
2235 int sampler_id = s_ld_inst->GetOperandAs<int>(2); // variable to load
2236 decor = spv::Decoration::BlockMatchSamplerQCOM;
2237 if (!_.HasDecoration(sampler_id, decor)) {
2238 return _.diag(SPV_ERROR_INVALID_DATA, ld_inst)
2239 << "Missing decoration " << _.SpvDecorationString(decor);
2240 }
2241 }
2242
2243 return SPV_SUCCESS;
2244 }
2245
ValidateImageProcessingQCOM(ValidationState_t & _,const Instruction * inst)2246 spv_result_t ValidateImageProcessingQCOM(ValidationState_t& _,
2247 const Instruction* inst) {
2248 spv_result_t res = SPV_SUCCESS;
2249 const spv::Op opcode = inst->opcode();
2250 switch (opcode) {
2251 case spv::Op::OpImageSampleWeightedQCOM: {
2252 int wi_idx = inst->GetOperandAs<int>(4); // weight
2253 res = ValidateImageProcessingQCOMDecoration(
2254 _, wi_idx, spv::Decoration::WeightTextureQCOM);
2255 break;
2256 }
2257 case spv::Op::OpImageBlockMatchSSDQCOM:
2258 case spv::Op::OpImageBlockMatchSADQCOM: {
2259 int tgt_idx = inst->GetOperandAs<int>(2); // target
2260 res = ValidateImageProcessingQCOMDecoration(
2261 _, tgt_idx, spv::Decoration::BlockMatchTextureQCOM);
2262 if (res != SPV_SUCCESS) break;
2263 int ref_idx = inst->GetOperandAs<int>(4); // reference
2264 res = ValidateImageProcessingQCOMDecoration(
2265 _, ref_idx, spv::Decoration::BlockMatchTextureQCOM);
2266 break;
2267 }
2268 case spv::Op::OpImageBlockMatchWindowSSDQCOM:
2269 case spv::Op::OpImageBlockMatchWindowSADQCOM: {
2270 int tgt_idx = inst->GetOperandAs<int>(2); // target
2271 res = ValidateImageProcessing2QCOMWindowDecoration(_, tgt_idx);
2272 if (res != SPV_SUCCESS) break;
2273 int ref_idx = inst->GetOperandAs<int>(4); // reference
2274 res = ValidateImageProcessing2QCOMWindowDecoration(_, ref_idx);
2275 break;
2276 }
2277 case spv::Op::OpImageBlockMatchGatherSSDQCOM:
2278 case spv::Op::OpImageBlockMatchGatherSADQCOM: {
2279 int tgt_idx = inst->GetOperandAs<int>(2); // target
2280 res = ValidateImageProcessingQCOMDecoration(
2281 _, tgt_idx, spv::Decoration::BlockMatchTextureQCOM);
2282 if (res != SPV_SUCCESS) break;
2283 int ref_idx = inst->GetOperandAs<int>(4); // reference
2284 res = ValidateImageProcessingQCOMDecoration(
2285 _, ref_idx, spv::Decoration::BlockMatchTextureQCOM);
2286 break;
2287 }
2288 default:
2289 break;
2290 }
2291
2292 return res;
2293 }
2294
2295 } // namespace
2296
2297 // Validates correctness of image instructions.
ImagePass(ValidationState_t & _,const Instruction * inst)2298 spv_result_t ImagePass(ValidationState_t& _, const Instruction* inst) {
2299 const spv::Op opcode = inst->opcode();
2300 if (IsImplicitLod(opcode)) {
2301 _.function(inst->function()->id())
2302 ->RegisterExecutionModelLimitation([opcode](spv::ExecutionModel model,
2303 std::string* message) {
2304 if (model != spv::ExecutionModel::Fragment &&
2305 model != spv::ExecutionModel::GLCompute) {
2306 if (message) {
2307 *message =
2308 std::string(
2309 "ImplicitLod instructions require Fragment or GLCompute "
2310 "execution model: ") +
2311 spvOpcodeString(opcode);
2312 }
2313 return false;
2314 }
2315 return true;
2316 });
2317 _.function(inst->function()->id())
2318 ->RegisterLimitation([opcode](const ValidationState_t& state,
2319 const Function* entry_point,
2320 std::string* message) {
2321 const auto* models = state.GetExecutionModels(entry_point->id());
2322 const auto* modes = state.GetExecutionModes(entry_point->id());
2323 if (models &&
2324 models->find(spv::ExecutionModel::GLCompute) != models->end() &&
2325 (!modes ||
2326 (modes->find(spv::ExecutionMode::DerivativeGroupLinearNV) ==
2327 modes->end() &&
2328 modes->find(spv::ExecutionMode::DerivativeGroupQuadsNV) ==
2329 modes->end()))) {
2330 if (message) {
2331 *message =
2332 std::string(
2333 "ImplicitLod instructions require DerivativeGroupQuadsNV "
2334 "or DerivativeGroupLinearNV execution mode for GLCompute "
2335 "execution model: ") +
2336 spvOpcodeString(opcode);
2337 }
2338 return false;
2339 }
2340 return true;
2341 });
2342 }
2343
2344 switch (opcode) {
2345 case spv::Op::OpTypeImage:
2346 return ValidateTypeImage(_, inst);
2347 case spv::Op::OpTypeSampledImage:
2348 return ValidateTypeSampledImage(_, inst);
2349 case spv::Op::OpSampledImage:
2350 return ValidateSampledImage(_, inst);
2351 case spv::Op::OpImageTexelPointer:
2352 return ValidateImageTexelPointer(_, inst);
2353
2354 case spv::Op::OpImageSampleImplicitLod:
2355 case spv::Op::OpImageSampleExplicitLod:
2356 case spv::Op::OpImageSampleProjImplicitLod:
2357 case spv::Op::OpImageSampleProjExplicitLod:
2358 case spv::Op::OpImageSparseSampleImplicitLod:
2359 case spv::Op::OpImageSparseSampleExplicitLod:
2360 return ValidateImageLod(_, inst);
2361
2362 case spv::Op::OpImageSampleDrefImplicitLod:
2363 case spv::Op::OpImageSampleDrefExplicitLod:
2364 case spv::Op::OpImageSampleProjDrefImplicitLod:
2365 case spv::Op::OpImageSampleProjDrefExplicitLod:
2366 case spv::Op::OpImageSparseSampleDrefImplicitLod:
2367 case spv::Op::OpImageSparseSampleDrefExplicitLod:
2368 return ValidateImageDrefLod(_, inst);
2369
2370 case spv::Op::OpImageFetch:
2371 case spv::Op::OpImageSparseFetch:
2372 return ValidateImageFetch(_, inst);
2373
2374 case spv::Op::OpImageGather:
2375 case spv::Op::OpImageDrefGather:
2376 case spv::Op::OpImageSparseGather:
2377 case spv::Op::OpImageSparseDrefGather:
2378 return ValidateImageGather(_, inst);
2379
2380 case spv::Op::OpImageRead:
2381 case spv::Op::OpImageSparseRead:
2382 return ValidateImageRead(_, inst);
2383
2384 case spv::Op::OpImageWrite:
2385 return ValidateImageWrite(_, inst);
2386
2387 case spv::Op::OpImage:
2388 return ValidateImage(_, inst);
2389
2390 case spv::Op::OpImageQueryFormat:
2391 case spv::Op::OpImageQueryOrder:
2392 return ValidateImageQueryFormatOrOrder(_, inst);
2393
2394 case spv::Op::OpImageQuerySizeLod:
2395 return ValidateImageQuerySizeLod(_, inst);
2396 case spv::Op::OpImageQuerySize:
2397 return ValidateImageQuerySize(_, inst);
2398 case spv::Op::OpImageQueryLod:
2399 return ValidateImageQueryLod(_, inst);
2400
2401 case spv::Op::OpImageQueryLevels:
2402 case spv::Op::OpImageQuerySamples:
2403 return ValidateImageQueryLevelsOrSamples(_, inst);
2404
2405 case spv::Op::OpImageSparseSampleProjImplicitLod:
2406 case spv::Op::OpImageSparseSampleProjExplicitLod:
2407 case spv::Op::OpImageSparseSampleProjDrefImplicitLod:
2408 case spv::Op::OpImageSparseSampleProjDrefExplicitLod:
2409 return ValidateImageSparseLod(_, inst);
2410
2411 case spv::Op::OpImageSparseTexelsResident:
2412 return ValidateImageSparseTexelsResident(_, inst);
2413
2414 case spv::Op::OpImageSampleWeightedQCOM:
2415 case spv::Op::OpImageBoxFilterQCOM:
2416 case spv::Op::OpImageBlockMatchSSDQCOM:
2417 case spv::Op::OpImageBlockMatchSADQCOM:
2418 case spv::Op::OpImageBlockMatchWindowSADQCOM:
2419 case spv::Op::OpImageBlockMatchWindowSSDQCOM:
2420 case spv::Op::OpImageBlockMatchGatherSADQCOM:
2421 case spv::Op::OpImageBlockMatchGatherSSDQCOM:
2422 return ValidateImageProcessingQCOM(_, inst);
2423
2424 default:
2425 break;
2426 }
2427
2428 return SPV_SUCCESS;
2429 }
2430
IsImageInstruction(const spv::Op opcode)2431 bool IsImageInstruction(const spv::Op opcode) {
2432 switch (opcode) {
2433 case spv::Op::OpImageSampleImplicitLod:
2434 case spv::Op::OpImageSampleDrefImplicitLod:
2435 case spv::Op::OpImageSampleProjImplicitLod:
2436 case spv::Op::OpImageSampleProjDrefImplicitLod:
2437 case spv::Op::OpImageSparseSampleImplicitLod:
2438 case spv::Op::OpImageSparseSampleDrefImplicitLod:
2439 case spv::Op::OpImageSparseSampleProjImplicitLod:
2440 case spv::Op::OpImageSparseSampleProjDrefImplicitLod:
2441
2442 case spv::Op::OpImageSampleExplicitLod:
2443 case spv::Op::OpImageSampleDrefExplicitLod:
2444 case spv::Op::OpImageSampleProjExplicitLod:
2445 case spv::Op::OpImageSampleProjDrefExplicitLod:
2446 case spv::Op::OpImageSparseSampleExplicitLod:
2447 case spv::Op::OpImageSparseSampleDrefExplicitLod:
2448 case spv::Op::OpImageSparseSampleProjExplicitLod:
2449 case spv::Op::OpImageSparseSampleProjDrefExplicitLod:
2450
2451 case spv::Op::OpImage:
2452 case spv::Op::OpImageFetch:
2453 case spv::Op::OpImageSparseFetch:
2454 case spv::Op::OpImageGather:
2455 case spv::Op::OpImageDrefGather:
2456 case spv::Op::OpImageSparseGather:
2457 case spv::Op::OpImageSparseDrefGather:
2458 case spv::Op::OpImageRead:
2459 case spv::Op::OpImageSparseRead:
2460 case spv::Op::OpImageWrite:
2461
2462 case spv::Op::OpImageQueryFormat:
2463 case spv::Op::OpImageQueryOrder:
2464 case spv::Op::OpImageQuerySizeLod:
2465 case spv::Op::OpImageQuerySize:
2466 case spv::Op::OpImageQueryLod:
2467 case spv::Op::OpImageQueryLevels:
2468 case spv::Op::OpImageQuerySamples:
2469
2470 case spv::Op::OpImageSampleWeightedQCOM:
2471 case spv::Op::OpImageBoxFilterQCOM:
2472 case spv::Op::OpImageBlockMatchSSDQCOM:
2473 case spv::Op::OpImageBlockMatchSADQCOM:
2474 case spv::Op::OpImageBlockMatchWindowSADQCOM:
2475 case spv::Op::OpImageBlockMatchWindowSSDQCOM:
2476 case spv::Op::OpImageBlockMatchGatherSADQCOM:
2477 case spv::Op::OpImageBlockMatchGatherSSDQCOM:
2478 return true;
2479 default:
2480 break;
2481 }
2482 return false;
2483 }
2484
ValidateQCOMImageProcessingTextureUsages(ValidationState_t & _,const Instruction * inst)2485 spv_result_t ValidateQCOMImageProcessingTextureUsages(ValidationState_t& _,
2486 const Instruction* inst) {
2487 const spv::Op opcode = inst->opcode();
2488 if (!IsImageInstruction(opcode)) return SPV_SUCCESS;
2489
2490 switch (opcode) {
2491 case spv::Op::OpImageSampleWeightedQCOM:
2492 case spv::Op::OpImageBoxFilterQCOM:
2493 case spv::Op::OpImageBlockMatchSSDQCOM:
2494 case spv::Op::OpImageBlockMatchSADQCOM:
2495 break;
2496 case spv::Op::OpImageBlockMatchWindowSADQCOM:
2497 case spv::Op::OpImageBlockMatchWindowSSDQCOM:
2498 case spv::Op::OpImageBlockMatchGatherSADQCOM:
2499 case spv::Op::OpImageBlockMatchGatherSSDQCOM:
2500 break;
2501 default:
2502 for (size_t i = 0; i < inst->operands().size(); ++i) {
2503 int id = inst->GetOperandAs<int>(i);
2504 const Instruction* operand_inst = _.FindDef(id);
2505 if (operand_inst == nullptr) continue;
2506 if (operand_inst->opcode() == spv::Op::OpLoad) {
2507 if (_.IsQCOMImageProcessingTextureConsumer(id)) {
2508 return _.diag(SPV_ERROR_INVALID_DATA, inst)
2509 << "Illegal use of QCOM image processing decorated texture";
2510 }
2511 }
2512 if (operand_inst->opcode() == spv::Op::OpSampledImage) {
2513 if (_.IsQCOMImageProcessingTextureConsumer(id)) {
2514 return _.diag(SPV_ERROR_INVALID_DATA, inst)
2515 << "Illegal use of QCOM image processing decorated texture";
2516 }
2517 }
2518 }
2519 break;
2520 }
2521 return SPV_SUCCESS;
2522 }
2523
2524 } // namespace val
2525 } // namespace spvtools
2526