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