• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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