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