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