• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2017 Google Inc.
2 // Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights
3 // reserved.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 //     http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 
17 // Validates correctness of image instructions.
18 
19 #include <string>
20 
21 #include "source/opcode.h"
22 #include "source/spirv_constant.h"
23 #include "source/spirv_target_env.h"
24 #include "source/util/bitutils.h"
25 #include "source/val/instruction.h"
26 #include "source/val/validate.h"
27 #include "source/val/validate_scopes.h"
28 #include "source/val/validation_state.h"
29 
30 namespace spvtools {
31 namespace val {
32 namespace {
33 
34 // Performs compile time check that all spv::ImageOperandsMask::XXX cases are
35 // handled in this module. If spv::ImageOperandsMask::XXX list changes, this
36 // function will fail the build. For all other purposes this is a placeholder
37 // function.
CheckAllImageOperandsHandled()38 bool CheckAllImageOperandsHandled() {
39   spv::ImageOperandsMask enum_val = spv::ImageOperandsMask::Bias;
40 
41   // Some improvised code to prevent the compiler from considering enum_val
42   // constant and optimizing the switch away.
43   uint32_t stack_var = 0;
44   if (reinterpret_cast<uintptr_t>(&stack_var) % 256)
45     enum_val = spv::ImageOperandsMask::Lod;
46 
47   switch (enum_val) {
48     // Please update the validation rules in this module if you are changing
49     // the list of image operands, and add new enum values to this switch.
50     case spv::ImageOperandsMask::MaskNone:
51       return false;
52     case spv::ImageOperandsMask::Bias:
53     case spv::ImageOperandsMask::Lod:
54     case spv::ImageOperandsMask::Grad:
55     case spv::ImageOperandsMask::ConstOffset:
56     case spv::ImageOperandsMask::Offset:
57     case spv::ImageOperandsMask::ConstOffsets:
58     case spv::ImageOperandsMask::Sample:
59     case spv::ImageOperandsMask::MinLod:
60 
61     // TODO(dneto): Support image operands related to the Vulkan memory model.
62     // https://gitlab.khronos.org/spirv/spirv-tools/issues/32
63     case spv::ImageOperandsMask::MakeTexelAvailableKHR:
64     case spv::ImageOperandsMask::MakeTexelVisibleKHR:
65     case spv::ImageOperandsMask::NonPrivateTexelKHR:
66     case spv::ImageOperandsMask::VolatileTexelKHR:
67     case spv::ImageOperandsMask::SignExtend:
68     case spv::ImageOperandsMask::ZeroExtend:
69     // TODO(jaebaek): Move this line properly after handling image offsets
70     //                operand. This line temporarily fixes CI failure that
71     //                blocks other PRs.
72     // https://github.com/KhronosGroup/SPIRV-Tools/issues/4565
73     case spv::ImageOperandsMask::Offsets:
74     case spv::ImageOperandsMask::Nontemporal:
75       return true;
76   }
77   return false;
78 }
79 
80 // Used by GetImageTypeInfo. See OpTypeImage spec for more information.
81 struct ImageTypeInfo {
82   uint32_t sampled_type = 0;
83   spv::Dim dim = spv::Dim::Max;
84   uint32_t depth = 0;
85   uint32_t arrayed = 0;
86   uint32_t multisampled = 0;
87   uint32_t sampled = 0;
88   spv::ImageFormat format = spv::ImageFormat::Max;
89   spv::AccessQualifier access_qualifier = spv::AccessQualifier::Max;
90 };
91 
92 // Provides information on image type. |id| should be object of either
93 // OpTypeImage or OpTypeSampledImage type. Returns false in case of failure
94 // (not a valid id, failed to parse the instruction, etc).
GetImageTypeInfo(const ValidationState_t & _,uint32_t id,ImageTypeInfo * info)95 bool GetImageTypeInfo(const ValidationState_t& _, uint32_t id,
96                       ImageTypeInfo* info) {
97   if (!id || !info) return false;
98 
99   const Instruction* inst = _.FindDef(id);
100   assert(inst);
101 
102   if (inst->opcode() == spv::Op::OpTypeSampledImage) {
103     inst = _.FindDef(inst->word(2));
104     assert(inst);
105   }
106 
107   if (inst->opcode() != spv::Op::OpTypeImage) return false;
108 
109   const size_t num_words = inst->words().size();
110   if (num_words != 9 && num_words != 10) return false;
111 
112   info->sampled_type = inst->word(2);
113   info->dim = static_cast<spv::Dim>(inst->word(3));
114   info->depth = inst->word(4);
115   info->arrayed = inst->word(5);
116   info->multisampled = inst->word(6);
117   info->sampled = inst->word(7);
118   info->format = static_cast<spv::ImageFormat>(inst->word(8));
119   info->access_qualifier =
120       num_words < 10 ? spv::AccessQualifier::Max
121                      : static_cast<spv::AccessQualifier>(inst->word(9));
122   return true;
123 }
124 
IsImplicitLod(spv::Op opcode)125 bool IsImplicitLod(spv::Op opcode) {
126   switch (opcode) {
127     case spv::Op::OpImageSampleImplicitLod:
128     case spv::Op::OpImageSampleDrefImplicitLod:
129     case spv::Op::OpImageSampleProjImplicitLod:
130     case spv::Op::OpImageSampleProjDrefImplicitLod:
131     case spv::Op::OpImageSparseSampleImplicitLod:
132     case spv::Op::OpImageSparseSampleDrefImplicitLod:
133     case spv::Op::OpImageSparseSampleProjImplicitLod:
134     case spv::Op::OpImageSparseSampleProjDrefImplicitLod:
135       return true;
136     default:
137       break;
138   }
139   return false;
140 }
141 
IsExplicitLod(spv::Op opcode)142 bool IsExplicitLod(spv::Op opcode) {
143   switch (opcode) {
144     case spv::Op::OpImageSampleExplicitLod:
145     case spv::Op::OpImageSampleDrefExplicitLod:
146     case spv::Op::OpImageSampleProjExplicitLod:
147     case spv::Op::OpImageSampleProjDrefExplicitLod:
148     case spv::Op::OpImageSparseSampleExplicitLod:
149     case spv::Op::OpImageSparseSampleDrefExplicitLod:
150     case spv::Op::OpImageSparseSampleProjExplicitLod:
151     case spv::Op::OpImageSparseSampleProjDrefExplicitLod:
152       return true;
153     default:
154       break;
155   }
156   return false;
157 }
158 
IsValidLodOperand(const ValidationState_t & _,spv::Op opcode)159 bool IsValidLodOperand(const ValidationState_t& _, spv::Op opcode) {
160   switch (opcode) {
161     case spv::Op::OpImageRead:
162     case spv::Op::OpImageWrite:
163     case spv::Op::OpImageSparseRead:
164       return _.HasCapability(spv::Capability::ImageReadWriteLodAMD);
165     default:
166       return IsExplicitLod(opcode);
167   }
168 }
169 
IsValidGatherLodBiasAMD(const ValidationState_t & _,spv::Op opcode)170 bool IsValidGatherLodBiasAMD(const ValidationState_t& _, spv::Op opcode) {
171   switch (opcode) {
172     case spv::Op::OpImageGather:
173     case spv::Op::OpImageSparseGather:
174       return _.HasCapability(spv::Capability::ImageGatherBiasLodAMD);
175     default:
176       break;
177   }
178   return false;
179 }
180 
181 // Returns true if the opcode is a Image instruction which applies
182 // homogenous projection to the coordinates.
IsProj(spv::Op opcode)183 bool IsProj(spv::Op opcode) {
184   switch (opcode) {
185     case spv::Op::OpImageSampleProjImplicitLod:
186     case spv::Op::OpImageSampleProjDrefImplicitLod:
187     case spv::Op::OpImageSparseSampleProjImplicitLod:
188     case spv::Op::OpImageSparseSampleProjDrefImplicitLod:
189     case spv::Op::OpImageSampleProjExplicitLod:
190     case spv::Op::OpImageSampleProjDrefExplicitLod:
191     case spv::Op::OpImageSparseSampleProjExplicitLod:
192     case spv::Op::OpImageSparseSampleProjDrefExplicitLod:
193       return true;
194     default:
195       break;
196   }
197   return false;
198 }
199 
200 // Returns the number of components in a coordinate used to access a texel in
201 // a single plane of an image with the given parameters.
GetPlaneCoordSize(const ImageTypeInfo & info)202 uint32_t GetPlaneCoordSize(const ImageTypeInfo& info) {
203   uint32_t plane_size = 0;
204   // If this switch breaks your build, please add new values below.
205   switch (info.dim) {
206     case spv::Dim::Dim1D:
207     case spv::Dim::Buffer:
208       plane_size = 1;
209       break;
210     case spv::Dim::Dim2D:
211     case spv::Dim::Rect:
212     case spv::Dim::SubpassData:
213     case spv::Dim::TileImageDataEXT:
214       plane_size = 2;
215       break;
216     case spv::Dim::Dim3D:
217     case spv::Dim::Cube:
218       // For Cube direction vector is used instead of UV.
219       plane_size = 3;
220       break;
221     case spv::Dim::Max:
222     default:
223       assert(0);
224       break;
225   }
226 
227   return plane_size;
228 }
229 
230 // Returns minimal number of coordinates based on image dim, arrayed and whether
231 // the instruction uses projection coordinates.
GetMinCoordSize(spv::Op opcode,const ImageTypeInfo & info)232 uint32_t GetMinCoordSize(spv::Op opcode, const ImageTypeInfo& info) {
233   if (info.dim == spv::Dim::Cube &&
234       (opcode == spv::Op::OpImageRead || opcode == spv::Op::OpImageWrite ||
235        opcode == spv::Op::OpImageSparseRead)) {
236     // These opcodes use UV for Cube, not direction vector.
237     return 3;
238   }
239 
240   return GetPlaneCoordSize(info) + info.arrayed + (IsProj(opcode) ? 1 : 0);
241 }
242 
243 // Checks ImageOperand bitfield and respective operands.
244 // word_index is the index of the first word after the image-operand mask word.
ValidateImageOperands(ValidationState_t & _,const Instruction * inst,const ImageTypeInfo & info,uint32_t word_index)245 spv_result_t ValidateImageOperands(ValidationState_t& _,
246                                    const Instruction* inst,
247                                    const ImageTypeInfo& info,
248                                    uint32_t word_index) {
249   static const bool kAllImageOperandsHandled = CheckAllImageOperandsHandled();
250   (void)kAllImageOperandsHandled;
251 
252   const spv::Op opcode = inst->opcode();
253   const size_t num_words = inst->words().size();
254 
255   const bool have_explicit_mask = (word_index - 1 < num_words);
256   const uint32_t mask = have_explicit_mask ? inst->word(word_index - 1) : 0u;
257 
258   if (have_explicit_mask) {
259     // NonPrivate, Volatile, SignExtend, ZeroExtend take no operand words.
260     const uint32_t mask_bits_having_operands =
261         mask & ~uint32_t(spv::ImageOperandsMask::NonPrivateTexelKHR |
262                          spv::ImageOperandsMask::VolatileTexelKHR |
263                          spv::ImageOperandsMask::SignExtend |
264                          spv::ImageOperandsMask::ZeroExtend |
265                          spv::ImageOperandsMask::Nontemporal);
266     size_t expected_num_image_operand_words =
267         spvtools::utils::CountSetBits(mask_bits_having_operands);
268     if (mask & uint32_t(spv::ImageOperandsMask::Grad)) {
269       // Grad uses two words.
270       ++expected_num_image_operand_words;
271     }
272 
273     if (expected_num_image_operand_words != num_words - word_index) {
274       return _.diag(SPV_ERROR_INVALID_DATA, inst)
275              << "Number of image operand ids doesn't correspond to the bit "
276                 "mask";
277     }
278   } else if (num_words != word_index - 1) {
279     return _.diag(SPV_ERROR_INVALID_DATA, inst)
280            << "Number of image operand ids doesn't correspond to the bit mask";
281   }
282 
283   if (info.multisampled &
284       (0 == (mask & uint32_t(spv::ImageOperandsMask::Sample)))) {
285     return _.diag(SPV_ERROR_INVALID_DATA, inst)
286            << "Image Operand Sample is required for operation on "
287               "multi-sampled image";
288   }
289 
290   // After this point, only set bits in the image operands mask can cause
291   // the module to be invalid.
292   if (mask == 0) return SPV_SUCCESS;
293 
294   if (spvtools::utils::CountSetBits(
295           mask & uint32_t(spv::ImageOperandsMask::Offset |
296                           spv::ImageOperandsMask::ConstOffset |
297                           spv::ImageOperandsMask::ConstOffsets |
298                           spv::ImageOperandsMask::Offsets)) > 1) {
299     return _.diag(SPV_ERROR_INVALID_DATA, inst)
300            << "Image Operands Offset, ConstOffset, ConstOffsets, Offsets "
301               "cannot be used together";
302   }
303 
304   const bool is_implicit_lod = IsImplicitLod(opcode);
305   const bool is_explicit_lod = IsExplicitLod(opcode);
306   const bool is_valid_lod_operand = IsValidLodOperand(_, opcode);
307   const bool is_valid_gather_lod_bias_amd = IsValidGatherLodBiasAMD(_, opcode);
308 
309   // The checks should be done in the order of definition of OperandImage.
310 
311   if (mask & uint32_t(spv::ImageOperandsMask::Bias)) {
312     if (!is_implicit_lod && !is_valid_gather_lod_bias_amd) {
313       return _.diag(SPV_ERROR_INVALID_DATA, inst)
314              << "Image Operand Bias can only be used with ImplicitLod opcodes";
315     }
316 
317     const uint32_t type_id = _.GetTypeId(inst->word(word_index++));
318     if (!_.IsFloatScalarType(type_id)) {
319       return _.diag(SPV_ERROR_INVALID_DATA, inst)
320              << "Expected Image Operand Bias to be float scalar";
321     }
322 
323     if (info.dim != spv::Dim::Dim1D && info.dim != spv::Dim::Dim2D &&
324         info.dim != spv::Dim::Dim3D && info.dim != spv::Dim::Cube) {
325       return _.diag(SPV_ERROR_INVALID_DATA, inst)
326              << "Image Operand Bias requires 'Dim' parameter to be 1D, 2D, 3D "
327                 "or Cube";
328     }
329 
330     // Multisampled is already checked.
331   }
332 
333   if (mask & uint32_t(spv::ImageOperandsMask::Lod)) {
334     if (!is_valid_lod_operand && opcode != spv::Op::OpImageFetch &&
335         opcode != spv::Op::OpImageSparseFetch &&
336         !is_valid_gather_lod_bias_amd) {
337       return _.diag(SPV_ERROR_INVALID_DATA, inst)
338              << "Image Operand Lod can only be used with ExplicitLod opcodes "
339              << "and OpImageFetch";
340     }
341 
342     if (mask & uint32_t(spv::ImageOperandsMask::Grad)) {
343       return _.diag(SPV_ERROR_INVALID_DATA, inst)
344              << "Image Operand bits Lod and Grad cannot be set at the same "
345                 "time";
346     }
347 
348     const uint32_t type_id = _.GetTypeId(inst->word(word_index++));
349     if (is_explicit_lod || is_valid_gather_lod_bias_amd) {
350       if (!_.IsFloatScalarType(type_id)) {
351         return _.diag(SPV_ERROR_INVALID_DATA, inst)
352                << "Expected Image Operand Lod to be float scalar when used "
353                << "with ExplicitLod";
354       }
355     } else {
356       if (!_.IsIntScalarType(type_id)) {
357         return _.diag(SPV_ERROR_INVALID_DATA, inst)
358                << "Expected Image Operand Lod to be int scalar when used with "
359                << "OpImageFetch";
360       }
361     }
362 
363     if (info.dim != spv::Dim::Dim1D && info.dim != spv::Dim::Dim2D &&
364         info.dim != spv::Dim::Dim3D && info.dim != spv::Dim::Cube) {
365       return _.diag(SPV_ERROR_INVALID_DATA, inst)
366              << "Image Operand Lod requires 'Dim' parameter to be 1D, 2D, 3D "
367                 "or Cube";
368     }
369 
370     // Multisampled is already checked.
371   }
372 
373   if (mask & uint32_t(spv::ImageOperandsMask::Grad)) {
374     if (!is_explicit_lod) {
375       return _.diag(SPV_ERROR_INVALID_DATA, inst)
376              << "Image Operand Grad can only be used with ExplicitLod opcodes";
377     }
378 
379     const uint32_t dx_type_id = _.GetTypeId(inst->word(word_index++));
380     const uint32_t dy_type_id = _.GetTypeId(inst->word(word_index++));
381     if (!_.IsFloatScalarOrVectorType(dx_type_id) ||
382         !_.IsFloatScalarOrVectorType(dy_type_id)) {
383       return _.diag(SPV_ERROR_INVALID_DATA, inst)
384              << "Expected both Image Operand Grad ids to be float scalars or "
385              << "vectors";
386     }
387 
388     const uint32_t plane_size = GetPlaneCoordSize(info);
389     const uint32_t dx_size = _.GetDimension(dx_type_id);
390     const uint32_t dy_size = _.GetDimension(dy_type_id);
391     if (plane_size != dx_size) {
392       return _.diag(SPV_ERROR_INVALID_DATA, inst)
393              << "Expected Image Operand Grad dx to have " << plane_size
394              << " components, but given " << dx_size;
395     }
396 
397     if (plane_size != dy_size) {
398       return _.diag(SPV_ERROR_INVALID_DATA, inst)
399              << "Expected Image Operand Grad dy to have " << plane_size
400              << " components, but given " << dy_size;
401     }
402 
403     // Multisampled is already checked.
404   }
405 
406   if (mask & uint32_t(spv::ImageOperandsMask::ConstOffset)) {
407     if (info.dim == spv::Dim::Cube) {
408       return _.diag(SPV_ERROR_INVALID_DATA, inst)
409              << "Image Operand ConstOffset cannot be used with Cube Image "
410                 "'Dim'";
411     }
412 
413     const uint32_t id = inst->word(word_index++);
414     const uint32_t type_id = _.GetTypeId(id);
415     if (!_.IsIntScalarOrVectorType(type_id)) {
416       return _.diag(SPV_ERROR_INVALID_DATA, inst)
417              << "Expected Image Operand ConstOffset to be int scalar or "
418              << "vector";
419     }
420 
421     if (!spvOpcodeIsConstant(_.GetIdOpcode(id))) {
422       return _.diag(SPV_ERROR_INVALID_DATA, inst)
423              << "Expected Image Operand ConstOffset to be a const object";
424     }
425 
426     const uint32_t plane_size = GetPlaneCoordSize(info);
427     const uint32_t offset_size = _.GetDimension(type_id);
428     if (plane_size != offset_size) {
429       return _.diag(SPV_ERROR_INVALID_DATA, inst)
430              << "Expected Image Operand ConstOffset to have " << plane_size
431              << " components, but given " << offset_size;
432     }
433   }
434 
435   if (mask & uint32_t(spv::ImageOperandsMask::Offset)) {
436     if (info.dim == spv::Dim::Cube) {
437       return _.diag(SPV_ERROR_INVALID_DATA, inst)
438              << "Image Operand Offset cannot be used with Cube Image 'Dim'";
439     }
440 
441     const uint32_t id = inst->word(word_index++);
442     const uint32_t type_id = _.GetTypeId(id);
443     if (!_.IsIntScalarOrVectorType(type_id)) {
444       return _.diag(SPV_ERROR_INVALID_DATA, inst)
445              << "Expected Image Operand Offset to be int scalar or "
446              << "vector";
447     }
448 
449     const uint32_t plane_size = GetPlaneCoordSize(info);
450     const uint32_t offset_size = _.GetDimension(type_id);
451     if (plane_size != offset_size) {
452       return _.diag(SPV_ERROR_INVALID_DATA, inst)
453              << "Expected Image Operand Offset to have " << plane_size
454              << " components, but given " << offset_size;
455     }
456 
457     if (!_.options()->before_hlsl_legalization &&
458         spvIsVulkanEnv(_.context()->target_env)) {
459       if (opcode != spv::Op::OpImageGather &&
460           opcode != spv::Op::OpImageDrefGather &&
461           opcode != spv::Op::OpImageSparseGather &&
462           opcode != spv::Op::OpImageSparseDrefGather) {
463         return _.diag(SPV_ERROR_INVALID_DATA, inst)
464                << _.VkErrorID(4663)
465                << "Image Operand Offset can only be used with "
466                   "OpImage*Gather operations";
467       }
468     }
469   }
470 
471   if (mask & uint32_t(spv::ImageOperandsMask::ConstOffsets)) {
472     if (opcode != spv::Op::OpImageGather &&
473         opcode != spv::Op::OpImageDrefGather &&
474         opcode != spv::Op::OpImageSparseGather &&
475         opcode != spv::Op::OpImageSparseDrefGather) {
476       return _.diag(SPV_ERROR_INVALID_DATA, inst)
477              << "Image Operand ConstOffsets can only be used with "
478                 "OpImageGather and OpImageDrefGather";
479     }
480 
481     if (info.dim == spv::Dim::Cube) {
482       return _.diag(SPV_ERROR_INVALID_DATA, inst)
483              << "Image Operand ConstOffsets cannot be used with Cube Image "
484                 "'Dim'";
485     }
486 
487     const uint32_t id = inst->word(word_index++);
488     const uint32_t type_id = _.GetTypeId(id);
489     const Instruction* type_inst = _.FindDef(type_id);
490     assert(type_inst);
491 
492     if (type_inst->opcode() != spv::Op::OpTypeArray) {
493       return _.diag(SPV_ERROR_INVALID_DATA, inst)
494              << "Expected Image Operand ConstOffsets to be an array of size 4";
495     }
496 
497     uint64_t array_size = 0;
498     if (!_.EvalConstantValUint64(type_inst->word(3), &array_size)) {
499       assert(0 && "Array type definition is corrupt");
500     }
501 
502     if (array_size != 4) {
503       return _.diag(SPV_ERROR_INVALID_DATA, inst)
504              << "Expected Image Operand ConstOffsets to be an array of size 4";
505     }
506 
507     const uint32_t component_type = type_inst->word(2);
508     if (!_.IsIntVectorType(component_type) ||
509         _.GetDimension(component_type) != 2) {
510       return _.diag(SPV_ERROR_INVALID_DATA, inst)
511              << "Expected Image Operand ConstOffsets array components to be "
512                 "int vectors of size 2";
513     }
514 
515     if (!spvOpcodeIsConstant(_.GetIdOpcode(id))) {
516       return _.diag(SPV_ERROR_INVALID_DATA, inst)
517              << "Expected Image Operand ConstOffsets to be a const object";
518     }
519   }
520 
521   if (mask & uint32_t(spv::ImageOperandsMask::Sample)) {
522     if (opcode != spv::Op::OpImageFetch && opcode != spv::Op::OpImageRead &&
523         opcode != spv::Op::OpImageWrite &&
524         opcode != spv::Op::OpImageSparseFetch &&
525         opcode != spv::Op::OpImageSparseRead) {
526       return _.diag(SPV_ERROR_INVALID_DATA, inst)
527              << "Image Operand Sample can only be used with OpImageFetch, "
528              << "OpImageRead, OpImageWrite, OpImageSparseFetch and "
529              << "OpImageSparseRead";
530     }
531 
532     if (info.multisampled == 0) {
533       return _.diag(SPV_ERROR_INVALID_DATA, inst)
534              << "Image Operand Sample requires non-zero 'MS' parameter";
535     }
536 
537     const uint32_t type_id = _.GetTypeId(inst->word(word_index++));
538     if (!_.IsIntScalarType(type_id)) {
539       return _.diag(SPV_ERROR_INVALID_DATA, inst)
540              << "Expected Image Operand Sample to be int scalar";
541     }
542   }
543 
544   if (mask & uint32_t(spv::ImageOperandsMask::MinLod)) {
545     if (!is_implicit_lod && !(mask & uint32_t(spv::ImageOperandsMask::Grad))) {
546       return _.diag(SPV_ERROR_INVALID_DATA, inst)
547              << "Image Operand MinLod can only be used with ImplicitLod "
548              << "opcodes or together with Image Operand Grad";
549     }
550 
551     const uint32_t type_id = _.GetTypeId(inst->word(word_index++));
552     if (!_.IsFloatScalarType(type_id)) {
553       return _.diag(SPV_ERROR_INVALID_DATA, inst)
554              << "Expected Image Operand MinLod to be float scalar";
555     }
556 
557     if (info.dim != spv::Dim::Dim1D && info.dim != spv::Dim::Dim2D &&
558         info.dim != spv::Dim::Dim3D && info.dim != spv::Dim::Cube) {
559       return _.diag(SPV_ERROR_INVALID_DATA, inst)
560              << "Image Operand MinLod requires 'Dim' parameter to be 1D, 2D, "
561                 "3D or Cube";
562     }
563 
564     if (info.multisampled != 0) {
565       return _.diag(SPV_ERROR_INVALID_DATA, inst)
566              << "Image Operand MinLod requires 'MS' parameter to be 0";
567     }
568   }
569 
570   if (mask & uint32_t(spv::ImageOperandsMask::MakeTexelAvailableKHR)) {
571     // Checked elsewhere: capability and memory model are correct.
572     if (opcode != spv::Op::OpImageWrite) {
573       return _.diag(SPV_ERROR_INVALID_DATA, inst)
574              << "Image Operand MakeTexelAvailableKHR can only be used with Op"
575              << spvOpcodeString(spv::Op::OpImageWrite) << ": Op"
576              << spvOpcodeString(opcode);
577     }
578 
579     if (!(mask & uint32_t(spv::ImageOperandsMask::NonPrivateTexelKHR))) {
580       return _.diag(SPV_ERROR_INVALID_DATA, inst)
581              << "Image Operand MakeTexelAvailableKHR requires "
582                 "NonPrivateTexelKHR is also specified: Op"
583              << spvOpcodeString(opcode);
584     }
585 
586     const auto available_scope = inst->word(word_index++);
587     if (auto error = ValidateMemoryScope(_, inst, available_scope))
588       return error;
589   }
590 
591   if (mask & uint32_t(spv::ImageOperandsMask::MakeTexelVisibleKHR)) {
592     // Checked elsewhere: capability and memory model are correct.
593     if (opcode != spv::Op::OpImageRead &&
594         opcode != spv::Op::OpImageSparseRead) {
595       return _.diag(SPV_ERROR_INVALID_DATA, inst)
596              << "Image Operand MakeTexelVisibleKHR can only be used with Op"
597              << spvOpcodeString(spv::Op::OpImageRead) << " or Op"
598              << spvOpcodeString(spv::Op::OpImageSparseRead) << ": Op"
599              << spvOpcodeString(opcode);
600     }
601 
602     if (!(mask & uint32_t(spv::ImageOperandsMask::NonPrivateTexelKHR))) {
603       return _.diag(SPV_ERROR_INVALID_DATA, inst)
604              << "Image Operand MakeTexelVisibleKHR requires NonPrivateTexelKHR "
605                 "is also specified: Op"
606              << spvOpcodeString(opcode);
607     }
608 
609     const auto visible_scope = inst->word(word_index++);
610     if (auto error = ValidateMemoryScope(_, inst, visible_scope)) return error;
611   }
612 
613   if (mask & uint32_t(spv::ImageOperandsMask::SignExtend)) {
614     // Checked elsewhere: SPIR-V 1.4 version or later.
615 
616     // "The texel value is converted to the target value via sign extension.
617     // Only valid when the texel type is a scalar or vector of integer type."
618     //
619     // We don't have enough information to know what the texel type is.
620     // In OpenCL, knowledge is deferred until runtime: the image SampledType is
621     // void, and the Format is Unknown.
622     // In Vulkan, the texel type is only known in all cases by the pipeline
623     // setup.
624   }
625 
626   if (mask & uint32_t(spv::ImageOperandsMask::ZeroExtend)) {
627     // Checked elsewhere: SPIR-V 1.4 version or later.
628 
629     // "The texel value is converted to the target value via zero extension.
630     // Only valid when the texel type is a scalar or vector of integer type."
631     //
632     // We don't have enough information to know what the texel type is.
633     // In OpenCL, knowledge is deferred until runtime: the image SampledType is
634     // void, and the Format is Unknown.
635     // In Vulkan, the texel type is only known in all cases by the pipeline
636     // setup.
637   }
638 
639   if (mask & uint32_t(spv::ImageOperandsMask::Offsets)) {
640     // TODO: add validation
641   }
642 
643   if (mask & uint32_t(spv::ImageOperandsMask::Nontemporal)) {
644     // Checked elsewhere: SPIR-V 1.6 version or later.
645   }
646 
647   return SPV_SUCCESS;
648 }
649 
650 // Validate OpImage*Proj* instructions
ValidateImageProj(ValidationState_t & _,const Instruction * inst,const ImageTypeInfo & info)651 spv_result_t ValidateImageProj(ValidationState_t& _, const Instruction* inst,
652                                const ImageTypeInfo& info) {
653   if (info.dim != spv::Dim::Dim1D && info.dim != spv::Dim::Dim2D &&
654       info.dim != spv::Dim::Dim3D && info.dim != spv::Dim::Rect) {
655     return _.diag(SPV_ERROR_INVALID_DATA, inst)
656            << "Expected Image 'Dim' parameter to be 1D, 2D, 3D or Rect";
657   }
658 
659   if (info.multisampled != 0) {
660     return _.diag(SPV_ERROR_INVALID_DATA, inst)
661            << "Expected Image 'MS' parameter to be 0";
662   }
663 
664   if (info.arrayed != 0) {
665     return _.diag(SPV_ERROR_INVALID_DATA, inst)
666            << "Expected Image 'arrayed' parameter to be 0";
667   }
668 
669   return SPV_SUCCESS;
670 }
671 
672 // Validate OpImage*Read and OpImage*Write instructions
ValidateImageReadWrite(ValidationState_t & _,const Instruction * inst,const ImageTypeInfo & info)673 spv_result_t ValidateImageReadWrite(ValidationState_t& _,
674                                     const Instruction* inst,
675                                     const ImageTypeInfo& info) {
676   if (info.sampled == 2) {
677     if (info.dim == spv::Dim::Dim1D &&
678         !_.HasCapability(spv::Capability::Image1D)) {
679       return _.diag(SPV_ERROR_INVALID_DATA, inst)
680              << "Capability Image1D is required to access storage image";
681     } else if (info.dim == spv::Dim::Rect &&
682                !_.HasCapability(spv::Capability::ImageRect)) {
683       return _.diag(SPV_ERROR_INVALID_DATA, inst)
684              << "Capability ImageRect is required to access storage image";
685     } else if (info.dim == spv::Dim::Buffer &&
686                !_.HasCapability(spv::Capability::ImageBuffer)) {
687       return _.diag(SPV_ERROR_INVALID_DATA, inst)
688              << "Capability ImageBuffer is required to access storage image";
689     } else if (info.dim == spv::Dim::Cube && info.arrayed == 1 &&
690                !_.HasCapability(spv::Capability::ImageCubeArray)) {
691       return _.diag(SPV_ERROR_INVALID_DATA, inst)
692              << "Capability ImageCubeArray is required to access "
693              << "storage image";
694     }
695 
696     if (info.multisampled == 1 && info.arrayed == 1 && info.sampled == 2 &&
697         !_.HasCapability(spv::Capability::ImageMSArray)) {
698       return _.diag(SPV_ERROR_INVALID_DATA, inst)
699              << "Capability ImageMSArray is required to access storage "
700              << "image";
701     }
702   } else if (info.sampled != 0) {
703     return _.diag(SPV_ERROR_INVALID_DATA, inst)
704            << "Expected Image 'Sampled' parameter to be 0 or 2";
705   }
706 
707   return SPV_SUCCESS;
708 }
709 
710 // Returns true if opcode is *ImageSparse*, false otherwise.
IsSparse(spv::Op opcode)711 bool IsSparse(spv::Op opcode) {
712   switch (opcode) {
713     case spv::Op::OpImageSparseSampleImplicitLod:
714     case spv::Op::OpImageSparseSampleExplicitLod:
715     case spv::Op::OpImageSparseSampleDrefImplicitLod:
716     case spv::Op::OpImageSparseSampleDrefExplicitLod:
717     case spv::Op::OpImageSparseSampleProjImplicitLod:
718     case spv::Op::OpImageSparseSampleProjExplicitLod:
719     case spv::Op::OpImageSparseSampleProjDrefImplicitLod:
720     case spv::Op::OpImageSparseSampleProjDrefExplicitLod:
721     case spv::Op::OpImageSparseFetch:
722     case spv::Op::OpImageSparseGather:
723     case spv::Op::OpImageSparseDrefGather:
724     case spv::Op::OpImageSparseTexelsResident:
725     case spv::Op::OpImageSparseRead: {
726       return true;
727     }
728 
729     default: { return false; }
730   }
731 
732   return false;
733 }
734 
735 // Checks sparse image opcode result type and returns the second struct member.
736 // Returns inst.type_id for non-sparse image opcodes.
737 // Not valid for sparse image opcodes which do not return a struct.
GetActualResultType(ValidationState_t & _,const Instruction * inst,uint32_t * actual_result_type)738 spv_result_t GetActualResultType(ValidationState_t& _, const Instruction* inst,
739                                  uint32_t* actual_result_type) {
740   const spv::Op opcode = inst->opcode();
741 
742   if (IsSparse(opcode)) {
743     const Instruction* const type_inst = _.FindDef(inst->type_id());
744     assert(type_inst);
745 
746     if (!type_inst || type_inst->opcode() != spv::Op::OpTypeStruct) {
747       return _.diag(SPV_ERROR_INVALID_DATA, inst)
748              << "Expected Result Type to be OpTypeStruct";
749     }
750 
751     if (type_inst->words().size() != 4 ||
752         !_.IsIntScalarType(type_inst->word(2))) {
753       return _.diag(SPV_ERROR_INVALID_DATA, inst)
754              << "Expected Result Type to be a struct containing an int "
755                 "scalar and a texel";
756     }
757 
758     *actual_result_type = type_inst->word(3);
759   } else {
760     *actual_result_type = inst->type_id();
761   }
762 
763   return SPV_SUCCESS;
764 }
765 
766 // Returns a string describing actual result type of an opcode.
767 // Not valid for sparse image opcodes which do not return a struct.
GetActualResultTypeStr(spv::Op opcode)768 const char* GetActualResultTypeStr(spv::Op opcode) {
769   if (IsSparse(opcode)) return "Result Type's second member";
770   return "Result Type";
771 }
772 
ValidateTypeImage(ValidationState_t & _,const Instruction * inst)773 spv_result_t ValidateTypeImage(ValidationState_t& _, const Instruction* inst) {
774   assert(inst->type_id() == 0);
775 
776   ImageTypeInfo info;
777   if (!GetImageTypeInfo(_, inst->word(1), &info)) {
778     return _.diag(SPV_ERROR_INVALID_DATA, inst)
779            << "Corrupt image type definition";
780   }
781 
782   if (_.IsIntScalarType(info.sampled_type) &&
783       (64 == _.GetBitWidth(info.sampled_type)) &&
784       !_.HasCapability(spv::Capability::Int64ImageEXT)) {
785     return _.diag(SPV_ERROR_INVALID_DATA, inst)
786            << "Capability Int64ImageEXT is required when using Sampled Type of "
787               "64-bit int";
788   }
789 
790   const auto target_env = _.context()->target_env;
791   if (spvIsVulkanEnv(target_env)) {
792     if ((!_.IsFloatScalarType(info.sampled_type) &&
793          !_.IsIntScalarType(info.sampled_type)) ||
794         ((32 != _.GetBitWidth(info.sampled_type)) &&
795          (64 != _.GetBitWidth(info.sampled_type))) ||
796         ((64 == _.GetBitWidth(info.sampled_type)) &&
797          _.IsFloatScalarType(info.sampled_type))) {
798       return _.diag(SPV_ERROR_INVALID_DATA, inst)
799              << _.VkErrorID(4656)
800              << "Expected Sampled Type to be a 32-bit int, 64-bit int or "
801                 "32-bit float scalar type for Vulkan environment";
802     }
803   } else if (spvIsOpenCLEnv(target_env)) {
804     if (!_.IsVoidType(info.sampled_type)) {
805       return _.diag(SPV_ERROR_INVALID_DATA, inst)
806              << "Sampled Type must be OpTypeVoid in the OpenCL environment.";
807     }
808   } else {
809     const spv::Op sampled_type_opcode = _.GetIdOpcode(info.sampled_type);
810     if (sampled_type_opcode != spv::Op::OpTypeVoid &&
811         sampled_type_opcode != spv::Op::OpTypeInt &&
812         sampled_type_opcode != spv::Op::OpTypeFloat) {
813       return _.diag(SPV_ERROR_INVALID_DATA, inst)
814              << "Expected Sampled Type to be either void or"
815              << " numerical scalar type";
816     }
817   }
818 
819   // Universal checks on image type operands
820   // Dim and Format and Access Qualifier are checked elsewhere.
821 
822   if (info.depth > 2) {
823     return _.diag(SPV_ERROR_INVALID_DATA, inst)
824            << "Invalid Depth " << info.depth << " (must be 0, 1 or 2)";
825   }
826 
827   if (info.arrayed > 1) {
828     return _.diag(SPV_ERROR_INVALID_DATA, inst)
829            << "Invalid Arrayed " << info.arrayed << " (must be 0 or 1)";
830   }
831 
832   if (info.multisampled > 1) {
833     return _.diag(SPV_ERROR_INVALID_DATA, inst)
834            << "Invalid MS " << info.multisampled << " (must be 0 or 1)";
835   }
836 
837   if (info.sampled > 2) {
838     return _.diag(SPV_ERROR_INVALID_DATA, inst)
839            << "Invalid Sampled " << info.sampled << " (must be 0, 1 or 2)";
840   }
841 
842   if (info.dim == spv::Dim::SubpassData) {
843     if (info.sampled != 2) {
844       return _.diag(SPV_ERROR_INVALID_DATA, inst)
845              << _.VkErrorID(6214) << "Dim SubpassData requires Sampled to be 2";
846     }
847 
848     if (info.format != spv::ImageFormat::Unknown) {
849       return _.diag(SPV_ERROR_INVALID_DATA, inst)
850              << "Dim SubpassData requires format Unknown";
851     }
852   } else if (info.dim == spv::Dim::TileImageDataEXT) {
853     if (_.IsVoidType(info.sampled_type)) {
854       return _.diag(SPV_ERROR_INVALID_DATA, inst)
855              << "Dim TileImageDataEXT requires Sampled Type to be not "
856                 "OpTypeVoid";
857     }
858     if (info.sampled != 2) {
859       return _.diag(SPV_ERROR_INVALID_DATA, inst)
860              << "Dim TileImageDataEXT requires Sampled to be 2";
861     }
862     if (info.format != spv::ImageFormat::Unknown) {
863       return _.diag(SPV_ERROR_INVALID_DATA, inst)
864              << "Dim TileImageDataEXT requires format Unknown";
865     }
866     if (info.depth != 0) {
867       return _.diag(SPV_ERROR_INVALID_DATA, inst)
868              << "Dim TileImageDataEXT requires Depth to be 0";
869     }
870     if (info.arrayed != 0) {
871       return _.diag(SPV_ERROR_INVALID_DATA, inst)
872              << "Dim TileImageDataEXT requires Arrayed to be 0";
873     }
874   } else {
875     if (info.multisampled && (info.sampled == 2) &&
876         !_.HasCapability(spv::Capability::StorageImageMultisample)) {
877       return _.diag(SPV_ERROR_INVALID_DATA, inst)
878              << "Capability StorageImageMultisample is required when using "
879                 "multisampled storage image";
880     }
881   }
882 
883   if (spvIsOpenCLEnv(target_env)) {
884     if ((info.arrayed == 1) && (info.dim != spv::Dim::Dim1D) &&
885         (info.dim != spv::Dim::Dim2D)) {
886       return _.diag(SPV_ERROR_INVALID_DATA, inst)
887              << "In the OpenCL environment, Arrayed may only be set to 1 "
888              << "when Dim is either 1D or 2D.";
889     }
890 
891     if (info.multisampled != 0) {
892       return _.diag(SPV_ERROR_INVALID_DATA, inst)
893              << "MS must be 0 in the OpenCL environment.";
894     }
895 
896     if (info.sampled != 0) {
897       return _.diag(SPV_ERROR_INVALID_DATA, inst)
898              << "Sampled must be 0 in the OpenCL environment.";
899     }
900 
901     if (info.access_qualifier == spv::AccessQualifier::Max) {
902       return _.diag(SPV_ERROR_INVALID_DATA, inst)
903              << "In the OpenCL environment, the optional Access Qualifier"
904              << " must be present.";
905     }
906   }
907 
908   if (spvIsVulkanEnv(target_env)) {
909     if (info.sampled == 0) {
910       return _.diag(SPV_ERROR_INVALID_DATA, inst)
911              << _.VkErrorID(4657)
912              << "Sampled must be 1 or 2 in the Vulkan environment.";
913     }
914 
915     if (info.dim == spv::Dim::SubpassData && info.arrayed != 0) {
916       return _.diag(SPV_ERROR_INVALID_DATA, inst)
917              << _.VkErrorID(6214)
918              << "Dim SubpassData requires Arrayed to be 0 in the Vulkan "
919                 "environment";
920     }
921 
922     if (info.dim == spv::Dim::Rect) {
923       return _.diag(SPV_ERROR_INVALID_DATA, inst)
924              << _.VkErrorID(9638)
925              << "Dim must not be Rect in the Vulkan environment";
926     }
927   }
928 
929   return SPV_SUCCESS;
930 }
931 
ValidateTypeSampledImage(ValidationState_t & _,const Instruction * inst)932 spv_result_t ValidateTypeSampledImage(ValidationState_t& _,
933                                       const Instruction* inst) {
934   const uint32_t image_type = inst->word(2);
935   if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
936     return _.diag(SPV_ERROR_INVALID_DATA, inst)
937            << "Expected Image to be of type OpTypeImage";
938   }
939 
940   ImageTypeInfo info;
941   if (!GetImageTypeInfo(_, image_type, &info)) {
942     return _.diag(SPV_ERROR_INVALID_DATA, inst)
943            << "Corrupt image type definition";
944   }
945   // OpenCL requires Sampled=0, checked elsewhere.
946   // Vulkan uses the Sampled=1 case.
947   // If Dim is TileImageDataEXT, Sampled must be 2 and this is validated
948   // elsewhere.
949   if ((info.sampled != 0) && (info.sampled != 1)) {
950     return _.diag(SPV_ERROR_INVALID_DATA, inst)
951            << _.VkErrorID(4657)
952            << "Sampled image type requires an image type with \"Sampled\" "
953               "operand set to 0 or 1";
954   }
955 
956   // This covers both OpTypeSampledImage and OpSampledImage.
957   if (_.version() >= SPV_SPIRV_VERSION_WORD(1, 6) &&
958       info.dim == spv::Dim::Buffer) {
959     return _.diag(SPV_ERROR_INVALID_ID, inst)
960            << "In SPIR-V 1.6 or later, sampled image dimension must not be "
961               "Buffer";
962   }
963 
964   return SPV_SUCCESS;
965 }
966 
IsAllowedSampledImageOperand(spv::Op opcode,ValidationState_t & _)967 bool IsAllowedSampledImageOperand(spv::Op opcode, ValidationState_t& _) {
968   switch (opcode) {
969     case spv::Op::OpSampledImage:
970     case spv::Op::OpImageSampleImplicitLod:
971     case spv::Op::OpImageSampleExplicitLod:
972     case spv::Op::OpImageSampleDrefImplicitLod:
973     case spv::Op::OpImageSampleDrefExplicitLod:
974     case spv::Op::OpImageSampleProjImplicitLod:
975     case spv::Op::OpImageSampleProjExplicitLod:
976     case spv::Op::OpImageSampleProjDrefImplicitLod:
977     case spv::Op::OpImageSampleProjDrefExplicitLod:
978     case spv::Op::OpImageGather:
979     case spv::Op::OpImageDrefGather:
980     case spv::Op::OpImage:
981     case spv::Op::OpImageQueryLod:
982     case spv::Op::OpImageSparseSampleImplicitLod:
983     case spv::Op::OpImageSparseSampleExplicitLod:
984     case spv::Op::OpImageSparseSampleDrefImplicitLod:
985     case spv::Op::OpImageSparseSampleDrefExplicitLod:
986     case spv::Op::OpImageSparseGather:
987     case spv::Op::OpImageSparseDrefGather:
988     case spv::Op::OpCopyObject:
989     case spv::Op::OpImageSampleWeightedQCOM:
990     case spv::Op::OpImageBoxFilterQCOM:
991     case spv::Op::OpImageBlockMatchSSDQCOM:
992     case spv::Op::OpImageBlockMatchSADQCOM:
993     case spv::Op::OpImageBlockMatchWindowSADQCOM:
994     case spv::Op::OpImageBlockMatchWindowSSDQCOM:
995     case spv::Op::OpImageBlockMatchGatherSADQCOM:
996     case spv::Op::OpImageBlockMatchGatherSSDQCOM:
997       return true;
998     case spv::Op::OpStore:
999       if (_.HasCapability(spv::Capability::BindlessTextureNV)) return true;
1000       return false;
1001     default:
1002       return false;
1003   }
1004 }
1005 
ValidateSampledImage(ValidationState_t & _,const Instruction * inst)1006 spv_result_t ValidateSampledImage(ValidationState_t& _,
1007                                   const Instruction* inst) {
1008   auto type_inst = _.FindDef(inst->type_id());
1009   if (type_inst->opcode() != spv::Op::OpTypeSampledImage) {
1010     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1011            << "Expected Result Type to be OpTypeSampledImage.";
1012   }
1013 
1014   const uint32_t image_type = _.GetOperandTypeId(inst, 2);
1015   if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
1016     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1017            << "Expected Image to be of type OpTypeImage.";
1018   }
1019 
1020   if (type_inst->GetOperandAs<uint32_t>(1) != image_type) {
1021     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1022            << "Expected Image to have the same type as Result Type Image";
1023   }
1024 
1025   ImageTypeInfo info;
1026   if (!GetImageTypeInfo(_, image_type, &info)) {
1027     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1028            << "Corrupt image type definition";
1029   }
1030 
1031   // TODO(atgoo@github.com) Check compatibility of result type and received
1032   // image.
1033 
1034   if (spvIsVulkanEnv(_.context()->target_env)) {
1035     if (info.sampled != 1) {
1036       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1037              << _.VkErrorID(6671)
1038              << "Expected Image 'Sampled' parameter to be 1 for Vulkan "
1039                 "environment.";
1040     }
1041   } else {
1042     if (info.sampled != 0 && info.sampled != 1) {
1043       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1044              << "Expected Image 'Sampled' parameter to be 0 or 1";
1045     }
1046   }
1047 
1048   if (info.dim == spv::Dim::SubpassData) {
1049     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1050            << "Expected Image 'Dim' parameter to be not SubpassData.";
1051   }
1052 
1053   if (_.GetIdOpcode(_.GetOperandTypeId(inst, 3)) != spv::Op::OpTypeSampler) {
1054     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1055            << "Expected Sampler to be of type OpTypeSampler";
1056   }
1057 
1058   // We need to validate 2 things:
1059   // * All OpSampledImage instructions must be in the same block in which their
1060   // Result <id> are consumed.
1061   // * Result <id> from OpSampledImage instructions must not appear as operands
1062   // to OpPhi instructions or OpSelect instructions, or any instructions other
1063   // than the image lookup and query instructions specified to take an operand
1064   // whose type is OpTypeSampledImage.
1065   std::vector<Instruction*> consumers = _.getSampledImageConsumers(inst->id());
1066   if (!consumers.empty()) {
1067     for (auto consumer_instr : consumers) {
1068       const auto consumer_opcode = consumer_instr->opcode();
1069       if (consumer_instr->block() != inst->block()) {
1070         return _.diag(SPV_ERROR_INVALID_ID, inst)
1071                << "All OpSampledImage instructions must be in the same block "
1072                   "in "
1073                   "which their Result <id> are consumed. OpSampledImage Result "
1074                   "Type <id> "
1075                << _.getIdName(inst->id())
1076                << " has a consumer in a different basic "
1077                   "block. The consumer instruction <id> is "
1078                << _.getIdName(consumer_instr->id()) << ".";
1079       }
1080 
1081       if (consumer_opcode == spv::Op::OpPhi ||
1082           consumer_opcode == spv::Op::OpSelect) {
1083         return _.diag(SPV_ERROR_INVALID_ID, inst)
1084                << "Result <id> from OpSampledImage instruction must not appear "
1085                   "as "
1086                   "operands of Op"
1087                << spvOpcodeString(static_cast<spv::Op>(consumer_opcode)) << "."
1088                << " Found result <id> " << _.getIdName(inst->id())
1089                << " as an operand of <id> " << _.getIdName(consumer_instr->id())
1090                << ".";
1091       }
1092 
1093       if (!IsAllowedSampledImageOperand(consumer_opcode, _)) {
1094         return _.diag(SPV_ERROR_INVALID_ID, inst)
1095                << "Result <id> from OpSampledImage instruction must not appear "
1096                   "as operand for Op"
1097                << spvOpcodeString(static_cast<spv::Op>(consumer_opcode))
1098                << ", since it is not specified as taking an "
1099                << "OpTypeSampledImage."
1100                << " Found result <id> " << _.getIdName(inst->id())
1101                << " as an operand of <id> " << _.getIdName(consumer_instr->id())
1102                << ".";
1103       }
1104     }
1105   }
1106 
1107   const Instruction* ld_inst;
1108   {
1109     int t_idx = inst->GetOperandAs<int>(2);
1110     ld_inst = _.FindDef(t_idx);
1111   }
1112 
1113   if (ld_inst->opcode() == spv::Op::OpLoad) {
1114     int texture_id = ld_inst->GetOperandAs<int>(2);  // variable to load
1115     _.RegisterQCOMImageProcessingTextureConsumer(texture_id, ld_inst, inst);
1116   }
1117 
1118   return SPV_SUCCESS;
1119 }
1120 
ValidateImageTexelPointer(ValidationState_t & _,const Instruction * inst)1121 spv_result_t ValidateImageTexelPointer(ValidationState_t& _,
1122                                        const Instruction* inst) {
1123   const auto result_type = _.FindDef(inst->type_id());
1124   if (result_type->opcode() != spv::Op::OpTypePointer) {
1125     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1126            << "Expected Result Type to be OpTypePointer";
1127   }
1128 
1129   const auto storage_class = result_type->GetOperandAs<spv::StorageClass>(1);
1130   if (storage_class != spv::StorageClass::Image) {
1131     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1132            << "Expected Result Type to be OpTypePointer whose Storage Class "
1133               "operand is Image";
1134   }
1135 
1136   const auto ptr_type = result_type->GetOperandAs<uint32_t>(2);
1137   const auto ptr_opcode = _.GetIdOpcode(ptr_type);
1138   if (ptr_opcode != spv::Op::OpTypeInt && ptr_opcode != spv::Op::OpTypeFloat &&
1139       ptr_opcode != spv::Op::OpTypeVoid &&
1140       !(ptr_opcode == spv::Op::OpTypeVector &&
1141         _.HasCapability(spv::Capability::AtomicFloat16VectorNV) &&
1142         _.IsFloat16Vector2Or4Type(ptr_type))) {
1143     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1144            << "Expected Result Type to be OpTypePointer whose Type operand "
1145               "must be a scalar numerical type or OpTypeVoid";
1146   }
1147 
1148   const auto image_ptr = _.FindDef(_.GetOperandTypeId(inst, 2));
1149   if (!image_ptr || image_ptr->opcode() != spv::Op::OpTypePointer) {
1150     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1151            << "Expected Image to be OpTypePointer";
1152   }
1153 
1154   const auto image_type = image_ptr->GetOperandAs<uint32_t>(2);
1155   if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
1156     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1157            << "Expected Image to be OpTypePointer with Type OpTypeImage";
1158   }
1159 
1160   ImageTypeInfo info;
1161   if (!GetImageTypeInfo(_, image_type, &info)) {
1162     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1163            << "Corrupt image type definition";
1164   }
1165 
1166   if (info.sampled_type != ptr_type &&
1167       !(_.HasCapability(spv::Capability::AtomicFloat16VectorNV) &&
1168         _.IsFloat16Vector2Or4Type(ptr_type) &&
1169         _.GetIdOpcode(info.sampled_type) == spv::Op::OpTypeFloat &&
1170         ((_.GetDimension(ptr_type) == 2 &&
1171           info.format == spv::ImageFormat::Rg16f) ||
1172          (_.GetDimension(ptr_type) == 4 &&
1173           info.format == spv::ImageFormat::Rgba16f)))) {
1174     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1175            << "Expected Image 'Sampled Type' to be the same as the Type "
1176               "pointed to by Result Type";
1177   }
1178 
1179   if (info.dim == spv::Dim::SubpassData) {
1180     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1181            << "Image Dim SubpassData cannot be used with OpImageTexelPointer";
1182   }
1183 
1184   if (info.dim == spv::Dim::TileImageDataEXT) {
1185     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1186            << "Image Dim TileImageDataEXT cannot be used with "
1187               "OpImageTexelPointer";
1188   }
1189 
1190   const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
1191   if (!coord_type || !_.IsIntScalarOrVectorType(coord_type)) {
1192     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1193            << "Expected Coordinate to be integer scalar or vector";
1194   }
1195 
1196   uint32_t expected_coord_size = 0;
1197   if (info.arrayed == 0) {
1198     expected_coord_size = GetPlaneCoordSize(info);
1199   } else if (info.arrayed == 1) {
1200     switch (info.dim) {
1201       case spv::Dim::Dim1D:
1202         expected_coord_size = 2;
1203         break;
1204       case spv::Dim::Cube:
1205       case spv::Dim::Dim2D:
1206         expected_coord_size = 3;
1207         break;
1208       default:
1209         return _.diag(SPV_ERROR_INVALID_DATA, inst)
1210                << "Expected Image 'Dim' must be one of 1D, 2D, or Cube when "
1211                   "Arrayed is 1";
1212         break;
1213     }
1214   }
1215 
1216   const uint32_t actual_coord_size = _.GetDimension(coord_type);
1217   if (expected_coord_size != actual_coord_size) {
1218     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1219            << "Expected Coordinate to have " << expected_coord_size
1220            << " components, but given " << actual_coord_size;
1221   }
1222 
1223   const uint32_t sample_type = _.GetOperandTypeId(inst, 4);
1224   if (!sample_type || !_.IsIntScalarType(sample_type)) {
1225     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1226            << "Expected Sample to be integer scalar";
1227   }
1228 
1229   if (info.multisampled == 0) {
1230     uint64_t ms = 0;
1231     if (!_.EvalConstantValUint64(inst->GetOperandAs<uint32_t>(4), &ms) ||
1232         ms != 0) {
1233       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1234              << "Expected Sample for Image with MS 0 to be a valid <id> for "
1235                 "the value 0";
1236     }
1237   }
1238 
1239   if (spvIsVulkanEnv(_.context()->target_env)) {
1240     if ((info.format != spv::ImageFormat::R64i) &&
1241         (info.format != spv::ImageFormat::R64ui) &&
1242         (info.format != spv::ImageFormat::R32f) &&
1243         (info.format != spv::ImageFormat::R32i) &&
1244         (info.format != spv::ImageFormat::R32ui) &&
1245         !((info.format == spv::ImageFormat::Rg16f ||
1246            info.format == spv::ImageFormat::Rgba16f) &&
1247           _.HasCapability(spv::Capability::AtomicFloat16VectorNV))) {
1248       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1249              << _.VkErrorID(4658)
1250              << "Expected the Image Format in Image to be R64i, R64ui, R32f, "
1251                 "R32i, or R32ui for Vulkan environment";
1252     }
1253   }
1254 
1255   return SPV_SUCCESS;
1256 }
1257 
ValidateImageLod(ValidationState_t & _,const Instruction * inst)1258 spv_result_t ValidateImageLod(ValidationState_t& _, const Instruction* inst) {
1259   const spv::Op opcode = inst->opcode();
1260   uint32_t actual_result_type = 0;
1261   if (spv_result_t error = GetActualResultType(_, inst, &actual_result_type)) {
1262     return error;
1263   }
1264 
1265   if (!_.IsIntVectorType(actual_result_type) &&
1266       !_.IsFloatVectorType(actual_result_type)) {
1267     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1268            << "Expected " << GetActualResultTypeStr(opcode)
1269            << " to be int or float vector type";
1270   }
1271 
1272   if (_.GetDimension(actual_result_type) != 4) {
1273     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1274            << "Expected " << GetActualResultTypeStr(opcode)
1275            << " to have 4 components";
1276   }
1277 
1278   const uint32_t image_type = _.GetOperandTypeId(inst, 2);
1279   if (_.GetIdOpcode(image_type) != spv::Op::OpTypeSampledImage) {
1280     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1281            << "Expected Sampled Image to be of type OpTypeSampledImage";
1282   }
1283 
1284   ImageTypeInfo info;
1285   if (!GetImageTypeInfo(_, image_type, &info)) {
1286     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1287            << "Corrupt image type definition";
1288   }
1289 
1290   if (IsProj(opcode)) {
1291     if (spv_result_t result = ValidateImageProj(_, inst, info)) return result;
1292   }
1293 
1294   if (info.multisampled) {
1295     // When using image operands, the Sample image operand is required if and
1296     // only if the image is multisampled (MS=1). The Sample image operand is
1297     // only allowed for fetch, read, and write.
1298     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1299            << "Sampling operation is invalid for multisample image";
1300   }
1301 
1302   if (_.GetIdOpcode(info.sampled_type) != spv::Op::OpTypeVoid) {
1303     const uint32_t texel_component_type =
1304         _.GetComponentType(actual_result_type);
1305     if (texel_component_type != info.sampled_type) {
1306       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1307              << "Expected Image 'Sampled Type' to be the same as "
1308              << GetActualResultTypeStr(opcode) << " components";
1309     }
1310   }
1311 
1312   const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
1313   if ((opcode == spv::Op::OpImageSampleExplicitLod ||
1314        opcode == spv::Op::OpImageSparseSampleExplicitLod) &&
1315       _.HasCapability(spv::Capability::Kernel)) {
1316     if (!_.IsFloatScalarOrVectorType(coord_type) &&
1317         !_.IsIntScalarOrVectorType(coord_type)) {
1318       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1319              << "Expected Coordinate to be int or float scalar or vector";
1320     }
1321   } else {
1322     if (!_.IsFloatScalarOrVectorType(coord_type)) {
1323       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1324              << "Expected Coordinate to be float scalar or vector";
1325     }
1326   }
1327 
1328   const uint32_t min_coord_size = GetMinCoordSize(opcode, info);
1329   const uint32_t actual_coord_size = _.GetDimension(coord_type);
1330   if (min_coord_size > actual_coord_size) {
1331     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1332            << "Expected Coordinate to have at least " << min_coord_size
1333            << " components, but given only " << actual_coord_size;
1334   }
1335 
1336   const uint32_t mask = inst->words().size() <= 5 ? 0 : inst->word(5);
1337 
1338   if (mask & uint32_t(spv::ImageOperandsMask::ConstOffset)) {
1339     if (spvIsOpenCLEnv(_.context()->target_env)) {
1340       if (opcode == spv::Op::OpImageSampleExplicitLod) {
1341         return _.diag(SPV_ERROR_INVALID_DATA, inst)
1342                << "ConstOffset image operand not allowed "
1343                << "in the OpenCL environment.";
1344       }
1345     }
1346   }
1347 
1348   if (spv_result_t result =
1349           ValidateImageOperands(_, inst, info, /* word_index = */ 6))
1350     return result;
1351 
1352   return SPV_SUCCESS;
1353 }
1354 
1355 // Validates anything OpImage*Dref* instruction
ValidateImageDref(ValidationState_t & _,const Instruction * inst,const ImageTypeInfo & info)1356 spv_result_t ValidateImageDref(ValidationState_t& _, const Instruction* inst,
1357                                const ImageTypeInfo& info) {
1358   const uint32_t dref_type = _.GetOperandTypeId(inst, 4);
1359   if (!_.IsFloatScalarType(dref_type) || _.GetBitWidth(dref_type) != 32) {
1360     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1361            << "Expected Dref to be of 32-bit float type";
1362   }
1363 
1364   if (spvIsVulkanEnv(_.context()->target_env)) {
1365     if (info.dim == spv::Dim::Dim3D) {
1366       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1367              << _.VkErrorID(4777)
1368              << "In Vulkan, OpImage*Dref* instructions must not use images "
1369                 "with a 3D Dim";
1370     }
1371   }
1372 
1373   return SPV_SUCCESS;
1374 }
1375 
ValidateImageDrefLod(ValidationState_t & _,const Instruction * inst)1376 spv_result_t ValidateImageDrefLod(ValidationState_t& _,
1377                                   const Instruction* inst) {
1378   const spv::Op opcode = inst->opcode();
1379   uint32_t actual_result_type = 0;
1380   if (spv_result_t error = GetActualResultType(_, inst, &actual_result_type)) {
1381     return error;
1382   }
1383 
1384   if (!_.IsIntScalarType(actual_result_type) &&
1385       !_.IsFloatScalarType(actual_result_type)) {
1386     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1387            << "Expected " << GetActualResultTypeStr(opcode)
1388            << " to be int or float scalar type";
1389   }
1390 
1391   const uint32_t image_type = _.GetOperandTypeId(inst, 2);
1392   if (_.GetIdOpcode(image_type) != spv::Op::OpTypeSampledImage) {
1393     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1394            << "Expected Sampled Image to be of type OpTypeSampledImage";
1395   }
1396 
1397   ImageTypeInfo info;
1398   if (!GetImageTypeInfo(_, image_type, &info)) {
1399     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1400            << "Corrupt image type definition";
1401   }
1402 
1403   if (IsProj(opcode)) {
1404     if (spv_result_t result = ValidateImageProj(_, inst, info)) return result;
1405   }
1406 
1407   if (info.multisampled) {
1408     // When using image operands, the Sample image operand is required if and
1409     // only if the image is multisampled (MS=1). The Sample image operand is
1410     // only allowed for fetch, read, and write.
1411     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1412            << "Dref sampling operation is invalid for multisample image";
1413   }
1414 
1415   if (actual_result_type != info.sampled_type) {
1416     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1417            << "Expected Image 'Sampled Type' to be the same as "
1418            << GetActualResultTypeStr(opcode);
1419   }
1420 
1421   const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
1422   if (!_.IsFloatScalarOrVectorType(coord_type)) {
1423     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1424            << "Expected Coordinate to be float scalar or vector";
1425   }
1426 
1427   const uint32_t min_coord_size = GetMinCoordSize(opcode, info);
1428   const uint32_t actual_coord_size = _.GetDimension(coord_type);
1429   if (min_coord_size > actual_coord_size) {
1430     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1431            << "Expected Coordinate to have at least " << min_coord_size
1432            << " components, but given only " << actual_coord_size;
1433   }
1434 
1435   if (spv_result_t result = ValidateImageDref(_, inst, info)) return result;
1436 
1437   if (spv_result_t result =
1438           ValidateImageOperands(_, inst, info, /* word_index = */ 7))
1439     return result;
1440 
1441   return SPV_SUCCESS;
1442 }
1443 
ValidateImageFetch(ValidationState_t & _,const Instruction * inst)1444 spv_result_t ValidateImageFetch(ValidationState_t& _, const Instruction* inst) {
1445   uint32_t actual_result_type = 0;
1446   if (spv_result_t error = GetActualResultType(_, inst, &actual_result_type)) {
1447     return error;
1448   }
1449 
1450   const spv::Op opcode = inst->opcode();
1451   if (!_.IsIntVectorType(actual_result_type) &&
1452       !_.IsFloatVectorType(actual_result_type)) {
1453     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1454            << "Expected " << GetActualResultTypeStr(opcode)
1455            << " to be int or float vector type";
1456   }
1457 
1458   if (_.GetDimension(actual_result_type) != 4) {
1459     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1460            << "Expected " << GetActualResultTypeStr(opcode)
1461            << " to have 4 components";
1462   }
1463 
1464   const uint32_t image_type = _.GetOperandTypeId(inst, 2);
1465   if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
1466     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1467            << "Expected Image to be of type OpTypeImage";
1468   }
1469 
1470   ImageTypeInfo info;
1471   if (!GetImageTypeInfo(_, image_type, &info)) {
1472     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1473            << "Corrupt image type definition";
1474   }
1475 
1476   if (_.GetIdOpcode(info.sampled_type) != spv::Op::OpTypeVoid) {
1477     const uint32_t result_component_type =
1478         _.GetComponentType(actual_result_type);
1479     if (result_component_type != info.sampled_type) {
1480       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1481              << "Expected Image 'Sampled Type' to be the same as "
1482              << GetActualResultTypeStr(opcode) << " components";
1483     }
1484   }
1485 
1486   if (info.dim == spv::Dim::Cube) {
1487     return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Image 'Dim' cannot be Cube";
1488   }
1489 
1490   if (info.sampled != 1) {
1491     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1492            << "Expected Image 'Sampled' parameter to be 1";
1493   }
1494 
1495   const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
1496   if (!_.IsIntScalarOrVectorType(coord_type)) {
1497     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1498            << "Expected Coordinate to be int scalar or vector";
1499   }
1500 
1501   const uint32_t min_coord_size = GetMinCoordSize(opcode, info);
1502   const uint32_t actual_coord_size = _.GetDimension(coord_type);
1503   if (min_coord_size > actual_coord_size) {
1504     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1505            << "Expected Coordinate to have at least " << min_coord_size
1506            << " components, but given only " << actual_coord_size;
1507   }
1508 
1509   if (spv_result_t result =
1510           ValidateImageOperands(_, inst, info, /* word_index = */ 6))
1511     return result;
1512 
1513   return SPV_SUCCESS;
1514 }
1515 
ValidateImageGather(ValidationState_t & _,const Instruction * inst)1516 spv_result_t ValidateImageGather(ValidationState_t& _,
1517                                  const Instruction* inst) {
1518   uint32_t actual_result_type = 0;
1519   if (spv_result_t error = GetActualResultType(_, inst, &actual_result_type))
1520     return error;
1521 
1522   const spv::Op opcode = inst->opcode();
1523   if (!_.IsIntVectorType(actual_result_type) &&
1524       !_.IsFloatVectorType(actual_result_type)) {
1525     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1526            << "Expected " << GetActualResultTypeStr(opcode)
1527            << " to be int or float vector type";
1528   }
1529 
1530   if (_.GetDimension(actual_result_type) != 4) {
1531     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1532            << "Expected " << GetActualResultTypeStr(opcode)
1533            << " to have 4 components";
1534   }
1535 
1536   const uint32_t image_type = _.GetOperandTypeId(inst, 2);
1537   if (_.GetIdOpcode(image_type) != spv::Op::OpTypeSampledImage) {
1538     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1539            << "Expected Sampled Image to be of type OpTypeSampledImage";
1540   }
1541 
1542   ImageTypeInfo info;
1543   if (!GetImageTypeInfo(_, image_type, &info)) {
1544     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1545            << "Corrupt image type definition";
1546   }
1547 
1548   if (info.multisampled) {
1549     // When using image operands, the Sample image operand is required if and
1550     // only if the image is multisampled (MS=1). The Sample image operand is
1551     // only allowed for fetch, read, and write.
1552     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1553            << "Gather operation is invalid for multisample image";
1554   }
1555 
1556   if (opcode == spv::Op::OpImageDrefGather ||
1557       opcode == spv::Op::OpImageSparseDrefGather ||
1558       _.GetIdOpcode(info.sampled_type) != spv::Op::OpTypeVoid) {
1559     const uint32_t result_component_type =
1560         _.GetComponentType(actual_result_type);
1561     if (result_component_type != info.sampled_type) {
1562       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1563              << "Expected Image 'Sampled Type' to be the same as "
1564              << GetActualResultTypeStr(opcode) << " components";
1565     }
1566   }
1567 
1568   if (info.dim != spv::Dim::Dim2D && info.dim != spv::Dim::Cube &&
1569       info.dim != spv::Dim::Rect) {
1570     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1571            << _.VkErrorID(4777)
1572            << "Expected Image 'Dim' to be 2D, Cube, or Rect";
1573   }
1574 
1575   const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
1576   if (!_.IsFloatScalarOrVectorType(coord_type)) {
1577     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1578            << "Expected Coordinate to be float scalar or vector";
1579   }
1580 
1581   const uint32_t min_coord_size = GetMinCoordSize(opcode, info);
1582   const uint32_t actual_coord_size = _.GetDimension(coord_type);
1583   if (min_coord_size > actual_coord_size) {
1584     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1585            << "Expected Coordinate to have at least " << min_coord_size
1586            << " components, but given only " << actual_coord_size;
1587   }
1588 
1589   if (opcode == spv::Op::OpImageGather ||
1590       opcode == spv::Op::OpImageSparseGather) {
1591     const uint32_t component = inst->GetOperandAs<uint32_t>(4);
1592     const uint32_t component_index_type = _.GetTypeId(component);
1593     if (!_.IsIntScalarType(component_index_type) ||
1594         _.GetBitWidth(component_index_type) != 32) {
1595       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1596              << "Expected Component to be 32-bit int scalar";
1597     }
1598     if (spvIsVulkanEnv(_.context()->target_env)) {
1599       if (!spvOpcodeIsConstant(_.GetIdOpcode(component))) {
1600         return _.diag(SPV_ERROR_INVALID_DATA, inst)
1601                << _.VkErrorID(4664)
1602                << "Expected Component Operand to be a const object for Vulkan "
1603                   "environment";
1604       }
1605     }
1606   } else {
1607     assert(opcode == spv::Op::OpImageDrefGather ||
1608            opcode == spv::Op::OpImageSparseDrefGather);
1609     if (spv_result_t result = ValidateImageDref(_, inst, info)) return result;
1610   }
1611 
1612   if (spv_result_t result =
1613           ValidateImageOperands(_, inst, info, /* word_index = */ 7))
1614     return result;
1615 
1616   return SPV_SUCCESS;
1617 }
1618 
ValidateImageRead(ValidationState_t & _,const Instruction * inst)1619 spv_result_t ValidateImageRead(ValidationState_t& _, const Instruction* inst) {
1620   const spv::Op opcode = inst->opcode();
1621   uint32_t actual_result_type = 0;
1622   if (spv_result_t error = GetActualResultType(_, inst, &actual_result_type)) {
1623     return error;
1624   }
1625 
1626   if (!_.IsIntScalarOrVectorType(actual_result_type) &&
1627       !_.IsFloatScalarOrVectorType(actual_result_type)) {
1628     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1629            << "Expected " << GetActualResultTypeStr(opcode)
1630            << " to be int or float scalar or vector type";
1631   }
1632 
1633   const auto target_env = _.context()->target_env;
1634   // Vulkan requires the result to be a 4-element int or float
1635   // vector.
1636   if (spvIsVulkanEnv(target_env)) {
1637     if (_.GetDimension(actual_result_type) != 4) {
1638       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1639              << _.VkErrorID(4780) << "Expected "
1640              << GetActualResultTypeStr(opcode) << " to have 4 components";
1641     }
1642   }  // Check OpenCL below, after we get the image info.
1643 
1644   const uint32_t image_type = _.GetOperandTypeId(inst, 2);
1645   if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
1646     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1647            << "Expected Image to be of type OpTypeImage";
1648   }
1649 
1650   ImageTypeInfo info;
1651   if (!GetImageTypeInfo(_, image_type, &info)) {
1652     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1653            << "Corrupt image type definition";
1654   }
1655 
1656   if (spvIsOpenCLEnv(target_env)) {
1657     // In OpenCL, a read from a depth image returns a scalar float. In other
1658     // cases, the result is always a 4-element vector.
1659     // https://www.khronos.org/registry/OpenCL/specs/3.0-unified/html/OpenCL_Env.html#_data_format_for_reading_and_writing_images
1660     // https://www.khronos.org/registry/OpenCL/specs/3.0-unified/html/OpenCL_C.html#image-read-and-write-functions
1661     // The builtins for reading depth images are:
1662     //   float read_imagef(aQual image2d_depth_t image, int2 coord)
1663     //   float read_imagef(aQual image2d_array_depth_t image, int4 coord)
1664     if (info.depth) {
1665       if (!_.IsFloatScalarType(actual_result_type)) {
1666         return _.diag(SPV_ERROR_INVALID_DATA, inst)
1667                << "Expected " << GetActualResultTypeStr(opcode)
1668                << " from a depth image read to result in a scalar float value";
1669       }
1670     } else {
1671       if (_.GetDimension(actual_result_type) != 4) {
1672         return _.diag(SPV_ERROR_INVALID_DATA, inst)
1673                << "Expected " << GetActualResultTypeStr(opcode)
1674                << " to have 4 components";
1675       }
1676     }
1677 
1678     const uint32_t mask = inst->words().size() <= 5 ? 0 : inst->word(5);
1679     if (mask & uint32_t(spv::ImageOperandsMask::ConstOffset)) {
1680       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1681              << "ConstOffset image operand not allowed "
1682              << "in the OpenCL environment.";
1683     }
1684   }
1685 
1686   if (info.dim == spv::Dim::SubpassData) {
1687     if (opcode == spv::Op::OpImageSparseRead) {
1688       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1689              << "Image Dim SubpassData cannot be used with ImageSparseRead";
1690     }
1691 
1692     _.function(inst->function()->id())
1693         ->RegisterExecutionModelLimitation(
1694             spv::ExecutionModel::Fragment,
1695             std::string("Dim SubpassData requires Fragment execution model: ") +
1696                 spvOpcodeString(opcode));
1697   }
1698 
1699   if (info.dim == spv::Dim::TileImageDataEXT) {
1700     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1701            << "Image Dim TileImageDataEXT cannot be used with "
1702            << spvOpcodeString(opcode);
1703   }
1704 
1705   if (_.GetIdOpcode(info.sampled_type) != spv::Op::OpTypeVoid) {
1706     const uint32_t result_component_type =
1707         _.GetComponentType(actual_result_type);
1708     if (result_component_type != info.sampled_type) {
1709       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1710              << "Expected Image 'Sampled Type' to be the same as "
1711              << GetActualResultTypeStr(opcode) << " components";
1712     }
1713   }
1714 
1715   if (spv_result_t result = ValidateImageReadWrite(_, inst, info))
1716     return result;
1717 
1718   const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
1719   if (!_.IsIntScalarOrVectorType(coord_type)) {
1720     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1721            << "Expected Coordinate to be int scalar or vector";
1722   }
1723 
1724   const uint32_t min_coord_size = GetMinCoordSize(opcode, info);
1725   const uint32_t actual_coord_size = _.GetDimension(coord_type);
1726   if (min_coord_size > actual_coord_size) {
1727     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1728            << "Expected Coordinate to have at least " << min_coord_size
1729            << " components, but given only " << actual_coord_size;
1730   }
1731 
1732   if (spvIsVulkanEnv(_.context()->target_env)) {
1733     if (info.format == spv::ImageFormat::Unknown &&
1734         info.dim != spv::Dim::SubpassData &&
1735         !_.HasCapability(spv::Capability::StorageImageReadWithoutFormat)) {
1736       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1737              << "Capability StorageImageReadWithoutFormat is required to "
1738              << "read storage image";
1739     }
1740   }
1741 
1742   if (spv_result_t result =
1743           ValidateImageOperands(_, inst, info, /* word_index = */ 6))
1744     return result;
1745 
1746   return SPV_SUCCESS;
1747 }
1748 
ValidateImageWrite(ValidationState_t & _,const Instruction * inst)1749 spv_result_t ValidateImageWrite(ValidationState_t& _, const Instruction* inst) {
1750   const uint32_t image_type = _.GetOperandTypeId(inst, 0);
1751   if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
1752     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1753            << "Expected Image to be of type OpTypeImage";
1754   }
1755 
1756   ImageTypeInfo info;
1757   if (!GetImageTypeInfo(_, image_type, &info)) {
1758     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1759            << "Corrupt image type definition";
1760   }
1761 
1762   if (info.dim == spv::Dim::SubpassData) {
1763     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1764            << "Image 'Dim' cannot be SubpassData";
1765   }
1766 
1767   if (info.dim == spv::Dim::TileImageDataEXT) {
1768     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1769            << "Image 'Dim' cannot be TileImageDataEXT";
1770   }
1771 
1772   if (spv_result_t result = ValidateImageReadWrite(_, inst, info))
1773     return result;
1774 
1775   const uint32_t coord_type = _.GetOperandTypeId(inst, 1);
1776   if (!_.IsIntScalarOrVectorType(coord_type)) {
1777     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1778            << "Expected Coordinate to be int scalar or vector";
1779   }
1780 
1781   const uint32_t min_coord_size = GetMinCoordSize(inst->opcode(), info);
1782   const uint32_t actual_coord_size = _.GetDimension(coord_type);
1783   if (min_coord_size > actual_coord_size) {
1784     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1785            << "Expected Coordinate to have at least " << min_coord_size
1786            << " components, but given only " << actual_coord_size;
1787   }
1788 
1789   // because it needs to match with 'Sampled Type' the Texel can't be a boolean
1790   const uint32_t texel_type = _.GetOperandTypeId(inst, 2);
1791   if (!_.IsIntScalarOrVectorType(texel_type) &&
1792       !_.IsFloatScalarOrVectorType(texel_type)) {
1793     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1794            << "Expected Texel to be int or float vector or scalar";
1795   }
1796 
1797   if (_.GetIdOpcode(info.sampled_type) != spv::Op::OpTypeVoid) {
1798     const uint32_t texel_component_type = _.GetComponentType(texel_type);
1799     if (texel_component_type != info.sampled_type) {
1800       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1801              << "Expected Image 'Sampled Type' to be the same as Texel "
1802              << "components";
1803     }
1804   }
1805 
1806   if (spvIsVulkanEnv(_.context()->target_env)) {
1807     if (info.format == spv::ImageFormat::Unknown &&
1808         info.dim != spv::Dim::SubpassData &&
1809         !_.HasCapability(spv::Capability::StorageImageWriteWithoutFormat)) {
1810       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1811              << "Capability StorageImageWriteWithoutFormat is required to "
1812                 "write "
1813              << "to storage image";
1814     }
1815   }
1816 
1817   if (inst->words().size() > 4) {
1818     if (spvIsOpenCLEnv(_.context()->target_env)) {
1819       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1820              << "Optional Image Operands are not allowed in the OpenCL "
1821              << "environment.";
1822     }
1823   }
1824 
1825   if (spv_result_t result =
1826           ValidateImageOperands(_, inst, info, /* word_index = */ 5))
1827     return result;
1828 
1829   return SPV_SUCCESS;
1830 }
1831 
ValidateImage(ValidationState_t & _,const Instruction * inst)1832 spv_result_t ValidateImage(ValidationState_t& _, const Instruction* inst) {
1833   const uint32_t result_type = inst->type_id();
1834   if (_.GetIdOpcode(result_type) != spv::Op::OpTypeImage) {
1835     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1836            << "Expected Result Type to be OpTypeImage";
1837   }
1838 
1839   const uint32_t sampled_image_type = _.GetOperandTypeId(inst, 2);
1840   const Instruction* sampled_image_type_inst = _.FindDef(sampled_image_type);
1841   assert(sampled_image_type_inst);
1842 
1843   if (sampled_image_type_inst->opcode() != spv::Op::OpTypeSampledImage) {
1844     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1845            << "Expected Sample Image to be of type OpTypeSampleImage";
1846   }
1847 
1848   if (sampled_image_type_inst->word(2) != result_type) {
1849     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1850            << "Expected Sample Image image type to be equal to Result Type";
1851   }
1852 
1853   return SPV_SUCCESS;
1854 }
1855 
ValidateImageQuerySizeLod(ValidationState_t & _,const Instruction * inst)1856 spv_result_t ValidateImageQuerySizeLod(ValidationState_t& _,
1857                                        const Instruction* inst) {
1858   const uint32_t result_type = inst->type_id();
1859   if (!_.IsIntScalarOrVectorType(result_type)) {
1860     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1861            << "Expected Result Type to be int scalar or vector type";
1862   }
1863 
1864   const uint32_t image_type = _.GetOperandTypeId(inst, 2);
1865   if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
1866     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1867            << "Expected Image to be of type OpTypeImage";
1868   }
1869 
1870   ImageTypeInfo info;
1871   if (!GetImageTypeInfo(_, image_type, &info)) {
1872     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1873            << "Corrupt image type definition";
1874   }
1875 
1876   uint32_t expected_num_components = info.arrayed;
1877   switch (info.dim) {
1878     case spv::Dim::Dim1D:
1879       expected_num_components += 1;
1880       break;
1881     case spv::Dim::Dim2D:
1882     case spv::Dim::Cube:
1883       expected_num_components += 2;
1884       break;
1885     case spv::Dim::Dim3D:
1886       expected_num_components += 3;
1887       break;
1888     default:
1889       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1890              << "Image 'Dim' must be 1D, 2D, 3D or Cube";
1891   }
1892 
1893   if (info.multisampled != 0) {
1894     return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Image 'MS' must be 0";
1895   }
1896 
1897   const auto target_env = _.context()->target_env;
1898   if (spvIsVulkanEnv(target_env)) {
1899     if (info.sampled != 1) {
1900       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1901              << _.VkErrorID(4659)
1902              << "OpImageQuerySizeLod must only consume an \"Image\" operand "
1903                 "whose type has its \"Sampled\" operand set to 1";
1904     }
1905   }
1906 
1907   uint32_t result_num_components = _.GetDimension(result_type);
1908   if (result_num_components != expected_num_components) {
1909     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1910            << "Result Type has " << result_num_components << " components, "
1911            << "but " << expected_num_components << " expected";
1912   }
1913 
1914   const uint32_t lod_type = _.GetOperandTypeId(inst, 3);
1915   if (!_.IsIntScalarType(lod_type)) {
1916     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1917            << "Expected Level of Detail to be int scalar";
1918   }
1919   return SPV_SUCCESS;
1920 }
1921 
ValidateImageQuerySize(ValidationState_t & _,const Instruction * inst)1922 spv_result_t ValidateImageQuerySize(ValidationState_t& _,
1923                                     const Instruction* inst) {
1924   const uint32_t result_type = inst->type_id();
1925   if (!_.IsIntScalarOrVectorType(result_type)) {
1926     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1927            << "Expected Result Type to be int scalar or vector type";
1928   }
1929 
1930   const uint32_t image_type = _.GetOperandTypeId(inst, 2);
1931   if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
1932     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1933            << "Expected Image to be of type OpTypeImage";
1934   }
1935 
1936   ImageTypeInfo info;
1937   if (!GetImageTypeInfo(_, image_type, &info)) {
1938     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1939            << "Corrupt image type definition";
1940   }
1941 
1942   uint32_t expected_num_components = info.arrayed;
1943   switch (info.dim) {
1944     case spv::Dim::Dim1D:
1945     case spv::Dim::Buffer:
1946       expected_num_components += 1;
1947       break;
1948     case spv::Dim::Dim2D:
1949     case spv::Dim::Cube:
1950     case spv::Dim::Rect:
1951       expected_num_components += 2;
1952       break;
1953     case spv::Dim::Dim3D:
1954       expected_num_components += 3;
1955       break;
1956     default:
1957       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1958              << "Image 'Dim' must be 1D, Buffer, 2D, Cube, 3D or Rect";
1959   }
1960 
1961   if (info.dim == spv::Dim::Dim1D || info.dim == spv::Dim::Dim2D ||
1962       info.dim == spv::Dim::Dim3D || info.dim == spv::Dim::Cube) {
1963     if (info.multisampled != 1 && info.sampled != 0 && info.sampled != 2) {
1964       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1965              << "Image must have either 'MS'=1 or 'Sampled'=0 or 'Sampled'=2";
1966     }
1967   }
1968 
1969   uint32_t result_num_components = _.GetDimension(result_type);
1970   if (result_num_components != expected_num_components) {
1971     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1972            << "Result Type has " << result_num_components << " components, "
1973            << "but " << expected_num_components << " expected";
1974   }
1975 
1976   return SPV_SUCCESS;
1977 }
1978 
ValidateImageQueryFormatOrOrder(ValidationState_t & _,const Instruction * inst)1979 spv_result_t ValidateImageQueryFormatOrOrder(ValidationState_t& _,
1980                                              const Instruction* inst) {
1981   if (!_.IsIntScalarType(inst->type_id())) {
1982     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1983            << "Expected Result Type to be int scalar type";
1984   }
1985 
1986   const uint32_t image_type = _.GetOperandTypeId(inst, 2);
1987   if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
1988     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1989            << "Expected operand to be of type OpTypeImage";
1990   }
1991 
1992   ImageTypeInfo info;
1993   if (!GetImageTypeInfo(_, image_type, &info)) {
1994     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1995            << "Corrupt image type definition";
1996   }
1997 
1998   if (info.dim == spv::Dim::TileImageDataEXT) {
1999     return _.diag(SPV_ERROR_INVALID_DATA, inst)
2000            << "Image 'Dim' cannot be TileImageDataEXT";
2001   }
2002   return SPV_SUCCESS;
2003 }
2004 
ValidateImageQueryLod(ValidationState_t & _,const Instruction * inst)2005 spv_result_t ValidateImageQueryLod(ValidationState_t& _,
2006                                    const Instruction* inst) {
2007   _.function(inst->function()->id())
2008       ->RegisterExecutionModelLimitation(
2009           [&](spv::ExecutionModel model, std::string* message) {
2010             if (model != spv::ExecutionModel::Fragment &&
2011                 model != spv::ExecutionModel::GLCompute) {
2012               if (message) {
2013                 *message = std::string(
2014                     "OpImageQueryLod requires Fragment or GLCompute execution "
2015                     "model");
2016               }
2017               return false;
2018             }
2019             return true;
2020           });
2021   _.function(inst->function()->id())
2022       ->RegisterLimitation([](const ValidationState_t& state,
2023                               const Function* entry_point,
2024                               std::string* message) {
2025         const auto* models = state.GetExecutionModels(entry_point->id());
2026         const auto* modes = state.GetExecutionModes(entry_point->id());
2027         if (models->find(spv::ExecutionModel::GLCompute) != models->end() &&
2028             modes->find(spv::ExecutionMode::DerivativeGroupLinearNV) ==
2029                 modes->end() &&
2030             modes->find(spv::ExecutionMode::DerivativeGroupQuadsNV) ==
2031                 modes->end()) {
2032           if (message) {
2033             *message = std::string(
2034                 "OpImageQueryLod requires DerivativeGroupQuadsNV "
2035                 "or DerivativeGroupLinearNV execution mode for GLCompute "
2036                 "execution model");
2037           }
2038           return false;
2039         }
2040         return true;
2041       });
2042 
2043   const uint32_t result_type = inst->type_id();
2044   if (!_.IsFloatVectorType(result_type)) {
2045     return _.diag(SPV_ERROR_INVALID_DATA, inst)
2046            << "Expected Result Type to be float vector type";
2047   }
2048 
2049   if (_.GetDimension(result_type) != 2) {
2050     return _.diag(SPV_ERROR_INVALID_DATA, inst)
2051            << "Expected Result Type to have 2 components";
2052   }
2053 
2054   const uint32_t image_type = _.GetOperandTypeId(inst, 2);
2055   if (_.GetIdOpcode(image_type) != spv::Op::OpTypeSampledImage) {
2056     return _.diag(SPV_ERROR_INVALID_DATA, inst)
2057            << "Expected Image operand to be of type OpTypeSampledImage";
2058   }
2059 
2060   ImageTypeInfo info;
2061   if (!GetImageTypeInfo(_, image_type, &info)) {
2062     return _.diag(SPV_ERROR_INVALID_DATA, inst)
2063            << "Corrupt image type definition";
2064   }
2065 
2066   if (info.dim != spv::Dim::Dim1D && info.dim != spv::Dim::Dim2D &&
2067       info.dim != spv::Dim::Dim3D && info.dim != spv::Dim::Cube) {
2068     return _.diag(SPV_ERROR_INVALID_DATA, inst)
2069            << "Image 'Dim' must be 1D, 2D, 3D or Cube";
2070   }
2071 
2072   const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
2073   if (_.HasCapability(spv::Capability::Kernel)) {
2074     if (!_.IsFloatScalarOrVectorType(coord_type) &&
2075         !_.IsIntScalarOrVectorType(coord_type)) {
2076       return _.diag(SPV_ERROR_INVALID_DATA, inst)
2077              << "Expected Coordinate to be int or float scalar or vector";
2078     }
2079   } else {
2080     if (!_.IsFloatScalarOrVectorType(coord_type)) {
2081       return _.diag(SPV_ERROR_INVALID_DATA, inst)
2082              << "Expected Coordinate to be float scalar or vector";
2083     }
2084   }
2085 
2086   const uint32_t min_coord_size = GetPlaneCoordSize(info);
2087   const uint32_t actual_coord_size = _.GetDimension(coord_type);
2088   if (min_coord_size > actual_coord_size) {
2089     return _.diag(SPV_ERROR_INVALID_DATA, inst)
2090            << "Expected Coordinate to have at least " << min_coord_size
2091            << " components, but given only " << actual_coord_size;
2092   }
2093 
2094   // The operand is a sampled image.
2095   // The sampled image type is already checked to be parameterized by an image
2096   // type with Sampled=0 or Sampled=1.  Vulkan bans Sampled=0, and so we have
2097   // Sampled=1.  So the validator already enforces Vulkan VUID 4659:
2098   //   OpImageQuerySizeLod must only consume an "Image" operand whose type has
2099   //   its "Sampled" operand set to 1
2100   return SPV_SUCCESS;
2101 }
2102 
ValidateImageSparseLod(ValidationState_t & _,const Instruction * inst)2103 spv_result_t ValidateImageSparseLod(ValidationState_t& _,
2104                                     const Instruction* inst) {
2105   return _.diag(SPV_ERROR_INVALID_DATA, inst)
2106          << "Instruction reserved for future use, use of this instruction "
2107          << "is invalid";
2108 }
2109 
ValidateImageQueryLevelsOrSamples(ValidationState_t & _,const Instruction * inst)2110 spv_result_t ValidateImageQueryLevelsOrSamples(ValidationState_t& _,
2111                                                const Instruction* inst) {
2112   if (!_.IsIntScalarType(inst->type_id())) {
2113     return _.diag(SPV_ERROR_INVALID_DATA, inst)
2114            << "Expected Result Type to be int scalar type";
2115   }
2116 
2117   const uint32_t image_type = _.GetOperandTypeId(inst, 2);
2118   if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
2119     return _.diag(SPV_ERROR_INVALID_DATA, inst)
2120            << "Expected Image to be of type OpTypeImage";
2121   }
2122 
2123   ImageTypeInfo info;
2124   if (!GetImageTypeInfo(_, image_type, &info)) {
2125     return _.diag(SPV_ERROR_INVALID_DATA, inst)
2126            << "Corrupt image type definition";
2127   }
2128 
2129   const spv::Op opcode = inst->opcode();
2130   if (opcode == spv::Op::OpImageQueryLevels) {
2131     if (info.dim != spv::Dim::Dim1D && info.dim != spv::Dim::Dim2D &&
2132         info.dim != spv::Dim::Dim3D && info.dim != spv::Dim::Cube) {
2133       return _.diag(SPV_ERROR_INVALID_DATA, inst)
2134              << "Image 'Dim' must be 1D, 2D, 3D or Cube";
2135     }
2136     const auto target_env = _.context()->target_env;
2137     if (spvIsVulkanEnv(target_env)) {
2138       if (info.sampled != 1) {
2139         return _.diag(SPV_ERROR_INVALID_DATA, inst)
2140                << _.VkErrorID(4659)
2141                << "OpImageQueryLevels must only consume an \"Image\" operand "
2142                   "whose type has its \"Sampled\" operand set to 1";
2143       }
2144     }
2145   } else {
2146     assert(opcode == spv::Op::OpImageQuerySamples);
2147     if (info.dim != spv::Dim::Dim2D) {
2148       return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Image 'Dim' must be 2D";
2149     }
2150 
2151     if (info.multisampled != 1) {
2152       return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Image 'MS' must be 1";
2153     }
2154   }
2155   return SPV_SUCCESS;
2156 }
2157 
ValidateImageSparseTexelsResident(ValidationState_t & _,const Instruction * inst)2158 spv_result_t ValidateImageSparseTexelsResident(ValidationState_t& _,
2159                                                const Instruction* inst) {
2160   if (!_.IsBoolScalarType(inst->type_id())) {
2161     return _.diag(SPV_ERROR_INVALID_DATA, inst)
2162            << "Expected Result Type to be bool scalar type";
2163   }
2164 
2165   const uint32_t resident_code_type = _.GetOperandTypeId(inst, 2);
2166   if (!_.IsIntScalarType(resident_code_type)) {
2167     return _.diag(SPV_ERROR_INVALID_DATA, inst)
2168            << "Expected Resident Code to be int scalar";
2169   }
2170 
2171   return SPV_SUCCESS;
2172 }
2173 
ValidateImageProcessingQCOMDecoration(ValidationState_t & _,int id,spv::Decoration decor)2174 spv_result_t ValidateImageProcessingQCOMDecoration(ValidationState_t& _, int id,
2175                                                    spv::Decoration decor) {
2176   const Instruction* si_inst = nullptr;
2177   const Instruction* ld_inst = _.FindDef(id);
2178   bool is_intf_obj = (ld_inst->opcode() == spv::Op::OpSampledImage);
2179   if (is_intf_obj == true) {
2180     si_inst = ld_inst;
2181     int t_idx = si_inst->GetOperandAs<int>(2);  // texture
2182     ld_inst = _.FindDef(t_idx);
2183   }
2184   if (ld_inst->opcode() != spv::Op::OpLoad) {
2185     return _.diag(SPV_ERROR_INVALID_DATA, ld_inst) << "Expect to see OpLoad";
2186   }
2187   int texture_id = ld_inst->GetOperandAs<int>(2);  // variable to load
2188   if (!_.HasDecoration(texture_id, decor)) {
2189     return _.diag(SPV_ERROR_INVALID_DATA, ld_inst)
2190            << "Missing decoration " << _.SpvDecorationString(decor);
2191   }
2192 
2193   return SPV_SUCCESS;
2194 }
2195 
ValidateImageProcessing2QCOMWindowDecoration(ValidationState_t & _,int id)2196 spv_result_t ValidateImageProcessing2QCOMWindowDecoration(ValidationState_t& _,
2197                                                           int id) {
2198   const Instruction* ld_inst = _.FindDef(id);
2199   bool is_intf_obj = (ld_inst->opcode() != spv::Op::OpSampledImage);
2200   if (is_intf_obj == true) {
2201     if (ld_inst->opcode() != spv::Op::OpLoad) {
2202       return _.diag(SPV_ERROR_INVALID_DATA, ld_inst) << "Expect to see OpLoad";
2203     }
2204     int texture_id = ld_inst->GetOperandAs<int>(2);  // variable to load
2205     spv::Decoration decor = spv::Decoration::BlockMatchTextureQCOM;
2206     if (!_.HasDecoration(texture_id, decor)) {
2207       return _.diag(SPV_ERROR_INVALID_DATA, ld_inst)
2208              << "Missing decoration " << _.SpvDecorationString(decor);
2209     }
2210     decor = spv::Decoration::BlockMatchSamplerQCOM;
2211     if (!_.HasDecoration(texture_id, decor)) {
2212       return _.diag(SPV_ERROR_INVALID_DATA, ld_inst)
2213              << "Missing decoration " << _.SpvDecorationString(decor);
2214     }
2215   } else {
2216     const Instruction* si_inst = ld_inst;
2217     int t_idx = si_inst->GetOperandAs<int>(2);  // texture
2218     const Instruction* t_ld_inst = _.FindDef(t_idx);
2219     if (t_ld_inst->opcode() != spv::Op::OpLoad) {
2220       return _.diag(SPV_ERROR_INVALID_DATA, t_ld_inst)
2221              << "Expect to see OpLoad";
2222     }
2223     int texture_id = t_ld_inst->GetOperandAs<int>(2);  // variable to load
2224     spv::Decoration decor = spv::Decoration::BlockMatchTextureQCOM;
2225     if (!_.HasDecoration(texture_id, decor)) {
2226       return _.diag(SPV_ERROR_INVALID_DATA, ld_inst)
2227              << "Missing decoration " << _.SpvDecorationString(decor);
2228     }
2229     int s_idx = si_inst->GetOperandAs<int>(3);  // sampler
2230     const Instruction* s_ld_inst = _.FindDef(s_idx);
2231     if (s_ld_inst->opcode() != spv::Op::OpLoad) {
2232       return _.diag(SPV_ERROR_INVALID_DATA, s_ld_inst)
2233              << "Expect to see OpLoad";
2234     }
2235     int sampler_id = s_ld_inst->GetOperandAs<int>(2);  // variable to load
2236     decor = spv::Decoration::BlockMatchSamplerQCOM;
2237     if (!_.HasDecoration(sampler_id, decor)) {
2238       return _.diag(SPV_ERROR_INVALID_DATA, ld_inst)
2239              << "Missing decoration " << _.SpvDecorationString(decor);
2240     }
2241   }
2242 
2243   return SPV_SUCCESS;
2244 }
2245 
ValidateImageProcessingQCOM(ValidationState_t & _,const Instruction * inst)2246 spv_result_t ValidateImageProcessingQCOM(ValidationState_t& _,
2247                                          const Instruction* inst) {
2248   spv_result_t res = SPV_SUCCESS;
2249   const spv::Op opcode = inst->opcode();
2250   switch (opcode) {
2251     case spv::Op::OpImageSampleWeightedQCOM: {
2252       int wi_idx = inst->GetOperandAs<int>(4);  // weight
2253       res = ValidateImageProcessingQCOMDecoration(
2254           _, wi_idx, spv::Decoration::WeightTextureQCOM);
2255       break;
2256     }
2257     case spv::Op::OpImageBlockMatchSSDQCOM:
2258     case spv::Op::OpImageBlockMatchSADQCOM: {
2259       int tgt_idx = inst->GetOperandAs<int>(2);  // target
2260       res = ValidateImageProcessingQCOMDecoration(
2261           _, tgt_idx, spv::Decoration::BlockMatchTextureQCOM);
2262       if (res != SPV_SUCCESS) break;
2263       int ref_idx = inst->GetOperandAs<int>(4);  // reference
2264       res = ValidateImageProcessingQCOMDecoration(
2265           _, ref_idx, spv::Decoration::BlockMatchTextureQCOM);
2266       break;
2267     }
2268     case spv::Op::OpImageBlockMatchWindowSSDQCOM:
2269     case spv::Op::OpImageBlockMatchWindowSADQCOM: {
2270       int tgt_idx = inst->GetOperandAs<int>(2);  // target
2271       res = ValidateImageProcessing2QCOMWindowDecoration(_, tgt_idx);
2272       if (res != SPV_SUCCESS) break;
2273       int ref_idx = inst->GetOperandAs<int>(4);  // reference
2274       res = ValidateImageProcessing2QCOMWindowDecoration(_, ref_idx);
2275       break;
2276     }
2277     case spv::Op::OpImageBlockMatchGatherSSDQCOM:
2278     case spv::Op::OpImageBlockMatchGatherSADQCOM: {
2279       int tgt_idx = inst->GetOperandAs<int>(2);  // target
2280       res = ValidateImageProcessingQCOMDecoration(
2281           _, tgt_idx, spv::Decoration::BlockMatchTextureQCOM);
2282       if (res != SPV_SUCCESS) break;
2283       int ref_idx = inst->GetOperandAs<int>(4);  // reference
2284       res = ValidateImageProcessingQCOMDecoration(
2285           _, ref_idx, spv::Decoration::BlockMatchTextureQCOM);
2286       break;
2287     }
2288     default:
2289       break;
2290   }
2291 
2292   return res;
2293 }
2294 
2295 }  // namespace
2296 
2297 // Validates correctness of image instructions.
ImagePass(ValidationState_t & _,const Instruction * inst)2298 spv_result_t ImagePass(ValidationState_t& _, const Instruction* inst) {
2299   const spv::Op opcode = inst->opcode();
2300   if (IsImplicitLod(opcode)) {
2301     _.function(inst->function()->id())
2302         ->RegisterExecutionModelLimitation([opcode](spv::ExecutionModel model,
2303                                                     std::string* message) {
2304           if (model != spv::ExecutionModel::Fragment &&
2305               model != spv::ExecutionModel::GLCompute) {
2306             if (message) {
2307               *message =
2308                   std::string(
2309                       "ImplicitLod instructions require Fragment or GLCompute "
2310                       "execution model: ") +
2311                   spvOpcodeString(opcode);
2312             }
2313             return false;
2314           }
2315           return true;
2316         });
2317     _.function(inst->function()->id())
2318         ->RegisterLimitation([opcode](const ValidationState_t& state,
2319                                       const Function* entry_point,
2320                                       std::string* message) {
2321           const auto* models = state.GetExecutionModels(entry_point->id());
2322           const auto* modes = state.GetExecutionModes(entry_point->id());
2323           if (models &&
2324               models->find(spv::ExecutionModel::GLCompute) != models->end() &&
2325               (!modes ||
2326                (modes->find(spv::ExecutionMode::DerivativeGroupLinearNV) ==
2327                     modes->end() &&
2328                 modes->find(spv::ExecutionMode::DerivativeGroupQuadsNV) ==
2329                     modes->end()))) {
2330             if (message) {
2331               *message =
2332                   std::string(
2333                       "ImplicitLod instructions require DerivativeGroupQuadsNV "
2334                       "or DerivativeGroupLinearNV execution mode for GLCompute "
2335                       "execution model: ") +
2336                   spvOpcodeString(opcode);
2337             }
2338             return false;
2339           }
2340           return true;
2341         });
2342   }
2343 
2344   switch (opcode) {
2345     case spv::Op::OpTypeImage:
2346       return ValidateTypeImage(_, inst);
2347     case spv::Op::OpTypeSampledImage:
2348       return ValidateTypeSampledImage(_, inst);
2349     case spv::Op::OpSampledImage:
2350       return ValidateSampledImage(_, inst);
2351     case spv::Op::OpImageTexelPointer:
2352       return ValidateImageTexelPointer(_, inst);
2353 
2354     case spv::Op::OpImageSampleImplicitLod:
2355     case spv::Op::OpImageSampleExplicitLod:
2356     case spv::Op::OpImageSampleProjImplicitLod:
2357     case spv::Op::OpImageSampleProjExplicitLod:
2358     case spv::Op::OpImageSparseSampleImplicitLod:
2359     case spv::Op::OpImageSparseSampleExplicitLod:
2360       return ValidateImageLod(_, inst);
2361 
2362     case spv::Op::OpImageSampleDrefImplicitLod:
2363     case spv::Op::OpImageSampleDrefExplicitLod:
2364     case spv::Op::OpImageSampleProjDrefImplicitLod:
2365     case spv::Op::OpImageSampleProjDrefExplicitLod:
2366     case spv::Op::OpImageSparseSampleDrefImplicitLod:
2367     case spv::Op::OpImageSparseSampleDrefExplicitLod:
2368       return ValidateImageDrefLod(_, inst);
2369 
2370     case spv::Op::OpImageFetch:
2371     case spv::Op::OpImageSparseFetch:
2372       return ValidateImageFetch(_, inst);
2373 
2374     case spv::Op::OpImageGather:
2375     case spv::Op::OpImageDrefGather:
2376     case spv::Op::OpImageSparseGather:
2377     case spv::Op::OpImageSparseDrefGather:
2378       return ValidateImageGather(_, inst);
2379 
2380     case spv::Op::OpImageRead:
2381     case spv::Op::OpImageSparseRead:
2382       return ValidateImageRead(_, inst);
2383 
2384     case spv::Op::OpImageWrite:
2385       return ValidateImageWrite(_, inst);
2386 
2387     case spv::Op::OpImage:
2388       return ValidateImage(_, inst);
2389 
2390     case spv::Op::OpImageQueryFormat:
2391     case spv::Op::OpImageQueryOrder:
2392       return ValidateImageQueryFormatOrOrder(_, inst);
2393 
2394     case spv::Op::OpImageQuerySizeLod:
2395       return ValidateImageQuerySizeLod(_, inst);
2396     case spv::Op::OpImageQuerySize:
2397       return ValidateImageQuerySize(_, inst);
2398     case spv::Op::OpImageQueryLod:
2399       return ValidateImageQueryLod(_, inst);
2400 
2401     case spv::Op::OpImageQueryLevels:
2402     case spv::Op::OpImageQuerySamples:
2403       return ValidateImageQueryLevelsOrSamples(_, inst);
2404 
2405     case spv::Op::OpImageSparseSampleProjImplicitLod:
2406     case spv::Op::OpImageSparseSampleProjExplicitLod:
2407     case spv::Op::OpImageSparseSampleProjDrefImplicitLod:
2408     case spv::Op::OpImageSparseSampleProjDrefExplicitLod:
2409       return ValidateImageSparseLod(_, inst);
2410 
2411     case spv::Op::OpImageSparseTexelsResident:
2412       return ValidateImageSparseTexelsResident(_, inst);
2413 
2414     case spv::Op::OpImageSampleWeightedQCOM:
2415     case spv::Op::OpImageBoxFilterQCOM:
2416     case spv::Op::OpImageBlockMatchSSDQCOM:
2417     case spv::Op::OpImageBlockMatchSADQCOM:
2418     case spv::Op::OpImageBlockMatchWindowSADQCOM:
2419     case spv::Op::OpImageBlockMatchWindowSSDQCOM:
2420     case spv::Op::OpImageBlockMatchGatherSADQCOM:
2421     case spv::Op::OpImageBlockMatchGatherSSDQCOM:
2422       return ValidateImageProcessingQCOM(_, inst);
2423 
2424     default:
2425       break;
2426   }
2427 
2428   return SPV_SUCCESS;
2429 }
2430 
IsImageInstruction(const spv::Op opcode)2431 bool IsImageInstruction(const spv::Op opcode) {
2432   switch (opcode) {
2433     case spv::Op::OpImageSampleImplicitLod:
2434     case spv::Op::OpImageSampleDrefImplicitLod:
2435     case spv::Op::OpImageSampleProjImplicitLod:
2436     case spv::Op::OpImageSampleProjDrefImplicitLod:
2437     case spv::Op::OpImageSparseSampleImplicitLod:
2438     case spv::Op::OpImageSparseSampleDrefImplicitLod:
2439     case spv::Op::OpImageSparseSampleProjImplicitLod:
2440     case spv::Op::OpImageSparseSampleProjDrefImplicitLod:
2441 
2442     case spv::Op::OpImageSampleExplicitLod:
2443     case spv::Op::OpImageSampleDrefExplicitLod:
2444     case spv::Op::OpImageSampleProjExplicitLod:
2445     case spv::Op::OpImageSampleProjDrefExplicitLod:
2446     case spv::Op::OpImageSparseSampleExplicitLod:
2447     case spv::Op::OpImageSparseSampleDrefExplicitLod:
2448     case spv::Op::OpImageSparseSampleProjExplicitLod:
2449     case spv::Op::OpImageSparseSampleProjDrefExplicitLod:
2450 
2451     case spv::Op::OpImage:
2452     case spv::Op::OpImageFetch:
2453     case spv::Op::OpImageSparseFetch:
2454     case spv::Op::OpImageGather:
2455     case spv::Op::OpImageDrefGather:
2456     case spv::Op::OpImageSparseGather:
2457     case spv::Op::OpImageSparseDrefGather:
2458     case spv::Op::OpImageRead:
2459     case spv::Op::OpImageSparseRead:
2460     case spv::Op::OpImageWrite:
2461 
2462     case spv::Op::OpImageQueryFormat:
2463     case spv::Op::OpImageQueryOrder:
2464     case spv::Op::OpImageQuerySizeLod:
2465     case spv::Op::OpImageQuerySize:
2466     case spv::Op::OpImageQueryLod:
2467     case spv::Op::OpImageQueryLevels:
2468     case spv::Op::OpImageQuerySamples:
2469 
2470     case spv::Op::OpImageSampleWeightedQCOM:
2471     case spv::Op::OpImageBoxFilterQCOM:
2472     case spv::Op::OpImageBlockMatchSSDQCOM:
2473     case spv::Op::OpImageBlockMatchSADQCOM:
2474     case spv::Op::OpImageBlockMatchWindowSADQCOM:
2475     case spv::Op::OpImageBlockMatchWindowSSDQCOM:
2476     case spv::Op::OpImageBlockMatchGatherSADQCOM:
2477     case spv::Op::OpImageBlockMatchGatherSSDQCOM:
2478       return true;
2479     default:
2480       break;
2481   }
2482   return false;
2483 }
2484 
ValidateQCOMImageProcessingTextureUsages(ValidationState_t & _,const Instruction * inst)2485 spv_result_t ValidateQCOMImageProcessingTextureUsages(ValidationState_t& _,
2486                                                       const Instruction* inst) {
2487   const spv::Op opcode = inst->opcode();
2488   if (!IsImageInstruction(opcode)) return SPV_SUCCESS;
2489 
2490   switch (opcode) {
2491     case spv::Op::OpImageSampleWeightedQCOM:
2492     case spv::Op::OpImageBoxFilterQCOM:
2493     case spv::Op::OpImageBlockMatchSSDQCOM:
2494     case spv::Op::OpImageBlockMatchSADQCOM:
2495       break;
2496     case spv::Op::OpImageBlockMatchWindowSADQCOM:
2497     case spv::Op::OpImageBlockMatchWindowSSDQCOM:
2498     case spv::Op::OpImageBlockMatchGatherSADQCOM:
2499     case spv::Op::OpImageBlockMatchGatherSSDQCOM:
2500       break;
2501     default:
2502       for (size_t i = 0; i < inst->operands().size(); ++i) {
2503         int id = inst->GetOperandAs<int>(i);
2504         const Instruction* operand_inst = _.FindDef(id);
2505         if (operand_inst == nullptr) continue;
2506         if (operand_inst->opcode() == spv::Op::OpLoad) {
2507           if (_.IsQCOMImageProcessingTextureConsumer(id)) {
2508             return _.diag(SPV_ERROR_INVALID_DATA, inst)
2509                    << "Illegal use of QCOM image processing decorated texture";
2510           }
2511         }
2512         if (operand_inst->opcode() == spv::Op::OpSampledImage) {
2513           if (_.IsQCOMImageProcessingTextureConsumer(id)) {
2514             return _.diag(SPV_ERROR_INVALID_DATA, inst)
2515                    << "Illegal use of QCOM image processing decorated texture";
2516           }
2517         }
2518       }
2519       break;
2520   }
2521   return SPV_SUCCESS;
2522 }
2523 
2524 }  // namespace val
2525 }  // namespace spvtools
2526