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