• 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 (!_.GetConstantValUint64(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) << "Dim SubpassData requires Arrayed to be 0";
918     }
919   }
920 
921   return SPV_SUCCESS;
922 }
923 
ValidateTypeSampledImage(ValidationState_t & _,const Instruction * inst)924 spv_result_t ValidateTypeSampledImage(ValidationState_t& _,
925                                       const Instruction* inst) {
926   const uint32_t image_type = inst->word(2);
927   if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
928     return _.diag(SPV_ERROR_INVALID_DATA, inst)
929            << "Expected Image to be of type OpTypeImage";
930   }
931 
932   ImageTypeInfo info;
933   if (!GetImageTypeInfo(_, image_type, &info)) {
934     return _.diag(SPV_ERROR_INVALID_DATA, inst)
935            << "Corrupt image type definition";
936   }
937   // OpenCL requires Sampled=0, checked elsewhere.
938   // Vulkan uses the Sampled=1 case.
939   // If Dim is TileImageDataEXT, Sampled must be 2 and this is validated
940   // elsewhere.
941   if ((info.sampled != 0) && (info.sampled != 1)) {
942     return _.diag(SPV_ERROR_INVALID_DATA, inst)
943            << _.VkErrorID(4657)
944            << "Sampled image type requires an image type with \"Sampled\" "
945               "operand set to 0 or 1";
946   }
947 
948   // This covers both OpTypeSampledImage and OpSampledImage.
949   if (_.version() >= SPV_SPIRV_VERSION_WORD(1, 6) &&
950       info.dim == spv::Dim::Buffer) {
951     return _.diag(SPV_ERROR_INVALID_ID, inst)
952            << "In SPIR-V 1.6 or later, sampled image dimension must not be "
953               "Buffer";
954   }
955 
956   return SPV_SUCCESS;
957 }
958 
IsAllowedSampledImageOperand(spv::Op opcode,ValidationState_t & _)959 bool IsAllowedSampledImageOperand(spv::Op opcode, ValidationState_t& _) {
960   switch (opcode) {
961     case spv::Op::OpSampledImage:
962     case spv::Op::OpImageSampleImplicitLod:
963     case spv::Op::OpImageSampleExplicitLod:
964     case spv::Op::OpImageSampleDrefImplicitLod:
965     case spv::Op::OpImageSampleDrefExplicitLod:
966     case spv::Op::OpImageSampleProjImplicitLod:
967     case spv::Op::OpImageSampleProjExplicitLod:
968     case spv::Op::OpImageSampleProjDrefImplicitLod:
969     case spv::Op::OpImageSampleProjDrefExplicitLod:
970     case spv::Op::OpImageGather:
971     case spv::Op::OpImageDrefGather:
972     case spv::Op::OpImage:
973     case spv::Op::OpImageQueryLod:
974     case spv::Op::OpImageSparseSampleImplicitLod:
975     case spv::Op::OpImageSparseSampleExplicitLod:
976     case spv::Op::OpImageSparseSampleDrefImplicitLod:
977     case spv::Op::OpImageSparseSampleDrefExplicitLod:
978     case spv::Op::OpImageSparseGather:
979     case spv::Op::OpImageSparseDrefGather:
980     case spv::Op::OpCopyObject:
981     case spv::Op::OpImageSampleWeightedQCOM:
982     case spv::Op::OpImageBoxFilterQCOM:
983     case spv::Op::OpImageBlockMatchSSDQCOM:
984     case spv::Op::OpImageBlockMatchSADQCOM:
985       return true;
986     case spv::Op::OpStore:
987       if (_.HasCapability(spv::Capability::BindlessTextureNV)) return true;
988       return false;
989     default:
990       return false;
991   }
992 }
993 
ValidateSampledImage(ValidationState_t & _,const Instruction * inst)994 spv_result_t ValidateSampledImage(ValidationState_t& _,
995                                   const Instruction* inst) {
996   if (_.GetIdOpcode(inst->type_id()) != spv::Op::OpTypeSampledImage) {
997     return _.diag(SPV_ERROR_INVALID_DATA, inst)
998            << "Expected Result Type to be OpTypeSampledImage.";
999   }
1000 
1001   const uint32_t image_type = _.GetOperandTypeId(inst, 2);
1002   if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
1003     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1004            << "Expected Image to be of type OpTypeImage.";
1005   }
1006 
1007   ImageTypeInfo info;
1008   if (!GetImageTypeInfo(_, image_type, &info)) {
1009     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1010            << "Corrupt image type definition";
1011   }
1012 
1013   // TODO(atgoo@github.com) Check compatibility of result type and received
1014   // image.
1015 
1016   if (spvIsVulkanEnv(_.context()->target_env)) {
1017     if (info.sampled != 1) {
1018       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1019              << _.VkErrorID(6671)
1020              << "Expected Image 'Sampled' parameter to be 1 for Vulkan "
1021                 "environment.";
1022     }
1023   } else {
1024     if (info.sampled != 0 && info.sampled != 1) {
1025       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1026              << "Expected Image 'Sampled' parameter to be 0 or 1";
1027     }
1028   }
1029 
1030   if (info.dim == spv::Dim::SubpassData) {
1031     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1032            << "Expected Image 'Dim' parameter to be not SubpassData.";
1033   }
1034 
1035   if (_.GetIdOpcode(_.GetOperandTypeId(inst, 3)) != spv::Op::OpTypeSampler) {
1036     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1037            << "Expected Sampler to be of type OpTypeSampler";
1038   }
1039 
1040   // We need to validate 2 things:
1041   // * All OpSampledImage instructions must be in the same block in which their
1042   // Result <id> are consumed.
1043   // * Result <id> from OpSampledImage instructions must not appear as operands
1044   // to OpPhi instructions or OpSelect instructions, or any instructions other
1045   // than the image lookup and query instructions specified to take an operand
1046   // whose type is OpTypeSampledImage.
1047   std::vector<Instruction*> consumers = _.getSampledImageConsumers(inst->id());
1048   if (!consumers.empty()) {
1049     for (auto consumer_instr : consumers) {
1050       const auto consumer_opcode = consumer_instr->opcode();
1051       if (consumer_instr->block() != inst->block()) {
1052         return _.diag(SPV_ERROR_INVALID_ID, inst)
1053                << "All OpSampledImage instructions must be in the same block "
1054                   "in "
1055                   "which their Result <id> are consumed. OpSampledImage Result "
1056                   "Type <id> "
1057                << _.getIdName(inst->id())
1058                << " has a consumer in a different basic "
1059                   "block. The consumer instruction <id> is "
1060                << _.getIdName(consumer_instr->id()) << ".";
1061       }
1062 
1063       if (consumer_opcode == spv::Op::OpPhi ||
1064           consumer_opcode == spv::Op::OpSelect) {
1065         return _.diag(SPV_ERROR_INVALID_ID, inst)
1066                << "Result <id> from OpSampledImage instruction must not appear "
1067                   "as "
1068                   "operands of Op"
1069                << spvOpcodeString(static_cast<spv::Op>(consumer_opcode)) << "."
1070                << " Found result <id> " << _.getIdName(inst->id())
1071                << " as an operand of <id> " << _.getIdName(consumer_instr->id())
1072                << ".";
1073       }
1074 
1075       if (!IsAllowedSampledImageOperand(consumer_opcode, _)) {
1076         return _.diag(SPV_ERROR_INVALID_ID, inst)
1077                << "Result <id> from OpSampledImage instruction must not appear "
1078                   "as operand for Op"
1079                << spvOpcodeString(static_cast<spv::Op>(consumer_opcode))
1080                << ", since it is not specified as taking an "
1081                << "OpTypeSampledImage."
1082                << " Found result <id> " << _.getIdName(inst->id())
1083                << " as an operand of <id> " << _.getIdName(consumer_instr->id())
1084                << ".";
1085       }
1086     }
1087   }
1088 
1089   const Instruction* ld_inst;
1090   {
1091     int t_idx = inst->GetOperandAs<int>(2);
1092     ld_inst = _.FindDef(t_idx);
1093   }
1094 
1095   if (ld_inst->opcode() == spv::Op::OpLoad) {
1096     int texture_id = ld_inst->GetOperandAs<int>(2);  // variable to load
1097     _.RegisterQCOMImageProcessingTextureConsumer(texture_id, ld_inst, inst);
1098   }
1099 
1100   return SPV_SUCCESS;
1101 }
1102 
ValidateImageTexelPointer(ValidationState_t & _,const Instruction * inst)1103 spv_result_t ValidateImageTexelPointer(ValidationState_t& _,
1104                                        const Instruction* inst) {
1105   const auto result_type = _.FindDef(inst->type_id());
1106   if (result_type->opcode() != spv::Op::OpTypePointer) {
1107     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1108            << "Expected Result Type to be OpTypePointer";
1109   }
1110 
1111   const auto storage_class = result_type->GetOperandAs<spv::StorageClass>(1);
1112   if (storage_class != spv::StorageClass::Image) {
1113     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1114            << "Expected Result Type to be OpTypePointer whose Storage Class "
1115               "operand is Image";
1116   }
1117 
1118   const auto ptr_type = result_type->GetOperandAs<uint32_t>(2);
1119   const auto ptr_opcode = _.GetIdOpcode(ptr_type);
1120   if (ptr_opcode != spv::Op::OpTypeInt && ptr_opcode != spv::Op::OpTypeFloat &&
1121       ptr_opcode != spv::Op::OpTypeVoid) {
1122     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1123            << "Expected Result Type to be OpTypePointer whose Type operand "
1124               "must be a scalar numerical type or OpTypeVoid";
1125   }
1126 
1127   const auto image_ptr = _.FindDef(_.GetOperandTypeId(inst, 2));
1128   if (!image_ptr || image_ptr->opcode() != spv::Op::OpTypePointer) {
1129     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1130            << "Expected Image to be OpTypePointer";
1131   }
1132 
1133   const auto image_type = image_ptr->GetOperandAs<uint32_t>(2);
1134   if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
1135     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1136            << "Expected Image to be OpTypePointer with Type OpTypeImage";
1137   }
1138 
1139   ImageTypeInfo info;
1140   if (!GetImageTypeInfo(_, image_type, &info)) {
1141     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1142            << "Corrupt image type definition";
1143   }
1144 
1145   if (info.sampled_type != ptr_type) {
1146     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1147            << "Expected Image 'Sampled Type' to be the same as the Type "
1148               "pointed to by Result Type";
1149   }
1150 
1151   if (info.dim == spv::Dim::SubpassData) {
1152     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1153            << "Image Dim SubpassData cannot be used with OpImageTexelPointer";
1154   }
1155 
1156   if (info.dim == spv::Dim::TileImageDataEXT) {
1157     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1158            << "Image Dim TileImageDataEXT cannot be used with "
1159               "OpImageTexelPointer";
1160   }
1161 
1162   const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
1163   if (!coord_type || !_.IsIntScalarOrVectorType(coord_type)) {
1164     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1165            << "Expected Coordinate to be integer scalar or vector";
1166   }
1167 
1168   uint32_t expected_coord_size = 0;
1169   if (info.arrayed == 0) {
1170     expected_coord_size = GetPlaneCoordSize(info);
1171   } else if (info.arrayed == 1) {
1172     switch (info.dim) {
1173       case spv::Dim::Dim1D:
1174         expected_coord_size = 2;
1175         break;
1176       case spv::Dim::Cube:
1177       case spv::Dim::Dim2D:
1178         expected_coord_size = 3;
1179         break;
1180       default:
1181         return _.diag(SPV_ERROR_INVALID_DATA, inst)
1182                << "Expected Image 'Dim' must be one of 1D, 2D, or Cube when "
1183                   "Arrayed is 1";
1184         break;
1185     }
1186   }
1187 
1188   const uint32_t actual_coord_size = _.GetDimension(coord_type);
1189   if (expected_coord_size != actual_coord_size) {
1190     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1191            << "Expected Coordinate to have " << expected_coord_size
1192            << " components, but given " << actual_coord_size;
1193   }
1194 
1195   const uint32_t sample_type = _.GetOperandTypeId(inst, 4);
1196   if (!sample_type || !_.IsIntScalarType(sample_type)) {
1197     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1198            << "Expected Sample to be integer scalar";
1199   }
1200 
1201   if (info.multisampled == 0) {
1202     uint64_t ms = 0;
1203     if (!_.GetConstantValUint64(inst->GetOperandAs<uint32_t>(4), &ms) ||
1204         ms != 0) {
1205       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1206              << "Expected Sample for Image with MS 0 to be a valid <id> for "
1207                 "the value 0";
1208     }
1209   }
1210 
1211   if (spvIsVulkanEnv(_.context()->target_env)) {
1212     if ((info.format != spv::ImageFormat::R64i) &&
1213         (info.format != spv::ImageFormat::R64ui) &&
1214         (info.format != spv::ImageFormat::R32f) &&
1215         (info.format != spv::ImageFormat::R32i) &&
1216         (info.format != spv::ImageFormat::R32ui)) {
1217       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1218              << _.VkErrorID(4658)
1219              << "Expected the Image Format in Image to be R64i, R64ui, R32f, "
1220                 "R32i, or R32ui for Vulkan environment";
1221     }
1222   }
1223 
1224   return SPV_SUCCESS;
1225 }
1226 
ValidateImageLod(ValidationState_t & _,const Instruction * inst)1227 spv_result_t ValidateImageLod(ValidationState_t& _, const Instruction* inst) {
1228   const spv::Op opcode = inst->opcode();
1229   uint32_t actual_result_type = 0;
1230   if (spv_result_t error = GetActualResultType(_, inst, &actual_result_type)) {
1231     return error;
1232   }
1233 
1234   if (!_.IsIntVectorType(actual_result_type) &&
1235       !_.IsFloatVectorType(actual_result_type)) {
1236     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1237            << "Expected " << GetActualResultTypeStr(opcode)
1238            << " to be int or float vector type";
1239   }
1240 
1241   if (_.GetDimension(actual_result_type) != 4) {
1242     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1243            << "Expected " << GetActualResultTypeStr(opcode)
1244            << " to have 4 components";
1245   }
1246 
1247   const uint32_t image_type = _.GetOperandTypeId(inst, 2);
1248   if (_.GetIdOpcode(image_type) != spv::Op::OpTypeSampledImage) {
1249     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1250            << "Expected Sampled Image to be of type OpTypeSampledImage";
1251   }
1252 
1253   ImageTypeInfo info;
1254   if (!GetImageTypeInfo(_, image_type, &info)) {
1255     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1256            << "Corrupt image type definition";
1257   }
1258 
1259   if (IsProj(opcode)) {
1260     if (spv_result_t result = ValidateImageProj(_, inst, info)) return result;
1261   }
1262 
1263   if (info.multisampled) {
1264     // When using image operands, the Sample image operand is required if and
1265     // only if the image is multisampled (MS=1). The Sample image operand is
1266     // only allowed for fetch, read, and write.
1267     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1268            << "Sampling operation is invalid for multisample image";
1269   }
1270 
1271   if (_.GetIdOpcode(info.sampled_type) != spv::Op::OpTypeVoid) {
1272     const uint32_t texel_component_type =
1273         _.GetComponentType(actual_result_type);
1274     if (texel_component_type != info.sampled_type) {
1275       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1276              << "Expected Image 'Sampled Type' to be the same as "
1277              << GetActualResultTypeStr(opcode) << " components";
1278     }
1279   }
1280 
1281   const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
1282   if ((opcode == spv::Op::OpImageSampleExplicitLod ||
1283        opcode == spv::Op::OpImageSparseSampleExplicitLod) &&
1284       _.HasCapability(spv::Capability::Kernel)) {
1285     if (!_.IsFloatScalarOrVectorType(coord_type) &&
1286         !_.IsIntScalarOrVectorType(coord_type)) {
1287       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1288              << "Expected Coordinate to be int or float scalar or vector";
1289     }
1290   } else {
1291     if (!_.IsFloatScalarOrVectorType(coord_type)) {
1292       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1293              << "Expected Coordinate to be float scalar or vector";
1294     }
1295   }
1296 
1297   const uint32_t min_coord_size = GetMinCoordSize(opcode, info);
1298   const uint32_t actual_coord_size = _.GetDimension(coord_type);
1299   if (min_coord_size > actual_coord_size) {
1300     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1301            << "Expected Coordinate to have at least " << min_coord_size
1302            << " components, but given only " << actual_coord_size;
1303   }
1304 
1305   const uint32_t mask = inst->words().size() <= 5 ? 0 : inst->word(5);
1306 
1307   if (mask & uint32_t(spv::ImageOperandsMask::ConstOffset)) {
1308     if (spvIsOpenCLEnv(_.context()->target_env)) {
1309       if (opcode == spv::Op::OpImageSampleExplicitLod) {
1310         return _.diag(SPV_ERROR_INVALID_DATA, inst)
1311                << "ConstOffset image operand not allowed "
1312                << "in the OpenCL environment.";
1313       }
1314     }
1315   }
1316 
1317   if (spv_result_t result =
1318           ValidateImageOperands(_, inst, info, /* word_index = */ 6))
1319     return result;
1320 
1321   return SPV_SUCCESS;
1322 }
1323 
1324 // Validates anything OpImage*Dref* instruction
ValidateImageDref(ValidationState_t & _,const Instruction * inst,const ImageTypeInfo & info)1325 spv_result_t ValidateImageDref(ValidationState_t& _, const Instruction* inst,
1326                                const ImageTypeInfo& info) {
1327   const uint32_t dref_type = _.GetOperandTypeId(inst, 4);
1328   if (!_.IsFloatScalarType(dref_type) || _.GetBitWidth(dref_type) != 32) {
1329     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1330            << "Expected Dref to be of 32-bit float type";
1331   }
1332 
1333   if (spvIsVulkanEnv(_.context()->target_env)) {
1334     if (info.dim == spv::Dim::Dim3D) {
1335       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1336              << _.VkErrorID(4777)
1337              << "In Vulkan, OpImage*Dref* instructions must not use images "
1338                 "with a 3D Dim";
1339     }
1340   }
1341 
1342   return SPV_SUCCESS;
1343 }
1344 
ValidateImageDrefLod(ValidationState_t & _,const Instruction * inst)1345 spv_result_t ValidateImageDrefLod(ValidationState_t& _,
1346                                   const Instruction* inst) {
1347   const spv::Op opcode = inst->opcode();
1348   uint32_t actual_result_type = 0;
1349   if (spv_result_t error = GetActualResultType(_, inst, &actual_result_type)) {
1350     return error;
1351   }
1352 
1353   if (!_.IsIntScalarType(actual_result_type) &&
1354       !_.IsFloatScalarType(actual_result_type)) {
1355     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1356            << "Expected " << GetActualResultTypeStr(opcode)
1357            << " to be int or float scalar type";
1358   }
1359 
1360   const uint32_t image_type = _.GetOperandTypeId(inst, 2);
1361   if (_.GetIdOpcode(image_type) != spv::Op::OpTypeSampledImage) {
1362     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1363            << "Expected Sampled Image to be of type OpTypeSampledImage";
1364   }
1365 
1366   ImageTypeInfo info;
1367   if (!GetImageTypeInfo(_, image_type, &info)) {
1368     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1369            << "Corrupt image type definition";
1370   }
1371 
1372   if (IsProj(opcode)) {
1373     if (spv_result_t result = ValidateImageProj(_, inst, info)) return result;
1374   }
1375 
1376   if (info.multisampled) {
1377     // When using image operands, the Sample image operand is required if and
1378     // only if the image is multisampled (MS=1). The Sample image operand is
1379     // only allowed for fetch, read, and write.
1380     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1381            << "Dref sampling operation is invalid for multisample image";
1382   }
1383 
1384   if (actual_result_type != info.sampled_type) {
1385     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1386            << "Expected Image 'Sampled Type' to be the same as "
1387            << GetActualResultTypeStr(opcode);
1388   }
1389 
1390   const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
1391   if (!_.IsFloatScalarOrVectorType(coord_type)) {
1392     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1393            << "Expected Coordinate to be float scalar or vector";
1394   }
1395 
1396   const uint32_t min_coord_size = GetMinCoordSize(opcode, info);
1397   const uint32_t actual_coord_size = _.GetDimension(coord_type);
1398   if (min_coord_size > actual_coord_size) {
1399     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1400            << "Expected Coordinate to have at least " << min_coord_size
1401            << " components, but given only " << actual_coord_size;
1402   }
1403 
1404   if (spv_result_t result = ValidateImageDref(_, inst, info)) return result;
1405 
1406   if (spv_result_t result =
1407           ValidateImageOperands(_, inst, info, /* word_index = */ 7))
1408     return result;
1409 
1410   return SPV_SUCCESS;
1411 }
1412 
ValidateImageFetch(ValidationState_t & _,const Instruction * inst)1413 spv_result_t ValidateImageFetch(ValidationState_t& _, const Instruction* inst) {
1414   uint32_t actual_result_type = 0;
1415   if (spv_result_t error = GetActualResultType(_, inst, &actual_result_type)) {
1416     return error;
1417   }
1418 
1419   const spv::Op opcode = inst->opcode();
1420   if (!_.IsIntVectorType(actual_result_type) &&
1421       !_.IsFloatVectorType(actual_result_type)) {
1422     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1423            << "Expected " << GetActualResultTypeStr(opcode)
1424            << " to be int or float vector type";
1425   }
1426 
1427   if (_.GetDimension(actual_result_type) != 4) {
1428     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1429            << "Expected " << GetActualResultTypeStr(opcode)
1430            << " to have 4 components";
1431   }
1432 
1433   const uint32_t image_type = _.GetOperandTypeId(inst, 2);
1434   if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
1435     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1436            << "Expected Image to be of type OpTypeImage";
1437   }
1438 
1439   ImageTypeInfo info;
1440   if (!GetImageTypeInfo(_, image_type, &info)) {
1441     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1442            << "Corrupt image type definition";
1443   }
1444 
1445   if (_.GetIdOpcode(info.sampled_type) != spv::Op::OpTypeVoid) {
1446     const uint32_t result_component_type =
1447         _.GetComponentType(actual_result_type);
1448     if (result_component_type != info.sampled_type) {
1449       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1450              << "Expected Image 'Sampled Type' to be the same as "
1451              << GetActualResultTypeStr(opcode) << " components";
1452     }
1453   }
1454 
1455   if (info.dim == spv::Dim::Cube) {
1456     return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Image 'Dim' cannot be Cube";
1457   }
1458 
1459   if (info.sampled != 1) {
1460     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1461            << "Expected Image 'Sampled' parameter to be 1";
1462   }
1463 
1464   const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
1465   if (!_.IsIntScalarOrVectorType(coord_type)) {
1466     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1467            << "Expected Coordinate to be int scalar or vector";
1468   }
1469 
1470   const uint32_t min_coord_size = GetMinCoordSize(opcode, info);
1471   const uint32_t actual_coord_size = _.GetDimension(coord_type);
1472   if (min_coord_size > actual_coord_size) {
1473     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1474            << "Expected Coordinate to have at least " << min_coord_size
1475            << " components, but given only " << actual_coord_size;
1476   }
1477 
1478   if (spv_result_t result =
1479           ValidateImageOperands(_, inst, info, /* word_index = */ 6))
1480     return result;
1481 
1482   return SPV_SUCCESS;
1483 }
1484 
ValidateImageGather(ValidationState_t & _,const Instruction * inst)1485 spv_result_t ValidateImageGather(ValidationState_t& _,
1486                                  const Instruction* inst) {
1487   uint32_t actual_result_type = 0;
1488   if (spv_result_t error = GetActualResultType(_, inst, &actual_result_type))
1489     return error;
1490 
1491   const spv::Op opcode = inst->opcode();
1492   if (!_.IsIntVectorType(actual_result_type) &&
1493       !_.IsFloatVectorType(actual_result_type)) {
1494     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1495            << "Expected " << GetActualResultTypeStr(opcode)
1496            << " to be int or float vector type";
1497   }
1498 
1499   if (_.GetDimension(actual_result_type) != 4) {
1500     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1501            << "Expected " << GetActualResultTypeStr(opcode)
1502            << " to have 4 components";
1503   }
1504 
1505   const uint32_t image_type = _.GetOperandTypeId(inst, 2);
1506   if (_.GetIdOpcode(image_type) != spv::Op::OpTypeSampledImage) {
1507     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1508            << "Expected Sampled Image to be of type OpTypeSampledImage";
1509   }
1510 
1511   ImageTypeInfo info;
1512   if (!GetImageTypeInfo(_, image_type, &info)) {
1513     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1514            << "Corrupt image type definition";
1515   }
1516 
1517   if (info.multisampled) {
1518     // When using image operands, the Sample image operand is required if and
1519     // only if the image is multisampled (MS=1). The Sample image operand is
1520     // only allowed for fetch, read, and write.
1521     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1522            << "Gather operation is invalid for multisample image";
1523   }
1524 
1525   if (opcode == spv::Op::OpImageDrefGather ||
1526       opcode == spv::Op::OpImageSparseDrefGather ||
1527       _.GetIdOpcode(info.sampled_type) != spv::Op::OpTypeVoid) {
1528     const uint32_t result_component_type =
1529         _.GetComponentType(actual_result_type);
1530     if (result_component_type != info.sampled_type) {
1531       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1532              << "Expected Image 'Sampled Type' to be the same as "
1533              << GetActualResultTypeStr(opcode) << " components";
1534     }
1535   }
1536 
1537   if (info.dim != spv::Dim::Dim2D && info.dim != spv::Dim::Cube &&
1538       info.dim != spv::Dim::Rect) {
1539     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1540            << _.VkErrorID(4777)
1541            << "Expected Image 'Dim' to be 2D, Cube, or Rect";
1542   }
1543 
1544   const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
1545   if (!_.IsFloatScalarOrVectorType(coord_type)) {
1546     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1547            << "Expected Coordinate to be float scalar or vector";
1548   }
1549 
1550   const uint32_t min_coord_size = GetMinCoordSize(opcode, info);
1551   const uint32_t actual_coord_size = _.GetDimension(coord_type);
1552   if (min_coord_size > actual_coord_size) {
1553     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1554            << "Expected Coordinate to have at least " << min_coord_size
1555            << " components, but given only " << actual_coord_size;
1556   }
1557 
1558   if (opcode == spv::Op::OpImageGather ||
1559       opcode == spv::Op::OpImageSparseGather) {
1560     const uint32_t component = inst->GetOperandAs<uint32_t>(4);
1561     const uint32_t component_index_type = _.GetTypeId(component);
1562     if (!_.IsIntScalarType(component_index_type) ||
1563         _.GetBitWidth(component_index_type) != 32) {
1564       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1565              << "Expected Component to be 32-bit int scalar";
1566     }
1567     if (spvIsVulkanEnv(_.context()->target_env)) {
1568       if (!spvOpcodeIsConstant(_.GetIdOpcode(component))) {
1569         return _.diag(SPV_ERROR_INVALID_DATA, inst)
1570                << _.VkErrorID(4664)
1571                << "Expected Component Operand to be a const object for Vulkan "
1572                   "environment";
1573       }
1574     }
1575   } else {
1576     assert(opcode == spv::Op::OpImageDrefGather ||
1577            opcode == spv::Op::OpImageSparseDrefGather);
1578     if (spv_result_t result = ValidateImageDref(_, inst, info)) return result;
1579   }
1580 
1581   if (spv_result_t result =
1582           ValidateImageOperands(_, inst, info, /* word_index = */ 7))
1583     return result;
1584 
1585   return SPV_SUCCESS;
1586 }
1587 
ValidateImageRead(ValidationState_t & _,const Instruction * inst)1588 spv_result_t ValidateImageRead(ValidationState_t& _, const Instruction* inst) {
1589   const spv::Op opcode = inst->opcode();
1590   uint32_t actual_result_type = 0;
1591   if (spv_result_t error = GetActualResultType(_, inst, &actual_result_type)) {
1592     return error;
1593   }
1594 
1595   if (!_.IsIntScalarOrVectorType(actual_result_type) &&
1596       !_.IsFloatScalarOrVectorType(actual_result_type)) {
1597     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1598            << "Expected " << GetActualResultTypeStr(opcode)
1599            << " to be int or float scalar or vector type";
1600   }
1601 
1602   const auto target_env = _.context()->target_env;
1603   // Vulkan requires the result to be a 4-element int or float
1604   // vector.
1605   if (spvIsVulkanEnv(target_env)) {
1606     if (_.GetDimension(actual_result_type) != 4) {
1607       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1608              << _.VkErrorID(4780) << "Expected "
1609              << GetActualResultTypeStr(opcode) << " to have 4 components";
1610     }
1611   }  // Check OpenCL below, after we get the image info.
1612 
1613   const uint32_t image_type = _.GetOperandTypeId(inst, 2);
1614   if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
1615     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1616            << "Expected Image to be of type OpTypeImage";
1617   }
1618 
1619   ImageTypeInfo info;
1620   if (!GetImageTypeInfo(_, image_type, &info)) {
1621     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1622            << "Corrupt image type definition";
1623   }
1624 
1625   if (spvIsOpenCLEnv(target_env)) {
1626     // In OpenCL, a read from a depth image returns a scalar float. In other
1627     // cases, the result is always a 4-element vector.
1628     // https://www.khronos.org/registry/OpenCL/specs/3.0-unified/html/OpenCL_Env.html#_data_format_for_reading_and_writing_images
1629     // https://www.khronos.org/registry/OpenCL/specs/3.0-unified/html/OpenCL_C.html#image-read-and-write-functions
1630     // The builtins for reading depth images are:
1631     //   float read_imagef(aQual image2d_depth_t image, int2 coord)
1632     //   float read_imagef(aQual image2d_array_depth_t image, int4 coord)
1633     if (info.depth) {
1634       if (!_.IsFloatScalarType(actual_result_type)) {
1635         return _.diag(SPV_ERROR_INVALID_DATA, inst)
1636                << "Expected " << GetActualResultTypeStr(opcode)
1637                << " from a depth image read to result in a scalar float value";
1638       }
1639     } else {
1640       if (_.GetDimension(actual_result_type) != 4) {
1641         return _.diag(SPV_ERROR_INVALID_DATA, inst)
1642                << "Expected " << GetActualResultTypeStr(opcode)
1643                << " to have 4 components";
1644       }
1645     }
1646 
1647     const uint32_t mask = inst->words().size() <= 5 ? 0 : inst->word(5);
1648     if (mask & uint32_t(spv::ImageOperandsMask::ConstOffset)) {
1649       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1650              << "ConstOffset image operand not allowed "
1651              << "in the OpenCL environment.";
1652     }
1653   }
1654 
1655   if (info.dim == spv::Dim::SubpassData) {
1656     if (opcode == spv::Op::OpImageSparseRead) {
1657       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1658              << "Image Dim SubpassData cannot be used with ImageSparseRead";
1659     }
1660 
1661     _.function(inst->function()->id())
1662         ->RegisterExecutionModelLimitation(
1663             spv::ExecutionModel::Fragment,
1664             std::string("Dim SubpassData requires Fragment execution model: ") +
1665                 spvOpcodeString(opcode));
1666   }
1667 
1668   if (info.dim == spv::Dim::TileImageDataEXT) {
1669     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1670            << "Image Dim TileImageDataEXT cannot be used with "
1671            << spvOpcodeString(opcode);
1672   }
1673 
1674   if (_.GetIdOpcode(info.sampled_type) != spv::Op::OpTypeVoid) {
1675     const uint32_t result_component_type =
1676         _.GetComponentType(actual_result_type);
1677     if (result_component_type != info.sampled_type) {
1678       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1679              << "Expected Image 'Sampled Type' to be the same as "
1680              << GetActualResultTypeStr(opcode) << " components";
1681     }
1682   }
1683 
1684   if (spv_result_t result = ValidateImageReadWrite(_, inst, info))
1685     return result;
1686 
1687   const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
1688   if (!_.IsIntScalarOrVectorType(coord_type)) {
1689     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1690            << "Expected Coordinate to be int scalar or vector";
1691   }
1692 
1693   const uint32_t min_coord_size = GetMinCoordSize(opcode, info);
1694   const uint32_t actual_coord_size = _.GetDimension(coord_type);
1695   if (min_coord_size > actual_coord_size) {
1696     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1697            << "Expected Coordinate to have at least " << min_coord_size
1698            << " components, but given only " << actual_coord_size;
1699   }
1700 
1701   if (spvIsVulkanEnv(_.context()->target_env)) {
1702     if (info.format == spv::ImageFormat::Unknown &&
1703         info.dim != spv::Dim::SubpassData &&
1704         !_.HasCapability(spv::Capability::StorageImageReadWithoutFormat)) {
1705       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1706              << "Capability StorageImageReadWithoutFormat is required to "
1707              << "read storage image";
1708     }
1709   }
1710 
1711   if (spv_result_t result =
1712           ValidateImageOperands(_, inst, info, /* word_index = */ 6))
1713     return result;
1714 
1715   return SPV_SUCCESS;
1716 }
1717 
ValidateImageWrite(ValidationState_t & _,const Instruction * inst)1718 spv_result_t ValidateImageWrite(ValidationState_t& _, const Instruction* inst) {
1719   const uint32_t image_type = _.GetOperandTypeId(inst, 0);
1720   if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
1721     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1722            << "Expected Image to be of type OpTypeImage";
1723   }
1724 
1725   ImageTypeInfo info;
1726   if (!GetImageTypeInfo(_, image_type, &info)) {
1727     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1728            << "Corrupt image type definition";
1729   }
1730 
1731   if (info.dim == spv::Dim::SubpassData) {
1732     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1733            << "Image 'Dim' cannot be SubpassData";
1734   }
1735 
1736   if (info.dim == spv::Dim::TileImageDataEXT) {
1737     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1738            << "Image 'Dim' cannot be TileImageDataEXT";
1739   }
1740 
1741   if (spv_result_t result = ValidateImageReadWrite(_, inst, info))
1742     return result;
1743 
1744   const uint32_t coord_type = _.GetOperandTypeId(inst, 1);
1745   if (!_.IsIntScalarOrVectorType(coord_type)) {
1746     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1747            << "Expected Coordinate to be int scalar or vector";
1748   }
1749 
1750   const uint32_t min_coord_size = GetMinCoordSize(inst->opcode(), info);
1751   const uint32_t actual_coord_size = _.GetDimension(coord_type);
1752   if (min_coord_size > actual_coord_size) {
1753     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1754            << "Expected Coordinate to have at least " << min_coord_size
1755            << " components, but given only " << actual_coord_size;
1756   }
1757 
1758   // because it needs to match with 'Sampled Type' the Texel can't be a boolean
1759   const uint32_t texel_type = _.GetOperandTypeId(inst, 2);
1760   if (!_.IsIntScalarOrVectorType(texel_type) &&
1761       !_.IsFloatScalarOrVectorType(texel_type)) {
1762     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1763            << "Expected Texel to be int or float vector or scalar";
1764   }
1765 
1766   if (_.GetIdOpcode(info.sampled_type) != spv::Op::OpTypeVoid) {
1767     const uint32_t texel_component_type = _.GetComponentType(texel_type);
1768     if (texel_component_type != info.sampled_type) {
1769       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1770              << "Expected Image 'Sampled Type' to be the same as Texel "
1771              << "components";
1772     }
1773   }
1774 
1775   if (spvIsVulkanEnv(_.context()->target_env)) {
1776     if (info.format == spv::ImageFormat::Unknown &&
1777         info.dim != spv::Dim::SubpassData &&
1778         !_.HasCapability(spv::Capability::StorageImageWriteWithoutFormat)) {
1779       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1780              << "Capability StorageImageWriteWithoutFormat is required to "
1781                 "write "
1782              << "to storage image";
1783     }
1784   }
1785 
1786   if (inst->words().size() > 4) {
1787     if (spvIsOpenCLEnv(_.context()->target_env)) {
1788       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1789              << "Optional Image Operands are not allowed in the OpenCL "
1790              << "environment.";
1791     }
1792   }
1793 
1794   if (spv_result_t result =
1795           ValidateImageOperands(_, inst, info, /* word_index = */ 5))
1796     return result;
1797 
1798   return SPV_SUCCESS;
1799 }
1800 
ValidateImage(ValidationState_t & _,const Instruction * inst)1801 spv_result_t ValidateImage(ValidationState_t& _, const Instruction* inst) {
1802   const uint32_t result_type = inst->type_id();
1803   if (_.GetIdOpcode(result_type) != spv::Op::OpTypeImage) {
1804     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1805            << "Expected Result Type to be OpTypeImage";
1806   }
1807 
1808   const uint32_t sampled_image_type = _.GetOperandTypeId(inst, 2);
1809   const Instruction* sampled_image_type_inst = _.FindDef(sampled_image_type);
1810   assert(sampled_image_type_inst);
1811 
1812   if (sampled_image_type_inst->opcode() != spv::Op::OpTypeSampledImage) {
1813     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1814            << "Expected Sample Image to be of type OpTypeSampleImage";
1815   }
1816 
1817   if (sampled_image_type_inst->word(2) != result_type) {
1818     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1819            << "Expected Sample Image image type to be equal to Result Type";
1820   }
1821 
1822   return SPV_SUCCESS;
1823 }
1824 
ValidateImageQuerySizeLod(ValidationState_t & _,const Instruction * inst)1825 spv_result_t ValidateImageQuerySizeLod(ValidationState_t& _,
1826                                        const Instruction* inst) {
1827   const uint32_t result_type = inst->type_id();
1828   if (!_.IsIntScalarOrVectorType(result_type)) {
1829     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1830            << "Expected Result Type to be int scalar or vector type";
1831   }
1832 
1833   const uint32_t image_type = _.GetOperandTypeId(inst, 2);
1834   if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
1835     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1836            << "Expected Image to be of type OpTypeImage";
1837   }
1838 
1839   ImageTypeInfo info;
1840   if (!GetImageTypeInfo(_, image_type, &info)) {
1841     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1842            << "Corrupt image type definition";
1843   }
1844 
1845   uint32_t expected_num_components = info.arrayed;
1846   switch (info.dim) {
1847     case spv::Dim::Dim1D:
1848       expected_num_components += 1;
1849       break;
1850     case spv::Dim::Dim2D:
1851     case spv::Dim::Cube:
1852       expected_num_components += 2;
1853       break;
1854     case spv::Dim::Dim3D:
1855       expected_num_components += 3;
1856       break;
1857     default:
1858       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1859              << "Image 'Dim' must be 1D, 2D, 3D or Cube";
1860   }
1861 
1862   if (info.multisampled != 0) {
1863     return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Image 'MS' must be 0";
1864   }
1865 
1866   const auto target_env = _.context()->target_env;
1867   if (spvIsVulkanEnv(target_env)) {
1868     if (info.sampled != 1) {
1869       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1870              << _.VkErrorID(4659)
1871              << "OpImageQuerySizeLod must only consume an \"Image\" operand "
1872                 "whose type has its \"Sampled\" operand set to 1";
1873     }
1874   }
1875 
1876   uint32_t result_num_components = _.GetDimension(result_type);
1877   if (result_num_components != expected_num_components) {
1878     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1879            << "Result Type has " << result_num_components << " components, "
1880            << "but " << expected_num_components << " expected";
1881   }
1882 
1883   const uint32_t lod_type = _.GetOperandTypeId(inst, 3);
1884   if (!_.IsIntScalarType(lod_type)) {
1885     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1886            << "Expected Level of Detail to be int scalar";
1887   }
1888   return SPV_SUCCESS;
1889 }
1890 
ValidateImageQuerySize(ValidationState_t & _,const Instruction * inst)1891 spv_result_t ValidateImageQuerySize(ValidationState_t& _,
1892                                     const Instruction* inst) {
1893   const uint32_t result_type = inst->type_id();
1894   if (!_.IsIntScalarOrVectorType(result_type)) {
1895     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1896            << "Expected Result Type to be int scalar or vector type";
1897   }
1898 
1899   const uint32_t image_type = _.GetOperandTypeId(inst, 2);
1900   if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
1901     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1902            << "Expected Image to be of type OpTypeImage";
1903   }
1904 
1905   ImageTypeInfo info;
1906   if (!GetImageTypeInfo(_, image_type, &info)) {
1907     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1908            << "Corrupt image type definition";
1909   }
1910 
1911   uint32_t expected_num_components = info.arrayed;
1912   switch (info.dim) {
1913     case spv::Dim::Dim1D:
1914     case spv::Dim::Buffer:
1915       expected_num_components += 1;
1916       break;
1917     case spv::Dim::Dim2D:
1918     case spv::Dim::Cube:
1919     case spv::Dim::Rect:
1920       expected_num_components += 2;
1921       break;
1922     case spv::Dim::Dim3D:
1923       expected_num_components += 3;
1924       break;
1925     default:
1926       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1927              << "Image 'Dim' must be 1D, Buffer, 2D, Cube, 3D or Rect";
1928   }
1929 
1930   if (info.dim == spv::Dim::Dim1D || info.dim == spv::Dim::Dim2D ||
1931       info.dim == spv::Dim::Dim3D || info.dim == spv::Dim::Cube) {
1932     if (info.multisampled != 1 && info.sampled != 0 && info.sampled != 2) {
1933       return _.diag(SPV_ERROR_INVALID_DATA, inst)
1934              << "Image must have either 'MS'=1 or 'Sampled'=0 or 'Sampled'=2";
1935     }
1936   }
1937 
1938   uint32_t result_num_components = _.GetDimension(result_type);
1939   if (result_num_components != expected_num_components) {
1940     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1941            << "Result Type has " << result_num_components << " components, "
1942            << "but " << expected_num_components << " expected";
1943   }
1944 
1945   return SPV_SUCCESS;
1946 }
1947 
ValidateImageQueryFormatOrOrder(ValidationState_t & _,const Instruction * inst)1948 spv_result_t ValidateImageQueryFormatOrOrder(ValidationState_t& _,
1949                                              const Instruction* inst) {
1950   if (!_.IsIntScalarType(inst->type_id())) {
1951     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1952            << "Expected Result Type to be int scalar type";
1953   }
1954 
1955   const uint32_t image_type = _.GetOperandTypeId(inst, 2);
1956   if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
1957     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1958            << "Expected operand to be of type OpTypeImage";
1959   }
1960 
1961   ImageTypeInfo info;
1962   if (!GetImageTypeInfo(_, image_type, &info)) {
1963     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1964            << "Corrupt image type definition";
1965   }
1966 
1967   if (info.dim == spv::Dim::TileImageDataEXT) {
1968     return _.diag(SPV_ERROR_INVALID_DATA, inst)
1969            << "Image 'Dim' cannot be TileImageDataEXT";
1970   }
1971   return SPV_SUCCESS;
1972 }
1973 
ValidateImageQueryLod(ValidationState_t & _,const Instruction * inst)1974 spv_result_t ValidateImageQueryLod(ValidationState_t& _,
1975                                    const Instruction* inst) {
1976   _.function(inst->function()->id())
1977       ->RegisterExecutionModelLimitation(
1978           [&](spv::ExecutionModel model, std::string* message) {
1979             if (model != spv::ExecutionModel::Fragment &&
1980                 model != spv::ExecutionModel::GLCompute) {
1981               if (message) {
1982                 *message = std::string(
1983                     "OpImageQueryLod requires Fragment or GLCompute execution "
1984                     "model");
1985               }
1986               return false;
1987             }
1988             return true;
1989           });
1990   _.function(inst->function()->id())
1991       ->RegisterLimitation([](const ValidationState_t& state,
1992                               const Function* entry_point,
1993                               std::string* message) {
1994         const auto* models = state.GetExecutionModels(entry_point->id());
1995         const auto* modes = state.GetExecutionModes(entry_point->id());
1996         if (models->find(spv::ExecutionModel::GLCompute) != models->end() &&
1997             modes->find(spv::ExecutionMode::DerivativeGroupLinearNV) ==
1998                 modes->end() &&
1999             modes->find(spv::ExecutionMode::DerivativeGroupQuadsNV) ==
2000                 modes->end()) {
2001           if (message) {
2002             *message = std::string(
2003                 "OpImageQueryLod requires DerivativeGroupQuadsNV "
2004                 "or DerivativeGroupLinearNV execution mode for GLCompute "
2005                 "execution model");
2006           }
2007           return false;
2008         }
2009         return true;
2010       });
2011 
2012   const uint32_t result_type = inst->type_id();
2013   if (!_.IsFloatVectorType(result_type)) {
2014     return _.diag(SPV_ERROR_INVALID_DATA, inst)
2015            << "Expected Result Type to be float vector type";
2016   }
2017 
2018   if (_.GetDimension(result_type) != 2) {
2019     return _.diag(SPV_ERROR_INVALID_DATA, inst)
2020            << "Expected Result Type to have 2 components";
2021   }
2022 
2023   const uint32_t image_type = _.GetOperandTypeId(inst, 2);
2024   if (_.GetIdOpcode(image_type) != spv::Op::OpTypeSampledImage) {
2025     return _.diag(SPV_ERROR_INVALID_DATA, inst)
2026            << "Expected Image operand to be of type OpTypeSampledImage";
2027   }
2028 
2029   ImageTypeInfo info;
2030   if (!GetImageTypeInfo(_, image_type, &info)) {
2031     return _.diag(SPV_ERROR_INVALID_DATA, inst)
2032            << "Corrupt image type definition";
2033   }
2034 
2035   if (info.dim != spv::Dim::Dim1D && info.dim != spv::Dim::Dim2D &&
2036       info.dim != spv::Dim::Dim3D && info.dim != spv::Dim::Cube) {
2037     return _.diag(SPV_ERROR_INVALID_DATA, inst)
2038            << "Image 'Dim' must be 1D, 2D, 3D or Cube";
2039   }
2040 
2041   const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
2042   if (_.HasCapability(spv::Capability::Kernel)) {
2043     if (!_.IsFloatScalarOrVectorType(coord_type) &&
2044         !_.IsIntScalarOrVectorType(coord_type)) {
2045       return _.diag(SPV_ERROR_INVALID_DATA, inst)
2046              << "Expected Coordinate to be int or float scalar or vector";
2047     }
2048   } else {
2049     if (!_.IsFloatScalarOrVectorType(coord_type)) {
2050       return _.diag(SPV_ERROR_INVALID_DATA, inst)
2051              << "Expected Coordinate to be float scalar or vector";
2052     }
2053   }
2054 
2055   const uint32_t min_coord_size = GetPlaneCoordSize(info);
2056   const uint32_t actual_coord_size = _.GetDimension(coord_type);
2057   if (min_coord_size > actual_coord_size) {
2058     return _.diag(SPV_ERROR_INVALID_DATA, inst)
2059            << "Expected Coordinate to have at least " << min_coord_size
2060            << " components, but given only " << actual_coord_size;
2061   }
2062 
2063   // The operand is a sampled image.
2064   // The sampled image type is already checked to be parameterized by an image
2065   // type with Sampled=0 or Sampled=1.  Vulkan bans Sampled=0, and so we have
2066   // Sampled=1.  So the validator already enforces Vulkan VUID 4659:
2067   //   OpImageQuerySizeLod must only consume an "Image" operand whose type has
2068   //   its "Sampled" operand set to 1
2069   return SPV_SUCCESS;
2070 }
2071 
ValidateImageSparseLod(ValidationState_t & _,const Instruction * inst)2072 spv_result_t ValidateImageSparseLod(ValidationState_t& _,
2073                                     const Instruction* inst) {
2074   return _.diag(SPV_ERROR_INVALID_DATA, inst)
2075          << "Instruction reserved for future use, use of this instruction "
2076          << "is invalid";
2077 }
2078 
ValidateImageQueryLevelsOrSamples(ValidationState_t & _,const Instruction * inst)2079 spv_result_t ValidateImageQueryLevelsOrSamples(ValidationState_t& _,
2080                                                const Instruction* inst) {
2081   if (!_.IsIntScalarType(inst->type_id())) {
2082     return _.diag(SPV_ERROR_INVALID_DATA, inst)
2083            << "Expected Result Type to be int scalar type";
2084   }
2085 
2086   const uint32_t image_type = _.GetOperandTypeId(inst, 2);
2087   if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
2088     return _.diag(SPV_ERROR_INVALID_DATA, inst)
2089            << "Expected Image to be of type OpTypeImage";
2090   }
2091 
2092   ImageTypeInfo info;
2093   if (!GetImageTypeInfo(_, image_type, &info)) {
2094     return _.diag(SPV_ERROR_INVALID_DATA, inst)
2095            << "Corrupt image type definition";
2096   }
2097 
2098   const spv::Op opcode = inst->opcode();
2099   if (opcode == spv::Op::OpImageQueryLevels) {
2100     if (info.dim != spv::Dim::Dim1D && info.dim != spv::Dim::Dim2D &&
2101         info.dim != spv::Dim::Dim3D && info.dim != spv::Dim::Cube) {
2102       return _.diag(SPV_ERROR_INVALID_DATA, inst)
2103              << "Image 'Dim' must be 1D, 2D, 3D or Cube";
2104     }
2105     const auto target_env = _.context()->target_env;
2106     if (spvIsVulkanEnv(target_env)) {
2107       if (info.sampled != 1) {
2108         return _.diag(SPV_ERROR_INVALID_DATA, inst)
2109                << _.VkErrorID(4659)
2110                << "OpImageQueryLevels must only consume an \"Image\" operand "
2111                   "whose type has its \"Sampled\" operand set to 1";
2112       }
2113     }
2114   } else {
2115     assert(opcode == spv::Op::OpImageQuerySamples);
2116     if (info.dim != spv::Dim::Dim2D) {
2117       return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Image 'Dim' must be 2D";
2118     }
2119 
2120     if (info.multisampled != 1) {
2121       return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Image 'MS' must be 1";
2122     }
2123   }
2124   return SPV_SUCCESS;
2125 }
2126 
ValidateImageSparseTexelsResident(ValidationState_t & _,const Instruction * inst)2127 spv_result_t ValidateImageSparseTexelsResident(ValidationState_t& _,
2128                                                const Instruction* inst) {
2129   if (!_.IsBoolScalarType(inst->type_id())) {
2130     return _.diag(SPV_ERROR_INVALID_DATA, inst)
2131            << "Expected Result Type to be bool scalar type";
2132   }
2133 
2134   const uint32_t resident_code_type = _.GetOperandTypeId(inst, 2);
2135   if (!_.IsIntScalarType(resident_code_type)) {
2136     return _.diag(SPV_ERROR_INVALID_DATA, inst)
2137            << "Expected Resident Code to be int scalar";
2138   }
2139 
2140   return SPV_SUCCESS;
2141 }
2142 
ValidateImageProcessingQCOMDecoration(ValidationState_t & _,int id,spv::Decoration decor)2143 spv_result_t ValidateImageProcessingQCOMDecoration(ValidationState_t& _, int id,
2144                                                    spv::Decoration decor) {
2145   const Instruction* si_inst = nullptr;
2146   const Instruction* ld_inst = _.FindDef(id);
2147   if (ld_inst->opcode() == spv::Op::OpSampledImage) {
2148     si_inst = ld_inst;
2149     int t_idx = si_inst->GetOperandAs<int>(2);  // texture
2150     ld_inst = _.FindDef(t_idx);
2151   }
2152   if (ld_inst->opcode() != spv::Op::OpLoad) {
2153     return _.diag(SPV_ERROR_INVALID_DATA, ld_inst) << "Expect to see OpLoad";
2154   }
2155   int texture_id = ld_inst->GetOperandAs<int>(2);  // variable to load
2156   if (!_.HasDecoration(texture_id, decor)) {
2157     return _.diag(SPV_ERROR_INVALID_DATA, ld_inst)
2158            << "Missing decoration WeightTextureQCOM/BlockMatchTextureQCOM";
2159   }
2160 
2161   return SPV_SUCCESS;
2162 }
2163 
ValidateImageProcessingQCOM(ValidationState_t & _,const Instruction * inst)2164 spv_result_t ValidateImageProcessingQCOM(ValidationState_t& _,
2165                                          const Instruction* inst) {
2166   spv_result_t res = SPV_SUCCESS;
2167   const spv::Op opcode = inst->opcode();
2168   switch (opcode) {
2169     case spv::Op::OpImageSampleWeightedQCOM: {
2170       int wi_idx = inst->GetOperandAs<int>(4);  // weight
2171       res = ValidateImageProcessingQCOMDecoration(
2172           _, wi_idx, spv::Decoration::WeightTextureQCOM);
2173       break;
2174     }
2175     case spv::Op::OpImageBlockMatchSSDQCOM:
2176     case spv::Op::OpImageBlockMatchSADQCOM: {
2177       int tgt_idx = inst->GetOperandAs<int>(2);  // target
2178       res = ValidateImageProcessingQCOMDecoration(
2179           _, tgt_idx, spv::Decoration::BlockMatchTextureQCOM);
2180       if (res != SPV_SUCCESS) break;
2181       int ref_idx = inst->GetOperandAs<int>(4);  // reference
2182       res = ValidateImageProcessingQCOMDecoration(
2183           _, ref_idx, spv::Decoration::BlockMatchTextureQCOM);
2184       break;
2185     }
2186     default:
2187       break;
2188   }
2189 
2190   return res;
2191 }
2192 
2193 }  // namespace
2194 
2195 // Validates correctness of image instructions.
ImagePass(ValidationState_t & _,const Instruction * inst)2196 spv_result_t ImagePass(ValidationState_t& _, const Instruction* inst) {
2197   const spv::Op opcode = inst->opcode();
2198   if (IsImplicitLod(opcode)) {
2199     _.function(inst->function()->id())
2200         ->RegisterExecutionModelLimitation([opcode](spv::ExecutionModel model,
2201                                                     std::string* message) {
2202           if (model != spv::ExecutionModel::Fragment &&
2203               model != spv::ExecutionModel::GLCompute) {
2204             if (message) {
2205               *message =
2206                   std::string(
2207                       "ImplicitLod instructions require Fragment or GLCompute "
2208                       "execution model: ") +
2209                   spvOpcodeString(opcode);
2210             }
2211             return false;
2212           }
2213           return true;
2214         });
2215     _.function(inst->function()->id())
2216         ->RegisterLimitation([opcode](const ValidationState_t& state,
2217                                       const Function* entry_point,
2218                                       std::string* message) {
2219           const auto* models = state.GetExecutionModels(entry_point->id());
2220           const auto* modes = state.GetExecutionModes(entry_point->id());
2221           if (models &&
2222               models->find(spv::ExecutionModel::GLCompute) != models->end() &&
2223               (!modes ||
2224                (modes->find(spv::ExecutionMode::DerivativeGroupLinearNV) ==
2225                     modes->end() &&
2226                 modes->find(spv::ExecutionMode::DerivativeGroupQuadsNV) ==
2227                     modes->end()))) {
2228             if (message) {
2229               *message =
2230                   std::string(
2231                       "ImplicitLod instructions require DerivativeGroupQuadsNV "
2232                       "or DerivativeGroupLinearNV execution mode for GLCompute "
2233                       "execution model: ") +
2234                   spvOpcodeString(opcode);
2235             }
2236             return false;
2237           }
2238           return true;
2239         });
2240   }
2241 
2242   switch (opcode) {
2243     case spv::Op::OpTypeImage:
2244       return ValidateTypeImage(_, inst);
2245     case spv::Op::OpTypeSampledImage:
2246       return ValidateTypeSampledImage(_, inst);
2247     case spv::Op::OpSampledImage:
2248       return ValidateSampledImage(_, inst);
2249     case spv::Op::OpImageTexelPointer:
2250       return ValidateImageTexelPointer(_, inst);
2251 
2252     case spv::Op::OpImageSampleImplicitLod:
2253     case spv::Op::OpImageSampleExplicitLod:
2254     case spv::Op::OpImageSampleProjImplicitLod:
2255     case spv::Op::OpImageSampleProjExplicitLod:
2256     case spv::Op::OpImageSparseSampleImplicitLod:
2257     case spv::Op::OpImageSparseSampleExplicitLod:
2258       return ValidateImageLod(_, inst);
2259 
2260     case spv::Op::OpImageSampleDrefImplicitLod:
2261     case spv::Op::OpImageSampleDrefExplicitLod:
2262     case spv::Op::OpImageSampleProjDrefImplicitLod:
2263     case spv::Op::OpImageSampleProjDrefExplicitLod:
2264     case spv::Op::OpImageSparseSampleDrefImplicitLod:
2265     case spv::Op::OpImageSparseSampleDrefExplicitLod:
2266       return ValidateImageDrefLod(_, inst);
2267 
2268     case spv::Op::OpImageFetch:
2269     case spv::Op::OpImageSparseFetch:
2270       return ValidateImageFetch(_, inst);
2271 
2272     case spv::Op::OpImageGather:
2273     case spv::Op::OpImageDrefGather:
2274     case spv::Op::OpImageSparseGather:
2275     case spv::Op::OpImageSparseDrefGather:
2276       return ValidateImageGather(_, inst);
2277 
2278     case spv::Op::OpImageRead:
2279     case spv::Op::OpImageSparseRead:
2280       return ValidateImageRead(_, inst);
2281 
2282     case spv::Op::OpImageWrite:
2283       return ValidateImageWrite(_, inst);
2284 
2285     case spv::Op::OpImage:
2286       return ValidateImage(_, inst);
2287 
2288     case spv::Op::OpImageQueryFormat:
2289     case spv::Op::OpImageQueryOrder:
2290       return ValidateImageQueryFormatOrOrder(_, inst);
2291 
2292     case spv::Op::OpImageQuerySizeLod:
2293       return ValidateImageQuerySizeLod(_, inst);
2294     case spv::Op::OpImageQuerySize:
2295       return ValidateImageQuerySize(_, inst);
2296     case spv::Op::OpImageQueryLod:
2297       return ValidateImageQueryLod(_, inst);
2298 
2299     case spv::Op::OpImageQueryLevels:
2300     case spv::Op::OpImageQuerySamples:
2301       return ValidateImageQueryLevelsOrSamples(_, inst);
2302 
2303     case spv::Op::OpImageSparseSampleProjImplicitLod:
2304     case spv::Op::OpImageSparseSampleProjExplicitLod:
2305     case spv::Op::OpImageSparseSampleProjDrefImplicitLod:
2306     case spv::Op::OpImageSparseSampleProjDrefExplicitLod:
2307       return ValidateImageSparseLod(_, inst);
2308 
2309     case spv::Op::OpImageSparseTexelsResident:
2310       return ValidateImageSparseTexelsResident(_, inst);
2311 
2312     case spv::Op::OpImageSampleWeightedQCOM:
2313     case spv::Op::OpImageBoxFilterQCOM:
2314     case spv::Op::OpImageBlockMatchSSDQCOM:
2315     case spv::Op::OpImageBlockMatchSADQCOM:
2316       return ValidateImageProcessingQCOM(_, inst);
2317 
2318     default:
2319       break;
2320   }
2321 
2322   return SPV_SUCCESS;
2323 }
2324 
IsImageInstruction(const spv::Op opcode)2325 bool IsImageInstruction(const spv::Op opcode) {
2326   switch (opcode) {
2327     case spv::Op::OpImageSampleImplicitLod:
2328     case spv::Op::OpImageSampleDrefImplicitLod:
2329     case spv::Op::OpImageSampleProjImplicitLod:
2330     case spv::Op::OpImageSampleProjDrefImplicitLod:
2331     case spv::Op::OpImageSparseSampleImplicitLod:
2332     case spv::Op::OpImageSparseSampleDrefImplicitLod:
2333     case spv::Op::OpImageSparseSampleProjImplicitLod:
2334     case spv::Op::OpImageSparseSampleProjDrefImplicitLod:
2335 
2336     case spv::Op::OpImageSampleExplicitLod:
2337     case spv::Op::OpImageSampleDrefExplicitLod:
2338     case spv::Op::OpImageSampleProjExplicitLod:
2339     case spv::Op::OpImageSampleProjDrefExplicitLod:
2340     case spv::Op::OpImageSparseSampleExplicitLod:
2341     case spv::Op::OpImageSparseSampleDrefExplicitLod:
2342     case spv::Op::OpImageSparseSampleProjExplicitLod:
2343     case spv::Op::OpImageSparseSampleProjDrefExplicitLod:
2344 
2345     case spv::Op::OpImage:
2346     case spv::Op::OpImageFetch:
2347     case spv::Op::OpImageSparseFetch:
2348     case spv::Op::OpImageGather:
2349     case spv::Op::OpImageDrefGather:
2350     case spv::Op::OpImageSparseGather:
2351     case spv::Op::OpImageSparseDrefGather:
2352     case spv::Op::OpImageRead:
2353     case spv::Op::OpImageSparseRead:
2354     case spv::Op::OpImageWrite:
2355 
2356     case spv::Op::OpImageQueryFormat:
2357     case spv::Op::OpImageQueryOrder:
2358     case spv::Op::OpImageQuerySizeLod:
2359     case spv::Op::OpImageQuerySize:
2360     case spv::Op::OpImageQueryLod:
2361     case spv::Op::OpImageQueryLevels:
2362     case spv::Op::OpImageQuerySamples:
2363 
2364     case spv::Op::OpImageSampleWeightedQCOM:
2365     case spv::Op::OpImageBoxFilterQCOM:
2366     case spv::Op::OpImageBlockMatchSSDQCOM:
2367     case spv::Op::OpImageBlockMatchSADQCOM:
2368       return true;
2369     default:
2370       break;
2371   }
2372   return false;
2373 }
2374 
ValidateQCOMImageProcessingTextureUsages(ValidationState_t & _,const Instruction * inst)2375 spv_result_t ValidateQCOMImageProcessingTextureUsages(ValidationState_t& _,
2376                                                       const Instruction* inst) {
2377   const spv::Op opcode = inst->opcode();
2378   if (!IsImageInstruction(opcode)) return SPV_SUCCESS;
2379 
2380   switch (opcode) {
2381     case spv::Op::OpImageSampleWeightedQCOM:
2382     case spv::Op::OpImageBoxFilterQCOM:
2383     case spv::Op::OpImageBlockMatchSSDQCOM:
2384     case spv::Op::OpImageBlockMatchSADQCOM:
2385       break;
2386     default:
2387       for (size_t i = 0; i < inst->operands().size(); ++i) {
2388         int id = inst->GetOperandAs<int>(i);
2389         const Instruction* operand_inst = _.FindDef(id);
2390         if (operand_inst == nullptr) continue;
2391         if (operand_inst->opcode() == spv::Op::OpLoad) {
2392           if (_.IsQCOMImageProcessingTextureConsumer(id)) {
2393             return _.diag(SPV_ERROR_INVALID_DATA, inst)
2394                    << "Illegal use of QCOM image processing decorated texture";
2395           }
2396         }
2397         if (operand_inst->opcode() == spv::Op::OpSampledImage) {
2398           if (_.IsQCOMImageProcessingTextureConsumer(id)) {
2399             return _.diag(SPV_ERROR_INVALID_DATA, inst)
2400                    << "Illegal use of QCOM image processing decorated texture";
2401           }
2402         }
2403       }
2404       break;
2405   }
2406   return SPV_SUCCESS;
2407 }
2408 
2409 }  // namespace val
2410 }  // namespace spvtools
2411