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/diagnostic.h"
22 #include "source/opcode.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 SpvImageOperandsXXX cases are handled in
35 // this module. If SpvImageOperandsXXX list changes, this function will fail the
36 // build.
37 // For all other purposes this is a placeholder function.
CheckAllImageOperandsHandled()38 bool CheckAllImageOperandsHandled() {
39 SpvImageOperandsMask enum_val = SpvImageOperandsBiasMask;
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 = SpvImageOperandsLodMask;
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 SpvImageOperandsMaskNone:
51 return false;
52 case SpvImageOperandsBiasMask:
53 case SpvImageOperandsLodMask:
54 case SpvImageOperandsGradMask:
55 case SpvImageOperandsConstOffsetMask:
56 case SpvImageOperandsOffsetMask:
57 case SpvImageOperandsConstOffsetsMask:
58 case SpvImageOperandsSampleMask:
59 case SpvImageOperandsMinLodMask:
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 SpvImageOperandsMakeTexelAvailableKHRMask:
64 case SpvImageOperandsMakeTexelVisibleKHRMask:
65 case SpvImageOperandsNonPrivateTexelKHRMask:
66 case SpvImageOperandsVolatileTexelKHRMask:
67 case SpvImageOperandsSignExtendMask:
68 case SpvImageOperandsZeroExtendMask:
69 return true;
70 }
71 return false;
72 }
73
74 // Used by GetImageTypeInfo. See OpTypeImage spec for more information.
75 struct ImageTypeInfo {
76 uint32_t sampled_type = 0;
77 SpvDim dim = SpvDimMax;
78 uint32_t depth = 0;
79 uint32_t arrayed = 0;
80 uint32_t multisampled = 0;
81 uint32_t sampled = 0;
82 SpvImageFormat format = SpvImageFormatMax;
83 SpvAccessQualifier access_qualifier = SpvAccessQualifierMax;
84 };
85
86 // Provides information on image type. |id| should be object of either
87 // OpTypeImage or OpTypeSampledImage type. Returns false in case of failure
88 // (not a valid id, failed to parse the instruction, etc).
GetImageTypeInfo(const ValidationState_t & _,uint32_t id,ImageTypeInfo * info)89 bool GetImageTypeInfo(const ValidationState_t& _, uint32_t id,
90 ImageTypeInfo* info) {
91 if (!id || !info) return false;
92
93 const Instruction* inst = _.FindDef(id);
94 assert(inst);
95
96 if (inst->opcode() == SpvOpTypeSampledImage) {
97 inst = _.FindDef(inst->word(2));
98 assert(inst);
99 }
100
101 if (inst->opcode() != SpvOpTypeImage) return false;
102
103 const size_t num_words = inst->words().size();
104 if (num_words != 9 && num_words != 10) return false;
105
106 info->sampled_type = inst->word(2);
107 info->dim = static_cast<SpvDim>(inst->word(3));
108 info->depth = inst->word(4);
109 info->arrayed = inst->word(5);
110 info->multisampled = inst->word(6);
111 info->sampled = inst->word(7);
112 info->format = static_cast<SpvImageFormat>(inst->word(8));
113 info->access_qualifier = num_words < 10
114 ? SpvAccessQualifierMax
115 : static_cast<SpvAccessQualifier>(inst->word(9));
116 return true;
117 }
118
IsImplicitLod(SpvOp opcode)119 bool IsImplicitLod(SpvOp opcode) {
120 switch (opcode) {
121 case SpvOpImageSampleImplicitLod:
122 case SpvOpImageSampleDrefImplicitLod:
123 case SpvOpImageSampleProjImplicitLod:
124 case SpvOpImageSampleProjDrefImplicitLod:
125 case SpvOpImageSparseSampleImplicitLod:
126 case SpvOpImageSparseSampleDrefImplicitLod:
127 case SpvOpImageSparseSampleProjImplicitLod:
128 case SpvOpImageSparseSampleProjDrefImplicitLod:
129 return true;
130 default:
131 break;
132 }
133 return false;
134 }
135
IsExplicitLod(SpvOp opcode)136 bool IsExplicitLod(SpvOp opcode) {
137 switch (opcode) {
138 case SpvOpImageSampleExplicitLod:
139 case SpvOpImageSampleDrefExplicitLod:
140 case SpvOpImageSampleProjExplicitLod:
141 case SpvOpImageSampleProjDrefExplicitLod:
142 case SpvOpImageSparseSampleExplicitLod:
143 case SpvOpImageSparseSampleDrefExplicitLod:
144 case SpvOpImageSparseSampleProjExplicitLod:
145 case SpvOpImageSparseSampleProjDrefExplicitLod:
146 return true;
147 default:
148 break;
149 }
150 return false;
151 }
152
IsValidLodOperand(const ValidationState_t & _,SpvOp opcode)153 bool IsValidLodOperand(const ValidationState_t& _, SpvOp opcode) {
154 switch (opcode) {
155 case SpvOpImageRead:
156 case SpvOpImageWrite:
157 case SpvOpImageSparseRead:
158 return _.HasCapability(SpvCapabilityImageReadWriteLodAMD);
159 default:
160 return IsExplicitLod(opcode);
161 }
162 }
163
IsValidGatherLodBiasAMD(const ValidationState_t & _,SpvOp opcode)164 bool IsValidGatherLodBiasAMD(const ValidationState_t& _, SpvOp opcode) {
165 switch (opcode) {
166 case SpvOpImageGather:
167 case SpvOpImageSparseGather:
168 return _.HasCapability(SpvCapabilityImageGatherBiasLodAMD);
169 default:
170 break;
171 }
172 return false;
173 }
174
175 // Returns true if the opcode is a Image instruction which applies
176 // homogenous projection to the coordinates.
IsProj(SpvOp opcode)177 bool IsProj(SpvOp opcode) {
178 switch (opcode) {
179 case SpvOpImageSampleProjImplicitLod:
180 case SpvOpImageSampleProjDrefImplicitLod:
181 case SpvOpImageSparseSampleProjImplicitLod:
182 case SpvOpImageSparseSampleProjDrefImplicitLod:
183 case SpvOpImageSampleProjExplicitLod:
184 case SpvOpImageSampleProjDrefExplicitLod:
185 case SpvOpImageSparseSampleProjExplicitLod:
186 case SpvOpImageSparseSampleProjDrefExplicitLod:
187 return true;
188 default:
189 break;
190 }
191 return false;
192 }
193
194 // Returns the number of components in a coordinate used to access a texel in
195 // a single plane of an image with the given parameters.
GetPlaneCoordSize(const ImageTypeInfo & info)196 uint32_t GetPlaneCoordSize(const ImageTypeInfo& info) {
197 uint32_t plane_size = 0;
198 // If this switch breaks your build, please add new values below.
199 switch (info.dim) {
200 case SpvDim1D:
201 case SpvDimBuffer:
202 plane_size = 1;
203 break;
204 case SpvDim2D:
205 case SpvDimRect:
206 case SpvDimSubpassData:
207 plane_size = 2;
208 break;
209 case SpvDim3D:
210 case SpvDimCube:
211 // For Cube direction vector is used instead of UV.
212 plane_size = 3;
213 break;
214 case SpvDimMax:
215 assert(0);
216 break;
217 }
218
219 return plane_size;
220 }
221
222 // Returns minimal number of coordinates based on image dim, arrayed and whether
223 // the instruction uses projection coordinates.
GetMinCoordSize(SpvOp opcode,const ImageTypeInfo & info)224 uint32_t GetMinCoordSize(SpvOp opcode, const ImageTypeInfo& info) {
225 if (info.dim == SpvDimCube &&
226 (opcode == SpvOpImageRead || opcode == SpvOpImageWrite ||
227 opcode == SpvOpImageSparseRead)) {
228 // These opcodes use UV for Cube, not direction vector.
229 return 3;
230 }
231
232 return GetPlaneCoordSize(info) + info.arrayed + (IsProj(opcode) ? 1 : 0);
233 }
234
235 // Checks ImageOperand bitfield and respective operands.
236 // 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)237 spv_result_t ValidateImageOperands(ValidationState_t& _,
238 const Instruction* inst,
239 const ImageTypeInfo& info,
240 uint32_t word_index) {
241 static const bool kAllImageOperandsHandled = CheckAllImageOperandsHandled();
242 (void)kAllImageOperandsHandled;
243
244 const SpvOp opcode = inst->opcode();
245 const size_t num_words = inst->words().size();
246
247 const bool have_explicit_mask = (word_index - 1 < num_words);
248 const uint32_t mask = have_explicit_mask ? inst->word(word_index - 1) : 0u;
249
250 if (have_explicit_mask) {
251 // NonPrivate, Volatile, SignExtend, ZeroExtend take no operand words.
252 const uint32_t mask_bits_having_operands =
253 mask & ~uint32_t(SpvImageOperandsNonPrivateTexelKHRMask |
254 SpvImageOperandsVolatileTexelKHRMask |
255 SpvImageOperandsSignExtendMask |
256 SpvImageOperandsZeroExtendMask);
257 size_t expected_num_image_operand_words =
258 spvtools::utils::CountSetBits(mask_bits_having_operands);
259 if (mask & SpvImageOperandsGradMask) {
260 // Grad uses two words.
261 ++expected_num_image_operand_words;
262 }
263
264 if (expected_num_image_operand_words != num_words - word_index) {
265 return _.diag(SPV_ERROR_INVALID_DATA, inst)
266 << "Number of image operand ids doesn't correspond to the bit "
267 "mask";
268 }
269 } else if (num_words != word_index - 1) {
270 return _.diag(SPV_ERROR_INVALID_DATA, inst)
271 << "Number of image operand ids doesn't correspond to the bit mask";
272 }
273
274 if (info.multisampled & (0 == (mask & SpvImageOperandsSampleMask))) {
275 return _.diag(SPV_ERROR_INVALID_DATA, inst)
276 << "Image Operand Sample is required for operation on "
277 "multi-sampled image";
278 }
279
280 // After this point, only set bits in the image operands mask can cause
281 // the module to be invalid.
282 if (mask == 0) return SPV_SUCCESS;
283
284 if (spvtools::utils::CountSetBits(
285 mask & (SpvImageOperandsOffsetMask | SpvImageOperandsConstOffsetMask |
286 SpvImageOperandsConstOffsetsMask)) > 1) {
287 return _.diag(SPV_ERROR_INVALID_DATA, inst)
288 << _.VkErrorID(4662)
289 << "Image Operands Offset, ConstOffset, ConstOffsets cannot be used "
290 << "together";
291 }
292
293 const bool is_implicit_lod = IsImplicitLod(opcode);
294 const bool is_explicit_lod = IsExplicitLod(opcode);
295 const bool is_valid_lod_operand = IsValidLodOperand(_, opcode);
296 const bool is_valid_gather_lod_bias_amd = IsValidGatherLodBiasAMD(_, opcode);
297
298 // The checks should be done in the order of definition of OperandImage.
299
300 if (mask & SpvImageOperandsBiasMask) {
301 if (!is_implicit_lod && !is_valid_gather_lod_bias_amd) {
302 return _.diag(SPV_ERROR_INVALID_DATA, inst)
303 << "Image Operand Bias can only be used with ImplicitLod opcodes";
304 }
305
306 const uint32_t type_id = _.GetTypeId(inst->word(word_index++));
307 if (!_.IsFloatScalarType(type_id)) {
308 return _.diag(SPV_ERROR_INVALID_DATA, inst)
309 << "Expected Image Operand Bias to be float scalar";
310 }
311
312 if (info.dim != SpvDim1D && info.dim != SpvDim2D && info.dim != SpvDim3D &&
313 info.dim != SpvDimCube) {
314 return _.diag(SPV_ERROR_INVALID_DATA, inst)
315 << "Image Operand Bias requires 'Dim' parameter to be 1D, 2D, 3D "
316 "or Cube";
317 }
318
319 // Multisampled is already checked.
320 }
321
322 if (mask & SpvImageOperandsLodMask) {
323 if (!is_valid_lod_operand && opcode != SpvOpImageFetch &&
324 opcode != SpvOpImageSparseFetch && !is_valid_gather_lod_bias_amd) {
325 return _.diag(SPV_ERROR_INVALID_DATA, inst)
326 << "Image Operand Lod can only be used with ExplicitLod opcodes "
327 << "and OpImageFetch";
328 }
329
330 if (mask & SpvImageOperandsGradMask) {
331 return _.diag(SPV_ERROR_INVALID_DATA, inst)
332 << "Image Operand bits Lod and Grad cannot be set at the same "
333 "time";
334 }
335
336 const uint32_t type_id = _.GetTypeId(inst->word(word_index++));
337 if (is_explicit_lod || is_valid_gather_lod_bias_amd) {
338 if (!_.IsFloatScalarType(type_id)) {
339 return _.diag(SPV_ERROR_INVALID_DATA, inst)
340 << "Expected Image Operand Lod to be float scalar when used "
341 << "with ExplicitLod";
342 }
343 } else {
344 if (!_.IsIntScalarType(type_id)) {
345 return _.diag(SPV_ERROR_INVALID_DATA, inst)
346 << "Expected Image Operand Lod to be int scalar when used with "
347 << "OpImageFetch";
348 }
349 }
350
351 if (info.dim != SpvDim1D && info.dim != SpvDim2D && info.dim != SpvDim3D &&
352 info.dim != SpvDimCube) {
353 return _.diag(SPV_ERROR_INVALID_DATA, inst)
354 << "Image Operand Lod requires 'Dim' parameter to be 1D, 2D, 3D "
355 "or Cube";
356 }
357
358 // Multisampled is already checked.
359 }
360
361 if (mask & SpvImageOperandsGradMask) {
362 if (!is_explicit_lod) {
363 return _.diag(SPV_ERROR_INVALID_DATA, inst)
364 << "Image Operand Grad can only be used with ExplicitLod opcodes";
365 }
366
367 const uint32_t dx_type_id = _.GetTypeId(inst->word(word_index++));
368 const uint32_t dy_type_id = _.GetTypeId(inst->word(word_index++));
369 if (!_.IsFloatScalarOrVectorType(dx_type_id) ||
370 !_.IsFloatScalarOrVectorType(dy_type_id)) {
371 return _.diag(SPV_ERROR_INVALID_DATA, inst)
372 << "Expected both Image Operand Grad ids to be float scalars or "
373 << "vectors";
374 }
375
376 const uint32_t plane_size = GetPlaneCoordSize(info);
377 const uint32_t dx_size = _.GetDimension(dx_type_id);
378 const uint32_t dy_size = _.GetDimension(dy_type_id);
379 if (plane_size != dx_size) {
380 return _.diag(SPV_ERROR_INVALID_DATA, inst)
381 << "Expected Image Operand Grad dx to have " << plane_size
382 << " components, but given " << dx_size;
383 }
384
385 if (plane_size != dy_size) {
386 return _.diag(SPV_ERROR_INVALID_DATA, inst)
387 << "Expected Image Operand Grad dy to have " << plane_size
388 << " components, but given " << dy_size;
389 }
390
391 // Multisampled is already checked.
392 }
393
394 if (mask & SpvImageOperandsConstOffsetMask) {
395 if (info.dim == SpvDimCube) {
396 return _.diag(SPV_ERROR_INVALID_DATA, inst)
397 << "Image Operand ConstOffset cannot be used with Cube Image "
398 "'Dim'";
399 }
400
401 const uint32_t id = inst->word(word_index++);
402 const uint32_t type_id = _.GetTypeId(id);
403 if (!_.IsIntScalarOrVectorType(type_id)) {
404 return _.diag(SPV_ERROR_INVALID_DATA, inst)
405 << "Expected Image Operand ConstOffset to be int scalar or "
406 << "vector";
407 }
408
409 if (!spvOpcodeIsConstant(_.GetIdOpcode(id))) {
410 return _.diag(SPV_ERROR_INVALID_DATA, inst)
411 << "Expected Image Operand ConstOffset to be a const object";
412 }
413
414 const uint32_t plane_size = GetPlaneCoordSize(info);
415 const uint32_t offset_size = _.GetDimension(type_id);
416 if (plane_size != offset_size) {
417 return _.diag(SPV_ERROR_INVALID_DATA, inst)
418 << "Expected Image Operand ConstOffset to have " << plane_size
419 << " components, but given " << offset_size;
420 }
421 }
422
423 if (mask & SpvImageOperandsOffsetMask) {
424 if (info.dim == SpvDimCube) {
425 return _.diag(SPV_ERROR_INVALID_DATA, inst)
426 << "Image Operand Offset cannot be used with Cube Image 'Dim'";
427 }
428
429 const uint32_t id = inst->word(word_index++);
430 const uint32_t type_id = _.GetTypeId(id);
431 if (!_.IsIntScalarOrVectorType(type_id)) {
432 return _.diag(SPV_ERROR_INVALID_DATA, inst)
433 << "Expected Image Operand Offset to be int scalar or "
434 << "vector";
435 }
436
437 const uint32_t plane_size = GetPlaneCoordSize(info);
438 const uint32_t offset_size = _.GetDimension(type_id);
439 if (plane_size != offset_size) {
440 return _.diag(SPV_ERROR_INVALID_DATA, inst)
441 << "Expected Image Operand Offset to have " << plane_size
442 << " components, but given " << offset_size;
443 }
444
445 if (spvIsVulkanEnv(_.context()->target_env)) {
446 if (opcode != SpvOpImageGather && opcode != SpvOpImageDrefGather &&
447 opcode != SpvOpImageSparseGather &&
448 opcode != SpvOpImageSparseDrefGather) {
449 return _.diag(SPV_ERROR_INVALID_DATA, inst)
450 << _.VkErrorID(4663)
451 << "Image Operand Offset can only be used with "
452 "OpImage*Gather operations";
453 }
454 }
455 }
456
457 if (mask & SpvImageOperandsConstOffsetsMask) {
458 if (opcode != SpvOpImageGather && opcode != SpvOpImageDrefGather &&
459 opcode != SpvOpImageSparseGather &&
460 opcode != SpvOpImageSparseDrefGather) {
461 return _.diag(SPV_ERROR_INVALID_DATA, inst)
462 << "Image Operand ConstOffsets can only be used with "
463 "OpImageGather and OpImageDrefGather";
464 }
465
466 if (info.dim == SpvDimCube) {
467 return _.diag(SPV_ERROR_INVALID_DATA, inst)
468 << "Image Operand ConstOffsets cannot be used with Cube Image "
469 "'Dim'";
470 }
471
472 const uint32_t id = inst->word(word_index++);
473 const uint32_t type_id = _.GetTypeId(id);
474 const Instruction* type_inst = _.FindDef(type_id);
475 assert(type_inst);
476
477 if (type_inst->opcode() != SpvOpTypeArray) {
478 return _.diag(SPV_ERROR_INVALID_DATA, inst)
479 << "Expected Image Operand ConstOffsets to be an array of size 4";
480 }
481
482 uint64_t array_size = 0;
483 if (!_.GetConstantValUint64(type_inst->word(3), &array_size)) {
484 assert(0 && "Array type definition is corrupt");
485 }
486
487 if (array_size != 4) {
488 return _.diag(SPV_ERROR_INVALID_DATA, inst)
489 << "Expected Image Operand ConstOffsets to be an array of size 4";
490 }
491
492 const uint32_t component_type = type_inst->word(2);
493 if (!_.IsIntVectorType(component_type) ||
494 _.GetDimension(component_type) != 2) {
495 return _.diag(SPV_ERROR_INVALID_DATA, inst)
496 << "Expected Image Operand ConstOffsets array componenets to be "
497 "int vectors of size 2";
498 }
499
500 if (!spvOpcodeIsConstant(_.GetIdOpcode(id))) {
501 return _.diag(SPV_ERROR_INVALID_DATA, inst)
502 << "Expected Image Operand ConstOffsets to be a const object";
503 }
504 }
505
506 if (mask & SpvImageOperandsSampleMask) {
507 if (opcode != SpvOpImageFetch && opcode != SpvOpImageRead &&
508 opcode != SpvOpImageWrite && opcode != SpvOpImageSparseFetch &&
509 opcode != SpvOpImageSparseRead) {
510 return _.diag(SPV_ERROR_INVALID_DATA, inst)
511 << "Image Operand Sample can only be used with OpImageFetch, "
512 << "OpImageRead, OpImageWrite, OpImageSparseFetch and "
513 << "OpImageSparseRead";
514 }
515
516 if (info.multisampled == 0) {
517 return _.diag(SPV_ERROR_INVALID_DATA, inst)
518 << "Image Operand Sample requires non-zero 'MS' parameter";
519 }
520
521 const uint32_t type_id = _.GetTypeId(inst->word(word_index++));
522 if (!_.IsIntScalarType(type_id)) {
523 return _.diag(SPV_ERROR_INVALID_DATA, inst)
524 << "Expected Image Operand Sample to be int scalar";
525 }
526 }
527
528 if (mask & SpvImageOperandsMinLodMask) {
529 if (!is_implicit_lod && !(mask & SpvImageOperandsGradMask)) {
530 return _.diag(SPV_ERROR_INVALID_DATA, inst)
531 << "Image Operand MinLod can only be used with ImplicitLod "
532 << "opcodes or together with Image Operand Grad";
533 }
534
535 const uint32_t type_id = _.GetTypeId(inst->word(word_index++));
536 if (!_.IsFloatScalarType(type_id)) {
537 return _.diag(SPV_ERROR_INVALID_DATA, inst)
538 << "Expected Image Operand MinLod to be float scalar";
539 }
540
541 if (info.dim != SpvDim1D && info.dim != SpvDim2D && info.dim != SpvDim3D &&
542 info.dim != SpvDimCube) {
543 return _.diag(SPV_ERROR_INVALID_DATA, inst)
544 << "Image Operand MinLod requires 'Dim' parameter to be 1D, 2D, "
545 "3D or Cube";
546 }
547
548 if (info.multisampled != 0) {
549 return _.diag(SPV_ERROR_INVALID_DATA, inst)
550 << "Image Operand MinLod requires 'MS' parameter to be 0";
551 }
552 }
553
554 if (mask & SpvImageOperandsMakeTexelAvailableKHRMask) {
555 // Checked elsewhere: capability and memory model are correct.
556 if (opcode != SpvOpImageWrite) {
557 return _.diag(SPV_ERROR_INVALID_DATA, inst)
558 << "Image Operand MakeTexelAvailableKHR can only be used with Op"
559 << spvOpcodeString(SpvOpImageWrite) << ": Op"
560 << spvOpcodeString(opcode);
561 }
562
563 if (!(mask & SpvImageOperandsNonPrivateTexelKHRMask)) {
564 return _.diag(SPV_ERROR_INVALID_DATA, inst)
565 << "Image Operand MakeTexelAvailableKHR requires "
566 "NonPrivateTexelKHR is also specified: Op"
567 << spvOpcodeString(opcode);
568 }
569
570 const auto available_scope = inst->word(word_index++);
571 if (auto error = ValidateMemoryScope(_, inst, available_scope))
572 return error;
573 }
574
575 if (mask & SpvImageOperandsMakeTexelVisibleKHRMask) {
576 // Checked elsewhere: capability and memory model are correct.
577 if (opcode != SpvOpImageRead && opcode != SpvOpImageSparseRead) {
578 return _.diag(SPV_ERROR_INVALID_DATA, inst)
579 << "Image Operand MakeTexelVisibleKHR can only be used with Op"
580 << spvOpcodeString(SpvOpImageRead) << " or Op"
581 << spvOpcodeString(SpvOpImageSparseRead) << ": Op"
582 << spvOpcodeString(opcode);
583 }
584
585 if (!(mask & SpvImageOperandsNonPrivateTexelKHRMask)) {
586 return _.diag(SPV_ERROR_INVALID_DATA, inst)
587 << "Image Operand MakeTexelVisibleKHR requires NonPrivateTexelKHR "
588 "is also specified: Op"
589 << spvOpcodeString(opcode);
590 }
591
592 const auto visible_scope = inst->word(word_index++);
593 if (auto error = ValidateMemoryScope(_, inst, visible_scope)) return error;
594 }
595
596 if (mask & SpvImageOperandsSignExtendMask) {
597 // Checked elsewhere: SPIR-V 1.4 version or later.
598
599 // "The texel value is converted to the target value via sign extension.
600 // Only valid when the texel type is a scalar or vector of integer type."
601 //
602 // We don't have enough information to know what the texel type is.
603 // In OpenCL, knowledge is deferred until runtime: the image SampledType is
604 // void, and the Format is Unknown.
605 // In Vulkan, the texel type is only known in all cases by the pipeline
606 // setup.
607 }
608
609 if (mask & SpvImageOperandsZeroExtendMask) {
610 // Checked elsewhere: SPIR-V 1.4 version or later.
611
612 // "The texel value is converted to the target value via zero extension.
613 // Only valid when the texel type is a scalar or vector of integer type."
614 //
615 // We don't have enough information to know what the texel type is.
616 // In OpenCL, knowledge is deferred until runtime: the image SampledType is
617 // void, and the Format is Unknown.
618 // In Vulkan, the texel type is only known in all cases by the pipeline
619 // setup.
620 }
621
622 return SPV_SUCCESS;
623 }
624
625 // Checks some of the validation rules which are common to multiple opcodes.
ValidateImageCommon(ValidationState_t & _,const Instruction * inst,const ImageTypeInfo & info)626 spv_result_t ValidateImageCommon(ValidationState_t& _, const Instruction* inst,
627 const ImageTypeInfo& info) {
628 const SpvOp opcode = inst->opcode();
629 if (IsProj(opcode)) {
630 if (info.dim != SpvDim1D && info.dim != SpvDim2D && info.dim != SpvDim3D &&
631 info.dim != SpvDimRect) {
632 return _.diag(SPV_ERROR_INVALID_DATA, inst)
633 << "Expected Image 'Dim' parameter to be 1D, 2D, 3D or Rect";
634 }
635
636 if (info.multisampled != 0) {
637 return _.diag(SPV_ERROR_INVALID_DATA, inst)
638 << "Expected Image 'MS' parameter to be 0";
639 }
640
641 if (info.arrayed != 0) {
642 return _.diag(SPV_ERROR_INVALID_DATA, inst)
643 << "Expected Image 'arrayed' parameter to be 0";
644 }
645 }
646
647 if (opcode == SpvOpImageRead || opcode == SpvOpImageSparseRead ||
648 opcode == SpvOpImageWrite) {
649 if (info.sampled == 0) {
650 } else if (info.sampled == 2) {
651 if (info.dim == SpvDim1D && !_.HasCapability(SpvCapabilityImage1D)) {
652 return _.diag(SPV_ERROR_INVALID_DATA, inst)
653 << "Capability Image1D is required to access storage image";
654 } else if (info.dim == SpvDimRect &&
655 !_.HasCapability(SpvCapabilityImageRect)) {
656 return _.diag(SPV_ERROR_INVALID_DATA, inst)
657 << "Capability ImageRect is required to access storage image";
658 } else if (info.dim == SpvDimBuffer &&
659 !_.HasCapability(SpvCapabilityImageBuffer)) {
660 return _.diag(SPV_ERROR_INVALID_DATA, inst)
661 << "Capability ImageBuffer is required to access storage image";
662 } else if (info.dim == SpvDimCube && info.arrayed == 1 &&
663 !_.HasCapability(SpvCapabilityImageCubeArray)) {
664 return _.diag(SPV_ERROR_INVALID_DATA, inst)
665 << "Capability ImageCubeArray is required to access "
666 << "storage image";
667 }
668
669 if (info.multisampled == 1 &&
670 !_.HasCapability(SpvCapabilityImageMSArray)) {
671 #if 0
672 // TODO(atgoo@github.com) The description of this rule in the spec
673 // is unclear and Glslang doesn't declare ImageMSArray. Need to clarify
674 // and reenable.
675 return _.diag(SPV_ERROR_INVALID_DATA, inst)
676 << "Capability ImageMSArray is required to access storage "
677 << "image";
678 #endif
679 }
680 } else {
681 return _.diag(SPV_ERROR_INVALID_DATA, inst)
682 << "Expected Image 'Sampled' parameter to be 0 or 2";
683 }
684 }
685
686 return SPV_SUCCESS;
687 }
688
689 // Returns true if opcode is *ImageSparse*, false otherwise.
IsSparse(SpvOp opcode)690 bool IsSparse(SpvOp opcode) {
691 switch (opcode) {
692 case SpvOpImageSparseSampleImplicitLod:
693 case SpvOpImageSparseSampleExplicitLod:
694 case SpvOpImageSparseSampleDrefImplicitLod:
695 case SpvOpImageSparseSampleDrefExplicitLod:
696 case SpvOpImageSparseSampleProjImplicitLod:
697 case SpvOpImageSparseSampleProjExplicitLod:
698 case SpvOpImageSparseSampleProjDrefImplicitLod:
699 case SpvOpImageSparseSampleProjDrefExplicitLod:
700 case SpvOpImageSparseFetch:
701 case SpvOpImageSparseGather:
702 case SpvOpImageSparseDrefGather:
703 case SpvOpImageSparseTexelsResident:
704 case SpvOpImageSparseRead: {
705 return true;
706 }
707
708 default: { return false; }
709 }
710
711 return false;
712 }
713
714 // Checks sparse image opcode result type and returns the second struct member.
715 // Returns inst.type_id for non-sparse image opcodes.
716 // Not valid for sparse image opcodes which do not return a struct.
GetActualResultType(ValidationState_t & _,const Instruction * inst,uint32_t * actual_result_type)717 spv_result_t GetActualResultType(ValidationState_t& _, const Instruction* inst,
718 uint32_t* actual_result_type) {
719 const SpvOp opcode = inst->opcode();
720
721 if (IsSparse(opcode)) {
722 const Instruction* const type_inst = _.FindDef(inst->type_id());
723 assert(type_inst);
724
725 if (!type_inst || type_inst->opcode() != SpvOpTypeStruct) {
726 return _.diag(SPV_ERROR_INVALID_DATA, inst)
727 << "Expected Result Type to be OpTypeStruct";
728 }
729
730 if (type_inst->words().size() != 4 ||
731 !_.IsIntScalarType(type_inst->word(2))) {
732 return _.diag(SPV_ERROR_INVALID_DATA, inst)
733 << "Expected Result Type to be a struct containing an int "
734 "scalar and a texel";
735 }
736
737 *actual_result_type = type_inst->word(3);
738 } else {
739 *actual_result_type = inst->type_id();
740 }
741
742 return SPV_SUCCESS;
743 }
744
745 // Returns a string describing actual result type of an opcode.
746 // Not valid for sparse image opcodes which do not return a struct.
GetActualResultTypeStr(SpvOp opcode)747 const char* GetActualResultTypeStr(SpvOp opcode) {
748 if (IsSparse(opcode)) return "Result Type's second member";
749 return "Result Type";
750 }
751
ValidateTypeImage(ValidationState_t & _,const Instruction * inst)752 spv_result_t ValidateTypeImage(ValidationState_t& _, const Instruction* inst) {
753 assert(inst->type_id() == 0);
754
755 ImageTypeInfo info;
756 if (!GetImageTypeInfo(_, inst->word(1), &info)) {
757 return _.diag(SPV_ERROR_INVALID_DATA, inst)
758 << "Corrupt image type definition";
759 }
760
761 if (_.IsIntScalarType(info.sampled_type) &&
762 (64 == _.GetBitWidth(info.sampled_type)) &&
763 !_.HasCapability(SpvCapabilityInt64ImageEXT)) {
764 return _.diag(SPV_ERROR_INVALID_DATA, inst)
765 << "Capability Int64ImageEXT is required when using Sampled Type of "
766 "64-bit int";
767 }
768
769 const auto target_env = _.context()->target_env;
770 if (spvIsVulkanEnv(target_env)) {
771 if ((!_.IsFloatScalarType(info.sampled_type) &&
772 !_.IsIntScalarType(info.sampled_type)) ||
773 ((32 != _.GetBitWidth(info.sampled_type)) &&
774 (64 != _.GetBitWidth(info.sampled_type))) ||
775 ((64 == _.GetBitWidth(info.sampled_type)) &&
776 _.IsFloatScalarType(info.sampled_type))) {
777 return _.diag(SPV_ERROR_INVALID_DATA, inst)
778 << _.VkErrorID(4656)
779 << "Expected Sampled Type to be a 32-bit int, 64-bit int or "
780 "32-bit float scalar type for Vulkan environment";
781 }
782 } else if (spvIsOpenCLEnv(target_env)) {
783 if (!_.IsVoidType(info.sampled_type)) {
784 return _.diag(SPV_ERROR_INVALID_DATA, inst)
785 << "Sampled Type must be OpTypeVoid in the OpenCL environment.";
786 }
787 } else {
788 const SpvOp sampled_type_opcode = _.GetIdOpcode(info.sampled_type);
789 if (sampled_type_opcode != SpvOpTypeVoid &&
790 sampled_type_opcode != SpvOpTypeInt &&
791 sampled_type_opcode != SpvOpTypeFloat) {
792 return _.diag(SPV_ERROR_INVALID_DATA, inst)
793 << "Expected Sampled Type to be either void or"
794 << " numerical scalar type";
795 }
796 }
797
798 // Dim is checked elsewhere.
799
800 if (info.depth > 2) {
801 return _.diag(SPV_ERROR_INVALID_DATA, inst)
802 << "Invalid Depth " << info.depth << " (must be 0, 1 or 2)";
803 }
804
805 if (info.arrayed > 1) {
806 return _.diag(SPV_ERROR_INVALID_DATA, inst)
807 << "Invalid Arrayed " << info.arrayed << " (must be 0 or 1)";
808 }
809
810 if (spvIsOpenCLEnv(target_env)) {
811 if ((info.arrayed == 1) && (info.dim != SpvDim1D) &&
812 (info.dim != SpvDim2D)) {
813 return _.diag(SPV_ERROR_INVALID_DATA, inst)
814 << "In the OpenCL environment, Arrayed may only be set to 1 "
815 << "when Dim is either 1D or 2D.";
816 }
817 }
818
819 if (info.multisampled > 1) {
820 return _.diag(SPV_ERROR_INVALID_DATA, inst)
821 << "Invalid MS " << info.multisampled << " (must be 0 or 1)";
822 }
823
824 if (spvIsOpenCLEnv(target_env)) {
825 if (info.multisampled != 0) {
826 return _.diag(SPV_ERROR_INVALID_DATA, inst)
827 << "MS must be 0 in the OpenCL environment.";
828 }
829 }
830
831 if (info.sampled > 2) {
832 return _.diag(SPV_ERROR_INVALID_DATA, inst)
833 << "Invalid Sampled " << info.sampled << " (must be 0, 1 or 2)";
834 }
835
836 if (spvIsVulkanEnv(target_env)) {
837 if (info.sampled == 0) {
838 return _.diag(SPV_ERROR_INVALID_DATA, inst)
839 << _.VkErrorID(4657)
840 << "Sampled must be 1 or 2 in the Vulkan environment.";
841 }
842 }
843
844 if (spvIsOpenCLEnv(_.context()->target_env)) {
845 if (info.sampled != 0) {
846 return _.diag(SPV_ERROR_INVALID_DATA, inst)
847 << "Sampled must be 0 in the OpenCL environment.";
848 }
849 }
850
851 if (info.dim == SpvDimSubpassData) {
852 if (info.sampled != 2) {
853 return _.diag(SPV_ERROR_INVALID_DATA, inst)
854 << "Dim SubpassData requires Sampled to be 2";
855 }
856
857 if (info.format != SpvImageFormatUnknown) {
858 return _.diag(SPV_ERROR_INVALID_DATA, inst)
859 << "Dim SubpassData requires format Unknown";
860 }
861 }
862
863 // Format and Access Qualifier are also checked elsewhere.
864
865 if (spvIsOpenCLEnv(_.context()->target_env)) {
866 if (info.access_qualifier == SpvAccessQualifierMax) {
867 return _.diag(SPV_ERROR_INVALID_DATA, inst)
868 << "In the OpenCL environment, the optional Access Qualifier"
869 << " must be present.";
870 }
871 }
872
873 if (info.multisampled && (info.sampled == 2) &&
874 (info.dim != SpvDimSubpassData)) {
875 if (!_.HasCapability(SpvCapabilityStorageImageMultisample)) {
876 return _.diag(SPV_ERROR_INVALID_DATA, inst)
877 << "Capability StorageImageMultisample is required when using "
878 "multisampled storage image";
879 }
880 }
881
882 return SPV_SUCCESS;
883 }
884
ValidateTypeSampledImage(ValidationState_t & _,const Instruction * inst)885 spv_result_t ValidateTypeSampledImage(ValidationState_t& _,
886 const Instruction* inst) {
887 const uint32_t image_type = inst->word(2);
888 if (_.GetIdOpcode(image_type) != SpvOpTypeImage) {
889 return _.diag(SPV_ERROR_INVALID_DATA, inst)
890 << "Expected Image to be of type OpTypeImage";
891 }
892
893 ImageTypeInfo info;
894 if (!GetImageTypeInfo(_, image_type, &info)) {
895 return _.diag(SPV_ERROR_INVALID_DATA, inst)
896 << "Corrupt image type definition";
897 }
898 // OpenCL requires Sampled=0, checked elsewhere.
899 // Vulkan uses the Sampled=1 case.
900 if ((info.sampled != 0) && (info.sampled != 1)) {
901 return _.diag(SPV_ERROR_INVALID_DATA, inst)
902 << _.VkErrorID(4657)
903 << "Sampled image type requires an image type with \"Sampled\" "
904 "operand set to 0 or 1";
905 }
906
907 return SPV_SUCCESS;
908 }
909
IsAllowedSampledImageOperand(SpvOp opcode)910 bool IsAllowedSampledImageOperand(SpvOp opcode) {
911 switch (opcode) {
912 case SpvOpSampledImage:
913 case SpvOpImageSampleImplicitLod:
914 case SpvOpImageSampleExplicitLod:
915 case SpvOpImageSampleDrefImplicitLod:
916 case SpvOpImageSampleDrefExplicitLod:
917 case SpvOpImageSampleProjImplicitLod:
918 case SpvOpImageSampleProjExplicitLod:
919 case SpvOpImageSampleProjDrefImplicitLod:
920 case SpvOpImageSampleProjDrefExplicitLod:
921 case SpvOpImageGather:
922 case SpvOpImageDrefGather:
923 case SpvOpImage:
924 case SpvOpImageQueryLod:
925 case SpvOpImageSparseSampleImplicitLod:
926 case SpvOpImageSparseSampleExplicitLod:
927 case SpvOpImageSparseSampleDrefImplicitLod:
928 case SpvOpImageSparseSampleDrefExplicitLod:
929 case SpvOpImageSparseGather:
930 case SpvOpImageSparseDrefGather:
931 case SpvOpCopyObject:
932 return true;
933 default:
934 return false;
935 }
936 }
937
ValidateSampledImage(ValidationState_t & _,const Instruction * inst)938 spv_result_t ValidateSampledImage(ValidationState_t& _,
939 const Instruction* inst) {
940 if (_.GetIdOpcode(inst->type_id()) != SpvOpTypeSampledImage) {
941 return _.diag(SPV_ERROR_INVALID_DATA, inst)
942 << "Expected Result Type to be OpTypeSampledImage.";
943 }
944
945 const uint32_t image_type = _.GetOperandTypeId(inst, 2);
946 if (_.GetIdOpcode(image_type) != SpvOpTypeImage) {
947 return _.diag(SPV_ERROR_INVALID_DATA, inst)
948 << "Expected Image to be of type OpTypeImage.";
949 }
950
951 ImageTypeInfo info;
952 if (!GetImageTypeInfo(_, image_type, &info)) {
953 return _.diag(SPV_ERROR_INVALID_DATA, inst)
954 << "Corrupt image type definition";
955 }
956
957 // TODO(atgoo@github.com) Check compatibility of result type and received
958 // image.
959
960 if (spvIsVulkanEnv(_.context()->target_env)) {
961 if (info.sampled != 1) {
962 return _.diag(SPV_ERROR_INVALID_DATA, inst)
963 << "Expected Image 'Sampled' parameter to be 1 "
964 << "for Vulkan environment.";
965 }
966 } else {
967 if (info.sampled != 0 && info.sampled != 1) {
968 return _.diag(SPV_ERROR_INVALID_DATA, inst)
969 << "Expected Image 'Sampled' parameter to be 0 or 1";
970 }
971 }
972
973 if (info.dim == SpvDimSubpassData) {
974 return _.diag(SPV_ERROR_INVALID_DATA, inst)
975 << "Expected Image 'Dim' parameter to be not SubpassData.";
976 }
977
978 if (_.GetIdOpcode(_.GetOperandTypeId(inst, 3)) != SpvOpTypeSampler) {
979 return _.diag(SPV_ERROR_INVALID_DATA, inst)
980 << "Expected Sampler to be of type OpTypeSampler";
981 }
982
983 // We need to validate 2 things:
984 // * All OpSampledImage instructions must be in the same block in which their
985 // Result <id> are consumed.
986 // * Result <id> from OpSampledImage instructions must not appear as operands
987 // to OpPhi instructions or OpSelect instructions, or any instructions other
988 // than the image lookup and query instructions specified to take an operand
989 // whose type is OpTypeSampledImage.
990 std::vector<Instruction*> consumers = _.getSampledImageConsumers(inst->id());
991 if (!consumers.empty()) {
992 for (auto consumer_instr : consumers) {
993 const auto consumer_opcode = consumer_instr->opcode();
994 if (consumer_instr->block() != inst->block()) {
995 return _.diag(SPV_ERROR_INVALID_ID, inst)
996 << "All OpSampledImage instructions must be in the same block "
997 "in "
998 "which their Result <id> are consumed. OpSampledImage Result "
999 "Type <id> '"
1000 << _.getIdName(inst->id())
1001 << "' has a consumer in a different basic "
1002 "block. The consumer instruction <id> is '"
1003 << _.getIdName(consumer_instr->id()) << "'.";
1004 }
1005
1006 if (consumer_opcode == SpvOpPhi || consumer_opcode == SpvOpSelect) {
1007 return _.diag(SPV_ERROR_INVALID_ID, inst)
1008 << "Result <id> from OpSampledImage instruction must not appear "
1009 "as "
1010 "operands of Op"
1011 << spvOpcodeString(static_cast<SpvOp>(consumer_opcode)) << "."
1012 << " Found result <id> '" << _.getIdName(inst->id())
1013 << "' as an operand of <id> '"
1014 << _.getIdName(consumer_instr->id()) << "'.";
1015 }
1016
1017 if (!IsAllowedSampledImageOperand(consumer_opcode)) {
1018 return _.diag(SPV_ERROR_INVALID_ID, inst)
1019 << "Result <id> from OpSampledImage instruction must not appear "
1020 "as operand for Op"
1021 << spvOpcodeString(static_cast<SpvOp>(consumer_opcode))
1022 << ", since it is not specificed as taking an "
1023 << "OpTypeSampledImage."
1024 << " Found result <id> '" << _.getIdName(inst->id())
1025 << "' as an operand of <id> '"
1026 << _.getIdName(consumer_instr->id()) << "'.";
1027 }
1028 }
1029 }
1030 return SPV_SUCCESS;
1031 }
1032
ValidateImageTexelPointer(ValidationState_t & _,const Instruction * inst)1033 spv_result_t ValidateImageTexelPointer(ValidationState_t& _,
1034 const Instruction* inst) {
1035 const auto result_type = _.FindDef(inst->type_id());
1036 if (result_type->opcode() != SpvOpTypePointer) {
1037 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1038 << "Expected Result Type to be OpTypePointer";
1039 }
1040
1041 const auto storage_class = result_type->GetOperandAs<uint32_t>(1);
1042 if (storage_class != SpvStorageClassImage) {
1043 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1044 << "Expected Result Type to be OpTypePointer whose Storage Class "
1045 "operand is Image";
1046 }
1047
1048 const auto ptr_type = result_type->GetOperandAs<uint32_t>(2);
1049 const auto ptr_opcode = _.GetIdOpcode(ptr_type);
1050 if (ptr_opcode != SpvOpTypeInt && ptr_opcode != SpvOpTypeFloat &&
1051 ptr_opcode != SpvOpTypeVoid) {
1052 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1053 << "Expected Result Type to be OpTypePointer whose Type operand "
1054 "must be a scalar numerical type or OpTypeVoid";
1055 }
1056
1057 const auto image_ptr = _.FindDef(_.GetOperandTypeId(inst, 2));
1058 if (!image_ptr || image_ptr->opcode() != SpvOpTypePointer) {
1059 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1060 << "Expected Image to be OpTypePointer";
1061 }
1062
1063 const auto image_type = image_ptr->GetOperandAs<uint32_t>(2);
1064 if (_.GetIdOpcode(image_type) != SpvOpTypeImage) {
1065 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1066 << "Expected Image to be OpTypePointer with Type OpTypeImage";
1067 }
1068
1069 ImageTypeInfo info;
1070 if (!GetImageTypeInfo(_, image_type, &info)) {
1071 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1072 << "Corrupt image type definition";
1073 }
1074
1075 if (info.sampled_type != ptr_type) {
1076 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1077 << "Expected Image 'Sampled Type' to be the same as the Type "
1078 "pointed to by Result Type";
1079 }
1080
1081 if (info.dim == SpvDimSubpassData) {
1082 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1083 << "Image Dim SubpassData cannot be used with OpImageTexelPointer";
1084 }
1085
1086 const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
1087 if (!coord_type || !_.IsIntScalarOrVectorType(coord_type)) {
1088 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1089 << "Expected Coordinate to be integer scalar or vector";
1090 }
1091
1092 uint32_t expected_coord_size = 0;
1093 if (info.arrayed == 0) {
1094 expected_coord_size = GetPlaneCoordSize(info);
1095 } else if (info.arrayed == 1) {
1096 switch (info.dim) {
1097 case SpvDim1D:
1098 expected_coord_size = 2;
1099 break;
1100 case SpvDimCube:
1101 case SpvDim2D:
1102 expected_coord_size = 3;
1103 break;
1104 default:
1105 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1106 << "Expected Image 'Dim' must be one of 1D, 2D, or Cube when "
1107 "Arrayed is 1";
1108 break;
1109 }
1110 }
1111
1112 const uint32_t actual_coord_size = _.GetDimension(coord_type);
1113 if (expected_coord_size != actual_coord_size) {
1114 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1115 << "Expected Coordinate to have " << expected_coord_size
1116 << " components, but given " << actual_coord_size;
1117 }
1118
1119 const uint32_t sample_type = _.GetOperandTypeId(inst, 4);
1120 if (!sample_type || !_.IsIntScalarType(sample_type)) {
1121 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1122 << "Expected Sample to be integer scalar";
1123 }
1124
1125 if (info.multisampled == 0) {
1126 uint64_t ms = 0;
1127 if (!_.GetConstantValUint64(inst->GetOperandAs<uint32_t>(4), &ms) ||
1128 ms != 0) {
1129 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1130 << "Expected Sample for Image with MS 0 to be a valid <id> for "
1131 "the value 0";
1132 }
1133 }
1134
1135 if (spvIsVulkanEnv(_.context()->target_env)) {
1136 if ((info.format != SpvImageFormatR64i) &&
1137 (info.format != SpvImageFormatR64ui) &&
1138 (info.format != SpvImageFormatR32f) &&
1139 (info.format != SpvImageFormatR32i) &&
1140 (info.format != SpvImageFormatR32ui)) {
1141 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1142 << _.VkErrorID(4658)
1143 << "Expected the Image Format in Image to be R64i, R64ui, R32f, "
1144 "R32i, or R32ui for Vulkan environment";
1145 }
1146 }
1147
1148 return SPV_SUCCESS;
1149 }
1150
ValidateImageLod(ValidationState_t & _,const Instruction * inst)1151 spv_result_t ValidateImageLod(ValidationState_t& _, const Instruction* inst) {
1152 const SpvOp opcode = inst->opcode();
1153 uint32_t actual_result_type = 0;
1154 if (spv_result_t error = GetActualResultType(_, inst, &actual_result_type)) {
1155 return error;
1156 }
1157
1158 if (!_.IsIntVectorType(actual_result_type) &&
1159 !_.IsFloatVectorType(actual_result_type)) {
1160 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1161 << "Expected " << GetActualResultTypeStr(opcode)
1162 << " to be int or float vector type";
1163 }
1164
1165 if (_.GetDimension(actual_result_type) != 4) {
1166 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1167 << "Expected " << GetActualResultTypeStr(opcode)
1168 << " to have 4 components";
1169 }
1170
1171 const uint32_t image_type = _.GetOperandTypeId(inst, 2);
1172 if (_.GetIdOpcode(image_type) != SpvOpTypeSampledImage) {
1173 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1174 << "Expected Sampled Image to be of type OpTypeSampledImage";
1175 }
1176
1177 ImageTypeInfo info;
1178 if (!GetImageTypeInfo(_, image_type, &info)) {
1179 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1180 << "Corrupt image type definition";
1181 }
1182
1183 if (spv_result_t result = ValidateImageCommon(_, inst, info)) return result;
1184
1185 if (info.multisampled) {
1186 // When using image operands, the Sample image operand is required if and
1187 // only if the image is multisampled (MS=1). The Sample image operand is
1188 // only allowed for fetch, read, and write.
1189 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1190 << "Sampling operation is invalid for multisample image";
1191 }
1192
1193 if (_.GetIdOpcode(info.sampled_type) != SpvOpTypeVoid) {
1194 const uint32_t texel_component_type =
1195 _.GetComponentType(actual_result_type);
1196 if (texel_component_type != info.sampled_type) {
1197 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1198 << "Expected Image 'Sampled Type' to be the same as "
1199 << GetActualResultTypeStr(opcode) << " components";
1200 }
1201 }
1202
1203 const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
1204 if ((opcode == SpvOpImageSampleExplicitLod ||
1205 opcode == SpvOpImageSparseSampleExplicitLod) &&
1206 _.HasCapability(SpvCapabilityKernel)) {
1207 if (!_.IsFloatScalarOrVectorType(coord_type) &&
1208 !_.IsIntScalarOrVectorType(coord_type)) {
1209 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1210 << "Expected Coordinate to be int or float scalar or vector";
1211 }
1212 } else {
1213 if (!_.IsFloatScalarOrVectorType(coord_type)) {
1214 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1215 << "Expected Coordinate to be float scalar or vector";
1216 }
1217 }
1218
1219 const uint32_t min_coord_size = GetMinCoordSize(opcode, info);
1220 const uint32_t actual_coord_size = _.GetDimension(coord_type);
1221 if (min_coord_size > actual_coord_size) {
1222 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1223 << "Expected Coordinate to have at least " << min_coord_size
1224 << " components, but given only " << actual_coord_size;
1225 }
1226
1227 const uint32_t mask = inst->words().size() <= 5 ? 0 : inst->word(5);
1228
1229 if (mask & SpvImageOperandsConstOffsetMask) {
1230 if (spvIsOpenCLEnv(_.context()->target_env)) {
1231 if (opcode == SpvOpImageSampleExplicitLod) {
1232 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1233 << "ConstOffset image operand not allowed "
1234 << "in the OpenCL environment.";
1235 }
1236 }
1237 }
1238
1239 if (spv_result_t result =
1240 ValidateImageOperands(_, inst, info, /* word_index = */ 6))
1241 return result;
1242
1243 return SPV_SUCCESS;
1244 }
1245
ValidateImageDrefLod(ValidationState_t & _,const Instruction * inst)1246 spv_result_t ValidateImageDrefLod(ValidationState_t& _,
1247 const Instruction* inst) {
1248 const SpvOp opcode = inst->opcode();
1249 uint32_t actual_result_type = 0;
1250 if (spv_result_t error = GetActualResultType(_, inst, &actual_result_type)) {
1251 return error;
1252 }
1253
1254 if (!_.IsIntScalarType(actual_result_type) &&
1255 !_.IsFloatScalarType(actual_result_type)) {
1256 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1257 << "Expected " << GetActualResultTypeStr(opcode)
1258 << " to be int or float scalar type";
1259 }
1260
1261 const uint32_t image_type = _.GetOperandTypeId(inst, 2);
1262 if (_.GetIdOpcode(image_type) != SpvOpTypeSampledImage) {
1263 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1264 << "Expected Sampled Image to be of type OpTypeSampledImage";
1265 }
1266
1267 ImageTypeInfo info;
1268 if (!GetImageTypeInfo(_, image_type, &info)) {
1269 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1270 << "Corrupt image type definition";
1271 }
1272
1273 if (spv_result_t result = ValidateImageCommon(_, inst, info)) return result;
1274
1275 if (info.multisampled) {
1276 // When using image operands, the Sample image operand is required if and
1277 // only if the image is multisampled (MS=1). The Sample image operand is
1278 // only allowed for fetch, read, and write.
1279 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1280 << "Dref sampling operation is invalid for multisample image";
1281 }
1282
1283 if (actual_result_type != info.sampled_type) {
1284 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1285 << "Expected Image 'Sampled Type' to be the same as "
1286 << GetActualResultTypeStr(opcode);
1287 }
1288
1289 const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
1290 if (!_.IsFloatScalarOrVectorType(coord_type)) {
1291 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1292 << "Expected Coordinate to be float scalar or vector";
1293 }
1294
1295 const uint32_t min_coord_size = GetMinCoordSize(opcode, info);
1296 const uint32_t actual_coord_size = _.GetDimension(coord_type);
1297 if (min_coord_size > actual_coord_size) {
1298 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1299 << "Expected Coordinate to have at least " << min_coord_size
1300 << " components, but given only " << actual_coord_size;
1301 }
1302
1303 const uint32_t dref_type = _.GetOperandTypeId(inst, 4);
1304 if (!_.IsFloatScalarType(dref_type) || _.GetBitWidth(dref_type) != 32) {
1305 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1306 << "Expected Dref to be of 32-bit float type";
1307 }
1308
1309 if (spv_result_t result =
1310 ValidateImageOperands(_, inst, info, /* word_index = */ 7))
1311 return result;
1312
1313 return SPV_SUCCESS;
1314 }
1315
ValidateImageFetch(ValidationState_t & _,const Instruction * inst)1316 spv_result_t ValidateImageFetch(ValidationState_t& _, const Instruction* inst) {
1317 uint32_t actual_result_type = 0;
1318 if (spv_result_t error = GetActualResultType(_, inst, &actual_result_type)) {
1319 return error;
1320 }
1321
1322 const SpvOp opcode = inst->opcode();
1323 if (!_.IsIntVectorType(actual_result_type) &&
1324 !_.IsFloatVectorType(actual_result_type)) {
1325 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1326 << "Expected " << GetActualResultTypeStr(opcode)
1327 << " to be int or float vector type";
1328 }
1329
1330 if (_.GetDimension(actual_result_type) != 4) {
1331 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1332 << "Expected " << GetActualResultTypeStr(opcode)
1333 << " to have 4 components";
1334 }
1335
1336 const uint32_t image_type = _.GetOperandTypeId(inst, 2);
1337 if (_.GetIdOpcode(image_type) != SpvOpTypeImage) {
1338 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1339 << "Expected Image to be of type OpTypeImage";
1340 }
1341
1342 ImageTypeInfo info;
1343 if (!GetImageTypeInfo(_, image_type, &info)) {
1344 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1345 << "Corrupt image type definition";
1346 }
1347
1348 if (_.GetIdOpcode(info.sampled_type) != SpvOpTypeVoid) {
1349 const uint32_t result_component_type =
1350 _.GetComponentType(actual_result_type);
1351 if (result_component_type != info.sampled_type) {
1352 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1353 << "Expected Image 'Sampled Type' to be the same as "
1354 << GetActualResultTypeStr(opcode) << " components";
1355 }
1356 }
1357
1358 if (info.dim == SpvDimCube) {
1359 return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Image 'Dim' cannot be Cube";
1360 }
1361
1362 if (info.sampled != 1) {
1363 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1364 << "Expected Image 'Sampled' parameter to be 1";
1365 }
1366
1367 const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
1368 if (!_.IsIntScalarOrVectorType(coord_type)) {
1369 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1370 << "Expected Coordinate to be int scalar or vector";
1371 }
1372
1373 const uint32_t min_coord_size = GetMinCoordSize(opcode, info);
1374 const uint32_t actual_coord_size = _.GetDimension(coord_type);
1375 if (min_coord_size > actual_coord_size) {
1376 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1377 << "Expected Coordinate to have at least " << min_coord_size
1378 << " components, but given only " << actual_coord_size;
1379 }
1380
1381 if (spv_result_t result =
1382 ValidateImageOperands(_, inst, info, /* word_index = */ 6))
1383 return result;
1384
1385 return SPV_SUCCESS;
1386 }
1387
ValidateImageGather(ValidationState_t & _,const Instruction * inst)1388 spv_result_t ValidateImageGather(ValidationState_t& _,
1389 const Instruction* inst) {
1390 uint32_t actual_result_type = 0;
1391 if (spv_result_t error = GetActualResultType(_, inst, &actual_result_type))
1392 return error;
1393
1394 const SpvOp opcode = inst->opcode();
1395 if (!_.IsIntVectorType(actual_result_type) &&
1396 !_.IsFloatVectorType(actual_result_type)) {
1397 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1398 << "Expected " << GetActualResultTypeStr(opcode)
1399 << " to be int or float vector type";
1400 }
1401
1402 if (_.GetDimension(actual_result_type) != 4) {
1403 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1404 << "Expected " << GetActualResultTypeStr(opcode)
1405 << " to have 4 components";
1406 }
1407
1408 const uint32_t image_type = _.GetOperandTypeId(inst, 2);
1409 if (_.GetIdOpcode(image_type) != SpvOpTypeSampledImage) {
1410 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1411 << "Expected Sampled Image to be of type OpTypeSampledImage";
1412 }
1413
1414 ImageTypeInfo info;
1415 if (!GetImageTypeInfo(_, image_type, &info)) {
1416 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1417 << "Corrupt image type definition";
1418 }
1419
1420 if (info.multisampled) {
1421 // When using image operands, the Sample image operand is required if and
1422 // only if the image is multisampled (MS=1). The Sample image operand is
1423 // only allowed for fetch, read, and write.
1424 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1425 << "Gather operation is invalid for multisample image";
1426 }
1427
1428 if (opcode == SpvOpImageDrefGather || opcode == SpvOpImageSparseDrefGather ||
1429 _.GetIdOpcode(info.sampled_type) != SpvOpTypeVoid) {
1430 const uint32_t result_component_type =
1431 _.GetComponentType(actual_result_type);
1432 if (result_component_type != info.sampled_type) {
1433 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1434 << "Expected Image 'Sampled Type' to be the same as "
1435 << GetActualResultTypeStr(opcode) << " components";
1436 }
1437 }
1438
1439 if (info.dim != SpvDim2D && info.dim != SpvDimCube &&
1440 info.dim != SpvDimRect) {
1441 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1442 << "Expected Image 'Dim' cannot be Cube";
1443 }
1444
1445 const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
1446 if (!_.IsFloatScalarOrVectorType(coord_type)) {
1447 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1448 << "Expected Coordinate to be float scalar or vector";
1449 }
1450
1451 const uint32_t min_coord_size = GetMinCoordSize(opcode, info);
1452 const uint32_t actual_coord_size = _.GetDimension(coord_type);
1453 if (min_coord_size > actual_coord_size) {
1454 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1455 << "Expected Coordinate to have at least " << min_coord_size
1456 << " components, but given only " << actual_coord_size;
1457 }
1458
1459 if (opcode == SpvOpImageGather || opcode == SpvOpImageSparseGather) {
1460 const uint32_t component = inst->GetOperandAs<uint32_t>(4);
1461 const uint32_t component_index_type = _.GetTypeId(component);
1462 if (!_.IsIntScalarType(component_index_type) ||
1463 _.GetBitWidth(component_index_type) != 32) {
1464 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1465 << "Expected Component to be 32-bit int scalar";
1466 }
1467 if (spvIsVulkanEnv(_.context()->target_env)) {
1468 if (!spvOpcodeIsConstant(_.GetIdOpcode(component))) {
1469 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1470 << _.VkErrorID(4664)
1471 << "Expected Component Operand to be a const object for Vulkan "
1472 "environment";
1473 }
1474 }
1475 } else {
1476 assert(opcode == SpvOpImageDrefGather ||
1477 opcode == SpvOpImageSparseDrefGather);
1478 const uint32_t dref_type = _.GetOperandTypeId(inst, 4);
1479 if (!_.IsFloatScalarType(dref_type) || _.GetBitWidth(dref_type) != 32) {
1480 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1481 << "Expected Dref to be of 32-bit float type";
1482 }
1483 }
1484
1485 if (spv_result_t result =
1486 ValidateImageOperands(_, inst, info, /* word_index = */ 7))
1487 return result;
1488
1489 return SPV_SUCCESS;
1490 }
1491
ValidateImageRead(ValidationState_t & _,const Instruction * inst)1492 spv_result_t ValidateImageRead(ValidationState_t& _, const Instruction* inst) {
1493 const SpvOp opcode = inst->opcode();
1494 uint32_t actual_result_type = 0;
1495 if (spv_result_t error = GetActualResultType(_, inst, &actual_result_type)) {
1496 return error;
1497 }
1498
1499 if (!_.IsIntScalarOrVectorType(actual_result_type) &&
1500 !_.IsFloatScalarOrVectorType(actual_result_type)) {
1501 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1502 << "Expected " << GetActualResultTypeStr(opcode)
1503 << " to be int or float scalar or vector type";
1504 }
1505
1506 const auto target_env = _.context()->target_env;
1507 // Vulkan requires the result to be a 4-element int or float
1508 // vector.
1509 if (spvIsVulkanEnv(target_env)) {
1510 if (_.GetDimension(actual_result_type) != 4) {
1511 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1512 << "Expected " << GetActualResultTypeStr(opcode)
1513 << " to have 4 components";
1514 }
1515 } // Check OpenCL below, after we get the image info.
1516
1517 const uint32_t image_type = _.GetOperandTypeId(inst, 2);
1518 if (_.GetIdOpcode(image_type) != SpvOpTypeImage) {
1519 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1520 << "Expected Image to be of type OpTypeImage";
1521 }
1522
1523 ImageTypeInfo info;
1524 if (!GetImageTypeInfo(_, image_type, &info)) {
1525 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1526 << "Corrupt image type definition";
1527 }
1528
1529 if (spvIsOpenCLEnv(target_env)) {
1530 // In OpenCL, a read from a depth image returns a scalar float. In other
1531 // cases, the result is always a 4-element vector.
1532 // https://www.khronos.org/registry/OpenCL/specs/3.0-unified/html/OpenCL_Env.html#_data_format_for_reading_and_writing_images
1533 // https://www.khronos.org/registry/OpenCL/specs/3.0-unified/html/OpenCL_C.html#image-read-and-write-functions
1534 // The builtins for reading depth images are:
1535 // float read_imagef(aQual image2d_depth_t image, int2 coord)
1536 // float read_imagef(aQual image2d_array_depth_t image, int4 coord)
1537 if (info.depth) {
1538 if (!_.IsFloatScalarType(actual_result_type)) {
1539 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1540 << "Expected " << GetActualResultTypeStr(opcode)
1541 << " from a depth image read to result in a scalar float value";
1542 }
1543 } else {
1544 if (_.GetDimension(actual_result_type) != 4) {
1545 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1546 << "Expected " << GetActualResultTypeStr(opcode)
1547 << " to have 4 components";
1548 }
1549 }
1550 }
1551
1552 if (info.dim == SpvDimSubpassData) {
1553 if (opcode == SpvOpImageSparseRead) {
1554 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1555 << "Image Dim SubpassData cannot be used with ImageSparseRead";
1556 }
1557
1558 _.function(inst->function()->id())
1559 ->RegisterExecutionModelLimitation(
1560 SpvExecutionModelFragment,
1561 std::string("Dim SubpassData requires Fragment execution model: ") +
1562 spvOpcodeString(opcode));
1563 }
1564
1565 if (_.GetIdOpcode(info.sampled_type) != SpvOpTypeVoid) {
1566 const uint32_t result_component_type =
1567 _.GetComponentType(actual_result_type);
1568 if (result_component_type != info.sampled_type) {
1569 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1570 << "Expected Image 'Sampled Type' to be the same as "
1571 << GetActualResultTypeStr(opcode) << " components";
1572 }
1573 }
1574
1575 if (spv_result_t result = ValidateImageCommon(_, inst, info)) return result;
1576
1577 const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
1578 if (!_.IsIntScalarOrVectorType(coord_type)) {
1579 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1580 << "Expected Coordinate to be int scalar or vector";
1581 }
1582
1583 const uint32_t min_coord_size = GetMinCoordSize(opcode, info);
1584 const uint32_t actual_coord_size = _.GetDimension(coord_type);
1585 if (min_coord_size > actual_coord_size) {
1586 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1587 << "Expected Coordinate to have at least " << min_coord_size
1588 << " components, but given only " << actual_coord_size;
1589 }
1590
1591 if (spvIsVulkanEnv(_.context()->target_env)) {
1592 if (info.format == SpvImageFormatUnknown && info.dim != SpvDimSubpassData &&
1593 !_.HasCapability(SpvCapabilityStorageImageReadWithoutFormat)) {
1594 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1595 << "Capability StorageImageReadWithoutFormat is required to "
1596 << "read storage image";
1597 }
1598 }
1599
1600 const uint32_t mask = inst->words().size() <= 5 ? 0 : inst->word(5);
1601
1602 if (mask & SpvImageOperandsConstOffsetMask) {
1603 if (spvIsOpenCLEnv(_.context()->target_env)) {
1604 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1605 << "ConstOffset image operand not allowed "
1606 << "in the OpenCL environment.";
1607 }
1608 }
1609
1610 if (spv_result_t result =
1611 ValidateImageOperands(_, inst, info, /* word_index = */ 6))
1612 return result;
1613
1614 return SPV_SUCCESS;
1615 }
1616
ValidateImageWrite(ValidationState_t & _,const Instruction * inst)1617 spv_result_t ValidateImageWrite(ValidationState_t& _, const Instruction* inst) {
1618 const uint32_t image_type = _.GetOperandTypeId(inst, 0);
1619 if (_.GetIdOpcode(image_type) != SpvOpTypeImage) {
1620 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1621 << "Expected Image to be of type OpTypeImage";
1622 }
1623
1624 ImageTypeInfo info;
1625 if (!GetImageTypeInfo(_, image_type, &info)) {
1626 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1627 << "Corrupt image type definition";
1628 }
1629
1630 if (info.dim == SpvDimSubpassData) {
1631 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1632 << "Image 'Dim' cannot be SubpassData";
1633 }
1634
1635 if (spv_result_t result = ValidateImageCommon(_, inst, info)) return result;
1636
1637 const uint32_t coord_type = _.GetOperandTypeId(inst, 1);
1638 if (!_.IsIntScalarOrVectorType(coord_type)) {
1639 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1640 << "Expected Coordinate to be int scalar or vector";
1641 }
1642
1643 const uint32_t min_coord_size = GetMinCoordSize(inst->opcode(), info);
1644 const uint32_t actual_coord_size = _.GetDimension(coord_type);
1645 if (min_coord_size > actual_coord_size) {
1646 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1647 << "Expected Coordinate to have at least " << min_coord_size
1648 << " components, but given only " << actual_coord_size;
1649 }
1650
1651 // TODO(atgoo@github.com) The spec doesn't explicitely say what the type
1652 // of texel should be.
1653 const uint32_t texel_type = _.GetOperandTypeId(inst, 2);
1654 if (!_.IsIntScalarOrVectorType(texel_type) &&
1655 !_.IsFloatScalarOrVectorType(texel_type)) {
1656 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1657 << "Expected Texel to be int or float vector or scalar";
1658 }
1659
1660 #if 0
1661 // TODO: See above.
1662 if (_.GetDimension(texel_type) != 4) {
1663 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1664 << "Expected Texel to have 4 components";
1665 }
1666 #endif
1667
1668 if (_.GetIdOpcode(info.sampled_type) != SpvOpTypeVoid) {
1669 const uint32_t texel_component_type = _.GetComponentType(texel_type);
1670 if (texel_component_type != info.sampled_type) {
1671 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1672 << "Expected Image 'Sampled Type' to be the same as Texel "
1673 << "components";
1674 }
1675 }
1676
1677 if (spvIsVulkanEnv(_.context()->target_env)) {
1678 if (info.format == SpvImageFormatUnknown && info.dim != SpvDimSubpassData &&
1679 !_.HasCapability(SpvCapabilityStorageImageWriteWithoutFormat)) {
1680 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1681 << "Capability StorageImageWriteWithoutFormat is required to "
1682 "write "
1683 << "to storage image";
1684 }
1685 }
1686
1687 if (inst->words().size() > 4) {
1688 if (spvIsOpenCLEnv(_.context()->target_env)) {
1689 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1690 << "Optional Image Operands are not allowed in the OpenCL "
1691 << "environment.";
1692 }
1693 }
1694
1695 if (spv_result_t result =
1696 ValidateImageOperands(_, inst, info, /* word_index = */ 5))
1697 return result;
1698
1699 return SPV_SUCCESS;
1700 }
1701
ValidateImage(ValidationState_t & _,const Instruction * inst)1702 spv_result_t ValidateImage(ValidationState_t& _, const Instruction* inst) {
1703 const uint32_t result_type = inst->type_id();
1704 if (_.GetIdOpcode(result_type) != SpvOpTypeImage) {
1705 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1706 << "Expected Result Type to be OpTypeImage";
1707 }
1708
1709 const uint32_t sampled_image_type = _.GetOperandTypeId(inst, 2);
1710 const Instruction* sampled_image_type_inst = _.FindDef(sampled_image_type);
1711 assert(sampled_image_type_inst);
1712
1713 if (sampled_image_type_inst->opcode() != SpvOpTypeSampledImage) {
1714 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1715 << "Expected Sample Image to be of type OpTypeSampleImage";
1716 }
1717
1718 if (sampled_image_type_inst->word(2) != result_type) {
1719 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1720 << "Expected Sample Image image type to be equal to Result Type";
1721 }
1722
1723 return SPV_SUCCESS;
1724 }
1725
ValidateImageQuerySizeLod(ValidationState_t & _,const Instruction * inst)1726 spv_result_t ValidateImageQuerySizeLod(ValidationState_t& _,
1727 const Instruction* inst) {
1728 const uint32_t result_type = inst->type_id();
1729 if (!_.IsIntScalarOrVectorType(result_type)) {
1730 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1731 << "Expected Result Type to be int scalar or vector type";
1732 }
1733
1734 const uint32_t image_type = _.GetOperandTypeId(inst, 2);
1735 if (_.GetIdOpcode(image_type) != SpvOpTypeImage) {
1736 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1737 << "Expected Image to be of type OpTypeImage";
1738 }
1739
1740 ImageTypeInfo info;
1741 if (!GetImageTypeInfo(_, image_type, &info)) {
1742 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1743 << "Corrupt image type definition";
1744 }
1745
1746 uint32_t expected_num_components = info.arrayed;
1747 switch (info.dim) {
1748 case SpvDim1D:
1749 expected_num_components += 1;
1750 break;
1751 case SpvDim2D:
1752 case SpvDimCube:
1753 expected_num_components += 2;
1754 break;
1755 case SpvDim3D:
1756 expected_num_components += 3;
1757 break;
1758 default:
1759 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1760 << "Image 'Dim' must be 1D, 2D, 3D or Cube";
1761 }
1762
1763 if (info.multisampled != 0) {
1764 return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Image 'MS' must be 0";
1765 }
1766
1767 const auto target_env = _.context()->target_env;
1768 if (spvIsVulkanEnv(target_env)) {
1769 if (info.sampled != 1) {
1770 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1771 << _.VkErrorID(4659)
1772 << "OpImageQuerySizeLod must only consume an \"Image\" operand "
1773 "whose type has its \"Sampled\" operand set to 1";
1774 }
1775 }
1776
1777 uint32_t result_num_components = _.GetDimension(result_type);
1778 if (result_num_components != expected_num_components) {
1779 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1780 << "Result Type has " << result_num_components << " components, "
1781 << "but " << expected_num_components << " expected";
1782 }
1783
1784 const uint32_t lod_type = _.GetOperandTypeId(inst, 3);
1785 if (!_.IsIntScalarType(lod_type)) {
1786 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1787 << "Expected Level of Detail to be int scalar";
1788 }
1789 return SPV_SUCCESS;
1790 }
1791
ValidateImageQuerySize(ValidationState_t & _,const Instruction * inst)1792 spv_result_t ValidateImageQuerySize(ValidationState_t& _,
1793 const Instruction* inst) {
1794 const uint32_t result_type = inst->type_id();
1795 if (!_.IsIntScalarOrVectorType(result_type)) {
1796 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1797 << "Expected Result Type to be int scalar or vector type";
1798 }
1799
1800 const uint32_t image_type = _.GetOperandTypeId(inst, 2);
1801 if (_.GetIdOpcode(image_type) != SpvOpTypeImage) {
1802 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1803 << "Expected Image to be of type OpTypeImage";
1804 }
1805
1806 ImageTypeInfo info;
1807 if (!GetImageTypeInfo(_, image_type, &info)) {
1808 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1809 << "Corrupt image type definition";
1810 }
1811
1812 uint32_t expected_num_components = info.arrayed;
1813 switch (info.dim) {
1814 case SpvDim1D:
1815 case SpvDimBuffer:
1816 expected_num_components += 1;
1817 break;
1818 case SpvDim2D:
1819 case SpvDimCube:
1820 case SpvDimRect:
1821 expected_num_components += 2;
1822 break;
1823 case SpvDim3D:
1824 expected_num_components += 3;
1825 break;
1826 default:
1827 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1828 << "Image 'Dim' must be 1D, Buffer, 2D, Cube, 3D or Rect";
1829 }
1830
1831 if (info.dim == SpvDim1D || info.dim == SpvDim2D || info.dim == SpvDim3D ||
1832 info.dim == SpvDimCube) {
1833 if (info.multisampled != 1 && info.sampled != 0 && info.sampled != 2) {
1834 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1835 << "Image must have either 'MS'=1 or 'Sampled'=0 or 'Sampled'=2";
1836 }
1837 }
1838
1839 uint32_t result_num_components = _.GetDimension(result_type);
1840 if (result_num_components != expected_num_components) {
1841 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1842 << "Result Type has " << result_num_components << " components, "
1843 << "but " << expected_num_components << " expected";
1844 }
1845
1846 return SPV_SUCCESS;
1847 }
1848
ValidateImageQueryFormatOrOrder(ValidationState_t & _,const Instruction * inst)1849 spv_result_t ValidateImageQueryFormatOrOrder(ValidationState_t& _,
1850 const Instruction* inst) {
1851 if (!_.IsIntScalarType(inst->type_id())) {
1852 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1853 << "Expected Result Type to be int scalar type";
1854 }
1855
1856 if (_.GetIdOpcode(_.GetOperandTypeId(inst, 2)) != SpvOpTypeImage) {
1857 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1858 << "Expected operand to be of type OpTypeImage";
1859 }
1860 return SPV_SUCCESS;
1861 }
1862
ValidateImageQueryLod(ValidationState_t & _,const Instruction * inst)1863 spv_result_t ValidateImageQueryLod(ValidationState_t& _,
1864 const Instruction* inst) {
1865 _.function(inst->function()->id())
1866 ->RegisterExecutionModelLimitation(
1867 [&](SpvExecutionModel model, std::string* message) {
1868 if (model != SpvExecutionModelFragment &&
1869 model != SpvExecutionModelGLCompute) {
1870 if (message) {
1871 *message = std::string(
1872 "OpImageQueryLod requires Fragment or GLCompute execution "
1873 "model");
1874 }
1875 return false;
1876 }
1877 return true;
1878 });
1879 _.function(inst->function()->id())
1880 ->RegisterLimitation([](const ValidationState_t& state,
1881 const Function* entry_point,
1882 std::string* message) {
1883 const auto* models = state.GetExecutionModels(entry_point->id());
1884 const auto* modes = state.GetExecutionModes(entry_point->id());
1885 if (models->find(SpvExecutionModelGLCompute) != models->end() &&
1886 modes->find(SpvExecutionModeDerivativeGroupLinearNV) ==
1887 modes->end() &&
1888 modes->find(SpvExecutionModeDerivativeGroupQuadsNV) ==
1889 modes->end()) {
1890 if (message) {
1891 *message = std::string(
1892 "OpImageQueryLod requires DerivativeGroupQuadsNV "
1893 "or DerivativeGroupLinearNV execution mode for GLCompute "
1894 "execution model");
1895 }
1896 return false;
1897 }
1898 return true;
1899 });
1900
1901 const uint32_t result_type = inst->type_id();
1902 if (!_.IsFloatVectorType(result_type)) {
1903 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1904 << "Expected Result Type to be float vector type";
1905 }
1906
1907 if (_.GetDimension(result_type) != 2) {
1908 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1909 << "Expected Result Type to have 2 components";
1910 }
1911
1912 const uint32_t image_type = _.GetOperandTypeId(inst, 2);
1913 if (_.GetIdOpcode(image_type) != SpvOpTypeSampledImage) {
1914 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1915 << "Expected Image operand to be of type OpTypeSampledImage";
1916 }
1917
1918 ImageTypeInfo info;
1919 if (!GetImageTypeInfo(_, image_type, &info)) {
1920 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1921 << "Corrupt image type definition";
1922 }
1923
1924 if (info.dim != SpvDim1D && info.dim != SpvDim2D && info.dim != SpvDim3D &&
1925 info.dim != SpvDimCube) {
1926 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1927 << "Image 'Dim' must be 1D, 2D, 3D or Cube";
1928 }
1929
1930 const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
1931 if (_.HasCapability(SpvCapabilityKernel)) {
1932 if (!_.IsFloatScalarOrVectorType(coord_type) &&
1933 !_.IsIntScalarOrVectorType(coord_type)) {
1934 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1935 << "Expected Coordinate to be int or float scalar or vector";
1936 }
1937 } else {
1938 if (!_.IsFloatScalarOrVectorType(coord_type)) {
1939 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1940 << "Expected Coordinate to be float scalar or vector";
1941 }
1942 }
1943
1944 const uint32_t min_coord_size = GetPlaneCoordSize(info);
1945 const uint32_t actual_coord_size = _.GetDimension(coord_type);
1946 if (min_coord_size > actual_coord_size) {
1947 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1948 << "Expected Coordinate to have at least " << min_coord_size
1949 << " components, but given only " << actual_coord_size;
1950 }
1951
1952 // The operad is a sampled image.
1953 // The sampled image type is already checked to be parameterized by an image
1954 // type with Sampled=0 or Sampled=1. Vulkan bans Sampled=0, and so we have
1955 // Sampled=1. So the validator already enforces Vulkan VUID 4659:
1956 // OpImageQuerySizeLod must only consume an “Image” operand whose type has
1957 // its "Sampled" operand set to 1
1958 return SPV_SUCCESS;
1959 }
1960
ValidateImageSparseLod(ValidationState_t & _,const Instruction * inst)1961 spv_result_t ValidateImageSparseLod(ValidationState_t& _,
1962 const Instruction* inst) {
1963 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1964 << "Instruction reserved for future use, use of this instruction "
1965 << "is invalid";
1966 }
1967
ValidateImageQueryLevelsOrSamples(ValidationState_t & _,const Instruction * inst)1968 spv_result_t ValidateImageQueryLevelsOrSamples(ValidationState_t& _,
1969 const Instruction* inst) {
1970 if (!_.IsIntScalarType(inst->type_id())) {
1971 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1972 << "Expected Result Type to be int scalar type";
1973 }
1974
1975 const uint32_t image_type = _.GetOperandTypeId(inst, 2);
1976 if (_.GetIdOpcode(image_type) != SpvOpTypeImage) {
1977 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1978 << "Expected Image to be of type OpTypeImage";
1979 }
1980
1981 ImageTypeInfo info;
1982 if (!GetImageTypeInfo(_, image_type, &info)) {
1983 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1984 << "Corrupt image type definition";
1985 }
1986
1987 const SpvOp opcode = inst->opcode();
1988 if (opcode == SpvOpImageQueryLevels) {
1989 if (info.dim != SpvDim1D && info.dim != SpvDim2D && info.dim != SpvDim3D &&
1990 info.dim != SpvDimCube) {
1991 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1992 << "Image 'Dim' must be 1D, 2D, 3D or Cube";
1993 }
1994 const auto target_env = _.context()->target_env;
1995 if (spvIsVulkanEnv(target_env)) {
1996 if (info.sampled != 1) {
1997 return _.diag(SPV_ERROR_INVALID_DATA, inst)
1998 << _.VkErrorID(4659)
1999 << "OpImageQueryLevels must only consume an \"Image\" operand "
2000 "whose type has its \"Sampled\" operand set to 1";
2001 }
2002 }
2003 } else {
2004 assert(opcode == SpvOpImageQuerySamples);
2005 if (info.dim != SpvDim2D) {
2006 return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Image 'Dim' must be 2D";
2007 }
2008
2009 if (info.multisampled != 1) {
2010 return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Image 'MS' must be 1";
2011 }
2012 }
2013 return SPV_SUCCESS;
2014 }
2015
ValidateImageSparseTexelsResident(ValidationState_t & _,const Instruction * inst)2016 spv_result_t ValidateImageSparseTexelsResident(ValidationState_t& _,
2017 const Instruction* inst) {
2018 if (!_.IsBoolScalarType(inst->type_id())) {
2019 return _.diag(SPV_ERROR_INVALID_DATA, inst)
2020 << "Expected Result Type to be bool scalar type";
2021 }
2022
2023 const uint32_t resident_code_type = _.GetOperandTypeId(inst, 2);
2024 if (!_.IsIntScalarType(resident_code_type)) {
2025 return _.diag(SPV_ERROR_INVALID_DATA, inst)
2026 << "Expected Resident Code to be int scalar";
2027 }
2028
2029 return SPV_SUCCESS;
2030 }
2031
2032 } // namespace
2033
2034 // Validates correctness of image instructions.
ImagePass(ValidationState_t & _,const Instruction * inst)2035 spv_result_t ImagePass(ValidationState_t& _, const Instruction* inst) {
2036 const SpvOp opcode = inst->opcode();
2037 if (IsImplicitLod(opcode)) {
2038 _.function(inst->function()->id())
2039 ->RegisterExecutionModelLimitation([opcode](SpvExecutionModel model,
2040 std::string* message) {
2041 if (model != SpvExecutionModelFragment &&
2042 model != SpvExecutionModelGLCompute) {
2043 if (message) {
2044 *message =
2045 std::string(
2046 "ImplicitLod instructions require Fragment or GLCompute "
2047 "execution model: ") +
2048 spvOpcodeString(opcode);
2049 }
2050 return false;
2051 }
2052 return true;
2053 });
2054 _.function(inst->function()->id())
2055 ->RegisterLimitation([opcode](const ValidationState_t& state,
2056 const Function* entry_point,
2057 std::string* message) {
2058 const auto* models = state.GetExecutionModels(entry_point->id());
2059 const auto* modes = state.GetExecutionModes(entry_point->id());
2060 if (models->find(SpvExecutionModelGLCompute) != models->end() &&
2061 modes->find(SpvExecutionModeDerivativeGroupLinearNV) ==
2062 modes->end() &&
2063 modes->find(SpvExecutionModeDerivativeGroupQuadsNV) ==
2064 modes->end()) {
2065 if (message) {
2066 *message =
2067 std::string(
2068 "ImplicitLod instructions require DerivativeGroupQuadsNV "
2069 "or DerivativeGroupLinearNV execution mode for GLCompute "
2070 "execution model: ") +
2071 spvOpcodeString(opcode);
2072 }
2073 return false;
2074 }
2075 return true;
2076 });
2077 }
2078
2079 switch (opcode) {
2080 case SpvOpTypeImage:
2081 return ValidateTypeImage(_, inst);
2082 case SpvOpTypeSampledImage:
2083 return ValidateTypeSampledImage(_, inst);
2084 case SpvOpSampledImage:
2085 return ValidateSampledImage(_, inst);
2086 case SpvOpImageTexelPointer:
2087 return ValidateImageTexelPointer(_, inst);
2088
2089 case SpvOpImageSampleImplicitLod:
2090 case SpvOpImageSampleExplicitLod:
2091 case SpvOpImageSampleProjImplicitLod:
2092 case SpvOpImageSampleProjExplicitLod:
2093 case SpvOpImageSparseSampleImplicitLod:
2094 case SpvOpImageSparseSampleExplicitLod:
2095 return ValidateImageLod(_, inst);
2096
2097 case SpvOpImageSampleDrefImplicitLod:
2098 case SpvOpImageSampleDrefExplicitLod:
2099 case SpvOpImageSampleProjDrefImplicitLod:
2100 case SpvOpImageSampleProjDrefExplicitLod:
2101 case SpvOpImageSparseSampleDrefImplicitLod:
2102 case SpvOpImageSparseSampleDrefExplicitLod:
2103 return ValidateImageDrefLod(_, inst);
2104
2105 case SpvOpImageFetch:
2106 case SpvOpImageSparseFetch:
2107 return ValidateImageFetch(_, inst);
2108
2109 case SpvOpImageGather:
2110 case SpvOpImageDrefGather:
2111 case SpvOpImageSparseGather:
2112 case SpvOpImageSparseDrefGather:
2113 return ValidateImageGather(_, inst);
2114
2115 case SpvOpImageRead:
2116 case SpvOpImageSparseRead:
2117 return ValidateImageRead(_, inst);
2118
2119 case SpvOpImageWrite:
2120 return ValidateImageWrite(_, inst);
2121
2122 case SpvOpImage:
2123 return ValidateImage(_, inst);
2124
2125 case SpvOpImageQueryFormat:
2126 case SpvOpImageQueryOrder:
2127 return ValidateImageQueryFormatOrOrder(_, inst);
2128
2129 case SpvOpImageQuerySizeLod:
2130 return ValidateImageQuerySizeLod(_, inst);
2131 case SpvOpImageQuerySize:
2132 return ValidateImageQuerySize(_, inst);
2133 case SpvOpImageQueryLod:
2134 return ValidateImageQueryLod(_, inst);
2135
2136 case SpvOpImageQueryLevels:
2137 case SpvOpImageQuerySamples:
2138 return ValidateImageQueryLevelsOrSamples(_, inst);
2139
2140 case SpvOpImageSparseSampleProjImplicitLod:
2141 case SpvOpImageSparseSampleProjExplicitLod:
2142 case SpvOpImageSparseSampleProjDrefImplicitLod:
2143 case SpvOpImageSparseSampleProjDrefExplicitLod:
2144 return ValidateImageSparseLod(_, inst);
2145
2146 case SpvOpImageSparseTexelsResident:
2147 return ValidateImageSparseTexelsResident(_, inst);
2148
2149 default:
2150 break;
2151 }
2152
2153 return SPV_SUCCESS;
2154 }
2155
2156 } // namespace val
2157 } // namespace spvtools
2158