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