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