1 // Copyright (c) 2018 Google LLC.
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 built-in variables.
18
19 #include <array>
20 #include <functional>
21 #include <list>
22 #include <map>
23 #include <set>
24 #include <sstream>
25 #include <stack>
26 #include <string>
27 #include <vector>
28
29 #include "source/opcode.h"
30 #include "source/spirv_target_env.h"
31 #include "source/util/bitutils.h"
32 #include "source/val/instruction.h"
33 #include "source/val/validate.h"
34 #include "source/val/validation_state.h"
35
36 namespace spvtools {
37 namespace val {
38 namespace {
39
40 // Returns a short textual description of the id defined by the given
41 // instruction.
GetIdDesc(const Instruction & inst)42 std::string GetIdDesc(const Instruction& inst) {
43 std::ostringstream ss;
44 ss << "ID <" << inst.id() << "> (Op" << spvOpcodeString(inst.opcode()) << ")";
45 return ss.str();
46 }
47
48 // Gets underlying data type which is
49 // - member type if instruction is OpTypeStruct
50 // (member index is taken from decoration).
51 // - data type if id creates a pointer.
52 // - type of the constant if instruction is OpConst or OpSpecConst.
53 //
54 // Fails in any other case. The function is based on built-ins allowed by
55 // the Vulkan spec.
56 // TODO: If non-Vulkan validation rules are added then it might need
57 // to be refactored.
GetUnderlyingType(ValidationState_t & _,const Decoration & decoration,const Instruction & inst,uint32_t * underlying_type)58 spv_result_t GetUnderlyingType(ValidationState_t& _,
59 const Decoration& decoration,
60 const Instruction& inst,
61 uint32_t* underlying_type) {
62 if (decoration.struct_member_index() != Decoration::kInvalidMember) {
63 if (inst.opcode() != spv::Op::OpTypeStruct) {
64 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
65 << GetIdDesc(inst)
66 << "Attempted to get underlying data type via member index for "
67 "non-struct type.";
68 }
69 *underlying_type = inst.word(decoration.struct_member_index() + 2);
70 return SPV_SUCCESS;
71 }
72
73 if (inst.opcode() == spv::Op::OpTypeStruct) {
74 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
75 << GetIdDesc(inst)
76 << " did not find an member index to get underlying data type for "
77 "struct type.";
78 }
79
80 if (spvOpcodeIsConstant(inst.opcode())) {
81 *underlying_type = inst.type_id();
82 return SPV_SUCCESS;
83 }
84
85 spv::StorageClass storage_class;
86 if (!_.GetPointerTypeInfo(inst.type_id(), underlying_type, &storage_class)) {
87 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
88 << GetIdDesc(inst)
89 << " is decorated with BuiltIn. BuiltIn decoration should only be "
90 "applied to struct types, variables and constants.";
91 }
92 return SPV_SUCCESS;
93 }
94
95 // Returns Storage Class used by the instruction if applicable.
96 // Returns spv::StorageClass::Max if not.
GetStorageClass(const Instruction & inst)97 spv::StorageClass GetStorageClass(const Instruction& inst) {
98 switch (inst.opcode()) {
99 case spv::Op::OpTypePointer:
100 case spv::Op::OpTypeForwardPointer: {
101 return spv::StorageClass(inst.word(2));
102 }
103 case spv::Op::OpVariable: {
104 return spv::StorageClass(inst.word(3));
105 }
106 case spv::Op::OpGenericCastToPtrExplicit: {
107 return spv::StorageClass(inst.word(4));
108 }
109 default: { break; }
110 }
111 return spv::StorageClass::Max;
112 }
113
114 typedef enum VUIDError_ {
115 VUIDErrorExecutionModel = 0,
116 VUIDErrorStorageClass = 1,
117 VUIDErrorType = 2,
118 VUIDErrorMax,
119 } VUIDError;
120
121 const static uint32_t NumVUIDBuiltins = 36;
122
123 typedef struct {
124 spv::BuiltIn builtIn;
125 uint32_t vuid[VUIDErrorMax]; // execution mode, storage class, type VUIDs
126 } BuiltinVUIDMapping;
127
128 std::array<BuiltinVUIDMapping, NumVUIDBuiltins> builtinVUIDInfo = {{
129 // clang-format off
130 {spv::BuiltIn::SubgroupEqMask, {0, 4370, 4371}},
131 {spv::BuiltIn::SubgroupGeMask, {0, 4372, 4373}},
132 {spv::BuiltIn::SubgroupGtMask, {0, 4374, 4375}},
133 {spv::BuiltIn::SubgroupLeMask, {0, 4376, 4377}},
134 {spv::BuiltIn::SubgroupLtMask, {0, 4378, 4379}},
135 {spv::BuiltIn::SubgroupLocalInvocationId, {0, 4380, 4381}},
136 {spv::BuiltIn::SubgroupSize, {0, 4382, 4383}},
137 {spv::BuiltIn::GlobalInvocationId, {4236, 4237, 4238}},
138 {spv::BuiltIn::LocalInvocationId, {4281, 4282, 4283}},
139 {spv::BuiltIn::NumWorkgroups, {4296, 4297, 4298}},
140 {spv::BuiltIn::NumSubgroups, {4293, 4294, 4295}},
141 {spv::BuiltIn::SubgroupId, {4367, 4368, 4369}},
142 {spv::BuiltIn::WorkgroupId, {4422, 4423, 4424}},
143 {spv::BuiltIn::HitKindKHR, {4242, 4243, 4244}},
144 {spv::BuiltIn::HitTNV, {4245, 4246, 4247}},
145 {spv::BuiltIn::InstanceCustomIndexKHR, {4251, 4252, 4253}},
146 {spv::BuiltIn::InstanceId, {4254, 4255, 4256}},
147 {spv::BuiltIn::RayGeometryIndexKHR, {4345, 4346, 4347}},
148 {spv::BuiltIn::ObjectRayDirectionKHR, {4299, 4300, 4301}},
149 {spv::BuiltIn::ObjectRayOriginKHR, {4302, 4303, 4304}},
150 {spv::BuiltIn::ObjectToWorldKHR, {4305, 4306, 4307}},
151 {spv::BuiltIn::WorldToObjectKHR, {4434, 4435, 4436}},
152 {spv::BuiltIn::IncomingRayFlagsKHR, {4248, 4249, 4250}},
153 {spv::BuiltIn::RayTminKHR, {4351, 4352, 4353}},
154 {spv::BuiltIn::RayTmaxKHR, {4348, 4349, 4350}},
155 {spv::BuiltIn::WorldRayDirectionKHR, {4428, 4429, 4430}},
156 {spv::BuiltIn::WorldRayOriginKHR, {4431, 4432, 4433}},
157 {spv::BuiltIn::LaunchIdKHR, {4266, 4267, 4268}},
158 {spv::BuiltIn::LaunchSizeKHR, {4269, 4270, 4271}},
159 {spv::BuiltIn::FragInvocationCountEXT, {4217, 4218, 4219}},
160 {spv::BuiltIn::FragSizeEXT, {4220, 4221, 4222}},
161 {spv::BuiltIn::FragStencilRefEXT, {4223, 4224, 4225}},
162 {spv::BuiltIn::FullyCoveredEXT, {4232, 4233, 4234}},
163 {spv::BuiltIn::CullMaskKHR, {6735, 6736, 6737}},
164 {spv::BuiltIn::BaryCoordKHR, {4154, 4155, 4156}},
165 {spv::BuiltIn::BaryCoordNoPerspKHR, {4160, 4161, 4162}},
166 // clang-format off
167 } };
168
GetVUIDForBuiltin(spv::BuiltIn builtIn,VUIDError type)169 uint32_t GetVUIDForBuiltin(spv::BuiltIn builtIn, VUIDError type) {
170 uint32_t vuid = 0;
171 for (const auto& iter: builtinVUIDInfo) {
172 if (iter.builtIn == builtIn) {
173 assert(type < VUIDErrorMax);
174 vuid = iter.vuid[type];
175 break;
176 }
177 }
178 return vuid;
179 }
180
IsExecutionModelValidForRtBuiltIn(spv::BuiltIn builtin,spv::ExecutionModel stage)181 bool IsExecutionModelValidForRtBuiltIn(spv::BuiltIn builtin,
182 spv::ExecutionModel stage) {
183 switch (builtin) {
184 case spv::BuiltIn::HitKindKHR:
185 case spv::BuiltIn::HitTNV:
186 if (stage == spv::ExecutionModel::AnyHitKHR ||
187 stage == spv::ExecutionModel::ClosestHitKHR) {
188 return true;
189 }
190 break;
191 case spv::BuiltIn::InstanceCustomIndexKHR:
192 case spv::BuiltIn::InstanceId:
193 case spv::BuiltIn::RayGeometryIndexKHR:
194 case spv::BuiltIn::ObjectRayDirectionKHR:
195 case spv::BuiltIn::ObjectRayOriginKHR:
196 case spv::BuiltIn::ObjectToWorldKHR:
197 case spv::BuiltIn::WorldToObjectKHR:
198 switch (stage) {
199 case spv::ExecutionModel::IntersectionKHR:
200 case spv::ExecutionModel::AnyHitKHR:
201 case spv::ExecutionModel::ClosestHitKHR:
202 return true;
203 default:
204 return false;
205 }
206 break;
207 case spv::BuiltIn::IncomingRayFlagsKHR:
208 case spv::BuiltIn::RayTminKHR:
209 case spv::BuiltIn::RayTmaxKHR:
210 case spv::BuiltIn::WorldRayDirectionKHR:
211 case spv::BuiltIn::WorldRayOriginKHR:
212 case spv::BuiltIn::CullMaskKHR:
213 switch (stage) {
214 case spv::ExecutionModel::IntersectionKHR:
215 case spv::ExecutionModel::AnyHitKHR:
216 case spv::ExecutionModel::ClosestHitKHR:
217 case spv::ExecutionModel::MissKHR:
218 return true;
219 default:
220 return false;
221 }
222 break;
223 case spv::BuiltIn::LaunchIdKHR:
224 case spv::BuiltIn::LaunchSizeKHR:
225 switch (stage) {
226 case spv::ExecutionModel::RayGenerationKHR:
227 case spv::ExecutionModel::IntersectionKHR:
228 case spv::ExecutionModel::AnyHitKHR:
229 case spv::ExecutionModel::ClosestHitKHR:
230 case spv::ExecutionModel::MissKHR:
231 case spv::ExecutionModel::CallableKHR:
232 return true;
233 default:
234 return false;
235 }
236 break;
237 default:
238 break;
239 }
240 return false;
241 }
242
243 // Helper class managing validation of built-ins.
244 // TODO: Generic functionality of this class can be moved into
245 // ValidationState_t to be made available to other users.
246 class BuiltInsValidator {
247 public:
BuiltInsValidator(ValidationState_t & vstate)248 BuiltInsValidator(ValidationState_t& vstate) : _(vstate) {}
249
250 // Run validation.
251 spv_result_t Run();
252
253 private:
254 // Goes through all decorations in the module, if decoration is BuiltIn
255 // calls ValidateSingleBuiltInAtDefinition().
256 spv_result_t ValidateBuiltInsAtDefinition();
257
258 // Validates the instruction defining an id with built-in decoration.
259 // Can be called multiple times for the same id, if multiple built-ins are
260 // specified. Seeds id_to_at_reference_checks_ with decorated ids if needed.
261 spv_result_t ValidateSingleBuiltInAtDefinition(const Decoration& decoration,
262 const Instruction& inst);
263
264 // The following section contains functions which are called when id defined
265 // by |inst| is decorated with BuiltIn |decoration|.
266 // Most functions are specific to a single built-in and have naming scheme:
267 // ValidateXYZAtDefinition. Some functions are common to multiple kinds of
268 // BuiltIn.
269 spv_result_t ValidateClipOrCullDistanceAtDefinition(
270 const Decoration& decoration, const Instruction& inst);
271 spv_result_t ValidateFragCoordAtDefinition(const Decoration& decoration,
272 const Instruction& inst);
273 spv_result_t ValidateFragDepthAtDefinition(const Decoration& decoration,
274 const Instruction& inst);
275 spv_result_t ValidateFrontFacingAtDefinition(const Decoration& decoration,
276 const Instruction& inst);
277 spv_result_t ValidateHelperInvocationAtDefinition(
278 const Decoration& decoration, const Instruction& inst);
279 spv_result_t ValidateInvocationIdAtDefinition(const Decoration& decoration,
280 const Instruction& inst);
281 spv_result_t ValidateInstanceIndexAtDefinition(const Decoration& decoration,
282 const Instruction& inst);
283 spv_result_t ValidateLayerOrViewportIndexAtDefinition(
284 const Decoration& decoration, const Instruction& inst);
285 spv_result_t ValidatePatchVerticesAtDefinition(const Decoration& decoration,
286 const Instruction& inst);
287 spv_result_t ValidatePointCoordAtDefinition(const Decoration& decoration,
288 const Instruction& inst);
289 spv_result_t ValidatePointSizeAtDefinition(const Decoration& decoration,
290 const Instruction& inst);
291 spv_result_t ValidatePositionAtDefinition(const Decoration& decoration,
292 const Instruction& inst);
293 spv_result_t ValidatePrimitiveIdAtDefinition(const Decoration& decoration,
294 const Instruction& inst);
295 spv_result_t ValidateSampleIdAtDefinition(const Decoration& decoration,
296 const Instruction& inst);
297 spv_result_t ValidateSampleMaskAtDefinition(const Decoration& decoration,
298 const Instruction& inst);
299 spv_result_t ValidateSamplePositionAtDefinition(const Decoration& decoration,
300 const Instruction& inst);
301 spv_result_t ValidateTessCoordAtDefinition(const Decoration& decoration,
302 const Instruction& inst);
303 spv_result_t ValidateTessLevelOuterAtDefinition(const Decoration& decoration,
304 const Instruction& inst);
305 spv_result_t ValidateTessLevelInnerAtDefinition(const Decoration& decoration,
306 const Instruction& inst);
307 spv_result_t ValidateVertexIndexAtDefinition(const Decoration& decoration,
308 const Instruction& inst);
309 spv_result_t ValidateVertexIdAtDefinition(const Decoration& decoration,
310 const Instruction& inst);
311 spv_result_t ValidateLocalInvocationIndexAtDefinition(
312 const Decoration& decoration, const Instruction& inst);
313 spv_result_t ValidateWorkgroupSizeAtDefinition(const Decoration& decoration,
314 const Instruction& inst);
315 spv_result_t ValidateBaseInstanceOrVertexAtDefinition(
316 const Decoration& decoration, const Instruction& inst);
317 spv_result_t ValidateDrawIndexAtDefinition(const Decoration& decoration,
318 const Instruction& inst);
319 spv_result_t ValidateViewIndexAtDefinition(const Decoration& decoration,
320 const Instruction& inst);
321 spv_result_t ValidateDeviceIndexAtDefinition(const Decoration& decoration,
322 const Instruction& inst);
323 spv_result_t ValidateFragInvocationCountAtDefinition(const Decoration& decoration,
324 const Instruction& inst);
325 spv_result_t ValidateFragSizeAtDefinition(const Decoration& decoration,
326 const Instruction& inst);
327 spv_result_t ValidateFragStencilRefAtDefinition(const Decoration& decoration,
328 const Instruction& inst);
329 spv_result_t ValidateFullyCoveredAtDefinition(const Decoration& decoration,
330 const Instruction& inst);
331 // Used for GlobalInvocationId, LocalInvocationId, NumWorkgroups, WorkgroupId.
332 spv_result_t ValidateComputeShaderI32Vec3InputAtDefinition(
333 const Decoration& decoration, const Instruction& inst);
334 spv_result_t ValidateNVSMOrARMCoreBuiltinsAtDefinition(const Decoration& decoration,
335 const Instruction& inst);
336 // Used for BaryCoord, BaryCoordNoPersp.
337 spv_result_t ValidateFragmentShaderF32Vec3InputAtDefinition(
338 const Decoration& decoration, const Instruction& inst);
339 // Used for SubgroupEqMask, SubgroupGeMask, SubgroupGtMask, SubgroupLtMask,
340 // SubgroupLeMask.
341 spv_result_t ValidateI32Vec4InputAtDefinition(const Decoration& decoration,
342 const Instruction& inst);
343 // Used for SubgroupLocalInvocationId, SubgroupSize.
344 spv_result_t ValidateI32InputAtDefinition(const Decoration& decoration,
345 const Instruction& inst);
346 // Used for SubgroupId, NumSubgroups.
347 spv_result_t ValidateComputeI32InputAtDefinition(const Decoration& decoration,
348 const Instruction& inst);
349
350 spv_result_t ValidatePrimitiveShadingRateAtDefinition(
351 const Decoration& decoration, const Instruction& inst);
352
353 spv_result_t ValidateShadingRateAtDefinition(const Decoration& decoration,
354 const Instruction& inst);
355
356 spv_result_t ValidateRayTracingBuiltinsAtDefinition(
357 const Decoration& decoration, const Instruction& inst);
358
359 // The following section contains functions which are called when id defined
360 // by |referenced_inst| is
361 // 1. referenced by |referenced_from_inst|
362 // 2. dependent on |built_in_inst| which is decorated with BuiltIn
363 // |decoration|. Most functions are specific to a single built-in and have
364 // naming scheme: ValidateXYZAtReference. Some functions are common to
365 // multiple kinds of BuiltIn.
366 spv_result_t ValidateFragCoordAtReference(
367 const Decoration& decoration, const Instruction& built_in_inst,
368 const Instruction& referenced_inst,
369 const Instruction& referenced_from_inst);
370
371 spv_result_t ValidateFragDepthAtReference(
372 const Decoration& decoration, const Instruction& built_in_inst,
373 const Instruction& referenced_inst,
374 const Instruction& referenced_from_inst);
375
376 spv_result_t ValidateFrontFacingAtReference(
377 const Decoration& decoration, const Instruction& built_in_inst,
378 const Instruction& referenced_inst,
379 const Instruction& referenced_from_inst);
380
381 spv_result_t ValidateHelperInvocationAtReference(
382 const Decoration& decoration, const Instruction& built_in_inst,
383 const Instruction& referenced_inst,
384 const Instruction& referenced_from_inst);
385
386 spv_result_t ValidateInvocationIdAtReference(
387 const Decoration& decoration, const Instruction& built_in_inst,
388 const Instruction& referenced_inst,
389 const Instruction& referenced_from_inst);
390
391 spv_result_t ValidateInstanceIndexAtReference(
392 const Decoration& decoration, const Instruction& built_in_inst,
393 const Instruction& referenced_inst,
394 const Instruction& referenced_from_inst);
395
396 spv_result_t ValidatePatchVerticesAtReference(
397 const Decoration& decoration, const Instruction& built_in_inst,
398 const Instruction& referenced_inst,
399 const Instruction& referenced_from_inst);
400
401 spv_result_t ValidatePointCoordAtReference(
402 const Decoration& decoration, const Instruction& built_in_inst,
403 const Instruction& referenced_inst,
404 const Instruction& referenced_from_inst);
405
406 spv_result_t ValidatePointSizeAtReference(
407 const Decoration& decoration, const Instruction& built_in_inst,
408 const Instruction& referenced_inst,
409 const Instruction& referenced_from_inst);
410
411 spv_result_t ValidatePositionAtReference(
412 const Decoration& decoration, const Instruction& built_in_inst,
413 const Instruction& referenced_inst,
414 const Instruction& referenced_from_inst);
415
416 spv_result_t ValidatePrimitiveIdAtReference(
417 const Decoration& decoration, const Instruction& built_in_inst,
418 const Instruction& referenced_inst,
419 const Instruction& referenced_from_inst);
420
421 spv_result_t ValidateSampleIdAtReference(
422 const Decoration& decoration, const Instruction& built_in_inst,
423 const Instruction& referenced_inst,
424 const Instruction& referenced_from_inst);
425
426 spv_result_t ValidateSampleMaskAtReference(
427 const Decoration& decoration, const Instruction& built_in_inst,
428 const Instruction& referenced_inst,
429 const Instruction& referenced_from_inst);
430
431 spv_result_t ValidateSamplePositionAtReference(
432 const Decoration& decoration, const Instruction& built_in_inst,
433 const Instruction& referenced_inst,
434 const Instruction& referenced_from_inst);
435
436 spv_result_t ValidateTessCoordAtReference(
437 const Decoration& decoration, const Instruction& built_in_inst,
438 const Instruction& referenced_inst,
439 const Instruction& referenced_from_inst);
440
441 spv_result_t ValidateTessLevelAtReference(
442 const Decoration& decoration, const Instruction& built_in_inst,
443 const Instruction& referenced_inst,
444 const Instruction& referenced_from_inst);
445
446 spv_result_t ValidateLocalInvocationIndexAtReference(
447 const Decoration& decoration, const Instruction& built_in_inst,
448 const Instruction& referenced_inst,
449 const Instruction& referenced_from_inst);
450
451 spv_result_t ValidateVertexIndexAtReference(
452 const Decoration& decoration, const Instruction& built_in_inst,
453 const Instruction& referenced_inst,
454 const Instruction& referenced_from_inst);
455
456 spv_result_t ValidateLayerOrViewportIndexAtReference(
457 const Decoration& decoration, const Instruction& built_in_inst,
458 const Instruction& referenced_inst,
459 const Instruction& referenced_from_inst);
460
461 spv_result_t ValidateWorkgroupSizeAtReference(
462 const Decoration& decoration, const Instruction& built_in_inst,
463 const Instruction& referenced_inst,
464 const Instruction& referenced_from_inst);
465
466 spv_result_t ValidateClipOrCullDistanceAtReference(
467 const Decoration& decoration, const Instruction& built_in_inst,
468 const Instruction& referenced_inst,
469 const Instruction& referenced_from_inst);
470
471 spv_result_t ValidateBaseInstanceOrVertexAtReference(
472 const Decoration& decoration, const Instruction& built_in_inst,
473 const Instruction& referenced_inst,
474 const Instruction& referenced_from_inst);
475
476 spv_result_t ValidateDrawIndexAtReference(
477 const Decoration& decoration, const Instruction& built_in_inst,
478 const Instruction& referenced_inst,
479 const Instruction& referenced_from_inst);
480
481 spv_result_t ValidateViewIndexAtReference(
482 const Decoration& decoration, const Instruction& built_in_inst,
483 const Instruction& referenced_inst,
484 const Instruction& referenced_from_inst);
485
486 spv_result_t ValidateDeviceIndexAtReference(
487 const Decoration& decoration, const Instruction& built_in_inst,
488 const Instruction& referenced_inst,
489 const Instruction& referenced_from_inst);
490
491 spv_result_t ValidateFragInvocationCountAtReference(
492 const Decoration& decoration, const Instruction& built_in_inst,
493 const Instruction& referenced_inst,
494 const Instruction& referenced_from_inst);
495
496 spv_result_t ValidateFragSizeAtReference(
497 const Decoration& decoration, const Instruction& built_in_inst,
498 const Instruction& referenced_inst,
499 const Instruction& referenced_from_inst);
500
501 spv_result_t ValidateFragStencilRefAtReference(
502 const Decoration& decoration, const Instruction& built_in_inst,
503 const Instruction& referenced_inst,
504 const Instruction& referenced_from_inst);
505
506 spv_result_t ValidateFullyCoveredAtReference(
507 const Decoration& decoration, const Instruction& built_in_inst,
508 const Instruction& referenced_inst,
509 const Instruction& referenced_from_inst);
510
511 // Used for GlobalInvocationId, LocalInvocationId, NumWorkgroups, WorkgroupId.
512 spv_result_t ValidateComputeShaderI32Vec3InputAtReference(
513 const Decoration& decoration, const Instruction& built_in_inst,
514 const Instruction& referenced_inst,
515 const Instruction& referenced_from_inst);
516
517 // Used for BaryCoord, BaryCoordNoPersp.
518 spv_result_t ValidateFragmentShaderF32Vec3InputAtReference(
519 const Decoration& decoration, const Instruction& built_in_inst,
520 const Instruction& referenced_inst,
521 const Instruction& referenced_from_inst);
522
523 // Used for SubgroupId and NumSubgroups.
524 spv_result_t ValidateComputeI32InputAtReference(
525 const Decoration& decoration, const Instruction& built_in_inst,
526 const Instruction& referenced_inst,
527 const Instruction& referenced_from_inst);
528
529 spv_result_t ValidateNVSMOrARMCoreBuiltinsAtReference(
530 const Decoration& decoration, const Instruction& built_in_inst,
531 const Instruction& referenced_inst,
532 const Instruction& referenced_from_inst);
533
534 spv_result_t ValidatePrimitiveShadingRateAtReference(
535 const Decoration& decoration, const Instruction& built_in_inst,
536 const Instruction& referenced_inst,
537 const Instruction& referenced_from_inst);
538
539 spv_result_t ValidateShadingRateAtReference(
540 const Decoration& decoration, const Instruction& built_in_inst,
541 const Instruction& referenced_inst,
542 const Instruction& referenced_from_inst);
543
544 spv_result_t ValidateRayTracingBuiltinsAtReference(
545 const Decoration& decoration, const Instruction& built_in_inst,
546 const Instruction& referenced_inst,
547 const Instruction& referenced_from_inst);
548
549 // Validates that |built_in_inst| is not (even indirectly) referenced from
550 // within a function which can be called with |execution_model|.
551 //
552 // |vuid| - Vulkan ID for the error, or a negative value if none.
553 // |comment| - text explaining why the restriction was imposed.
554 // |decoration| - BuiltIn decoration which causes the restriction.
555 // |referenced_inst| - instruction which is dependent on |built_in_inst| and
556 // defines the id which was referenced.
557 // |referenced_from_inst| - instruction which references id defined by
558 // |referenced_inst| from within a function.
559 spv_result_t ValidateNotCalledWithExecutionModel(
560 int vuid, const char* comment, spv::ExecutionModel execution_model,
561 const Decoration& decoration, const Instruction& built_in_inst,
562 const Instruction& referenced_inst,
563 const Instruction& referenced_from_inst);
564
565 // The following section contains functions which check that the decorated
566 // variable has the type specified in the function name. |diag| would be
567 // called with a corresponding error message, if validation is not successful.
568 spv_result_t ValidateBool(
569 const Decoration& decoration, const Instruction& inst,
570 const std::function<spv_result_t(const std::string& message)>& diag);
571 spv_result_t ValidateI(
572 const Decoration& decoration, const Instruction& inst,
573 const std::function<spv_result_t(const std::string& message)>& diag);
574 spv_result_t ValidateI32(
575 const Decoration& decoration, const Instruction& inst,
576 const std::function<spv_result_t(const std::string& message)>& diag);
577 spv_result_t ValidateI32Vec(
578 const Decoration& decoration, const Instruction& inst,
579 uint32_t num_components,
580 const std::function<spv_result_t(const std::string& message)>& diag);
581 spv_result_t ValidateI32Arr(
582 const Decoration& decoration, const Instruction& inst,
583 const std::function<spv_result_t(const std::string& message)>& diag);
584 spv_result_t ValidateOptionalArrayedI32(
585 const Decoration& decoration, const Instruction& inst,
586 const std::function<spv_result_t(const std::string& message)>& diag);
587 spv_result_t ValidateI32Helper(
588 const Decoration& decoration, const Instruction& inst,
589 const std::function<spv_result_t(const std::string& message)>& diag,
590 uint32_t underlying_type);
591 spv_result_t ValidateF32(
592 const Decoration& decoration, const Instruction& inst,
593 const std::function<spv_result_t(const std::string& message)>& diag);
594 spv_result_t ValidateOptionalArrayedF32(
595 const Decoration& decoration, const Instruction& inst,
596 const std::function<spv_result_t(const std::string& message)>& diag);
597 spv_result_t ValidateF32Helper(
598 const Decoration& decoration, const Instruction& inst,
599 const std::function<spv_result_t(const std::string& message)>& diag,
600 uint32_t underlying_type);
601 spv_result_t ValidateF32Vec(
602 const Decoration& decoration, const Instruction& inst,
603 uint32_t num_components,
604 const std::function<spv_result_t(const std::string& message)>& diag);
605 spv_result_t ValidateOptionalArrayedF32Vec(
606 const Decoration& decoration, const Instruction& inst,
607 uint32_t num_components,
608 const std::function<spv_result_t(const std::string& message)>& diag);
609 spv_result_t ValidateF32VecHelper(
610 const Decoration& decoration, const Instruction& inst,
611 uint32_t num_components,
612 const std::function<spv_result_t(const std::string& message)>& diag,
613 uint32_t underlying_type);
614 // If |num_components| is zero, the number of components is not checked.
615 spv_result_t ValidateF32Arr(
616 const Decoration& decoration, const Instruction& inst,
617 uint32_t num_components,
618 const std::function<spv_result_t(const std::string& message)>& diag);
619 spv_result_t ValidateOptionalArrayedF32Arr(
620 const Decoration& decoration, const Instruction& inst,
621 uint32_t num_components,
622 const std::function<spv_result_t(const std::string& message)>& diag);
623 spv_result_t ValidateF32ArrHelper(
624 const Decoration& decoration, const Instruction& inst,
625 uint32_t num_components,
626 const std::function<spv_result_t(const std::string& message)>& diag,
627 uint32_t underlying_type);
628 spv_result_t ValidateF32Mat(
629 const Decoration& decoration, const Instruction& inst,
630 uint32_t req_num_rows, uint32_t req_num_columns,
631 const std::function<spv_result_t(const std::string& message)>& diag);
632
633 // Generates strings like "Member #0 of struct ID <2>".
634 std::string GetDefinitionDesc(const Decoration& decoration,
635 const Instruction& inst) const;
636
637 // Generates strings like "ID <51> (OpTypePointer) is referencing ID <2>
638 // (OpTypeStruct) which is decorated with BuiltIn Position".
639 std::string GetReferenceDesc(
640 const Decoration& decoration, const Instruction& built_in_inst,
641 const Instruction& referenced_inst,
642 const Instruction& referenced_from_inst,
643 spv::ExecutionModel execution_model = spv::ExecutionModel::Max) const;
644
645 // Generates strings like "ID <51> (OpTypePointer) uses storage class
646 // UniformConstant".
647 std::string GetStorageClassDesc(const Instruction& inst) const;
648
649 // Updates inner working of the class. Is called sequentially for every
650 // instruction.
651 void Update(const Instruction& inst);
652
653 ValidationState_t& _;
654
655 // Mapping id -> list of rules which validate instruction referencing the
656 // id. Rules can create new rules and add them to this container.
657 // Using std::map, and not std::unordered_map to avoid iterator invalidation
658 // during rehashing.
659 std::map<uint32_t, std::list<std::function<spv_result_t(const Instruction&)>>>
660 id_to_at_reference_checks_;
661
662 // Id of the function we are currently inside. 0 if not inside a function.
663 uint32_t function_id_ = 0;
664
665 // Entry points which can (indirectly) call the current function.
666 // The pointer either points to a vector inside to function_to_entry_points_
667 // or to no_entry_points_. The pointer is guaranteed to never be null.
668 const std::vector<uint32_t> no_entry_points;
669 const std::vector<uint32_t>* entry_points_ = &no_entry_points;
670
671 // Execution models with which the current function can be called.
672 std::set<spv::ExecutionModel> execution_models_;
673 };
674
Update(const Instruction & inst)675 void BuiltInsValidator::Update(const Instruction& inst) {
676 const spv::Op opcode = inst.opcode();
677 if (opcode == spv::Op::OpFunction) {
678 // Entering a function.
679 assert(function_id_ == 0);
680 function_id_ = inst.id();
681 execution_models_.clear();
682 entry_points_ = &_.FunctionEntryPoints(function_id_);
683 // Collect execution models from all entry points from which the current
684 // function can be called.
685 for (const uint32_t entry_point : *entry_points_) {
686 if (const auto* models = _.GetExecutionModels(entry_point)) {
687 execution_models_.insert(models->begin(), models->end());
688 }
689 }
690 }
691
692 if (opcode == spv::Op::OpFunctionEnd) {
693 // Exiting a function.
694 assert(function_id_ != 0);
695 function_id_ = 0;
696 entry_points_ = &no_entry_points;
697 execution_models_.clear();
698 }
699 }
700
GetDefinitionDesc(const Decoration & decoration,const Instruction & inst) const701 std::string BuiltInsValidator::GetDefinitionDesc(
702 const Decoration& decoration, const Instruction& inst) const {
703 std::ostringstream ss;
704 if (decoration.struct_member_index() != Decoration::kInvalidMember) {
705 assert(inst.opcode() == spv::Op::OpTypeStruct);
706 ss << "Member #" << decoration.struct_member_index();
707 ss << " of struct ID <" << inst.id() << ">";
708 } else {
709 ss << GetIdDesc(inst);
710 }
711 return ss.str();
712 }
713
GetReferenceDesc(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst,spv::ExecutionModel execution_model) const714 std::string BuiltInsValidator::GetReferenceDesc(
715 const Decoration& decoration, const Instruction& built_in_inst,
716 const Instruction& referenced_inst, const Instruction& referenced_from_inst,
717 spv::ExecutionModel execution_model) const {
718 std::ostringstream ss;
719 ss << GetIdDesc(referenced_from_inst) << " is referencing "
720 << GetIdDesc(referenced_inst);
721 if (built_in_inst.id() != referenced_inst.id()) {
722 ss << " which is dependent on " << GetIdDesc(built_in_inst);
723 }
724
725 ss << " which is decorated with BuiltIn ";
726 ss << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
727 decoration.params()[0]);
728 if (function_id_) {
729 ss << " in function <" << function_id_ << ">";
730 if (execution_model != spv::ExecutionModel::Max) {
731 ss << " called with execution model ";
732 ss << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_EXECUTION_MODEL,
733 uint32_t(execution_model));
734 }
735 }
736 ss << ".";
737 return ss.str();
738 }
739
GetStorageClassDesc(const Instruction & inst) const740 std::string BuiltInsValidator::GetStorageClassDesc(
741 const Instruction& inst) const {
742 std::ostringstream ss;
743 ss << GetIdDesc(inst) << " uses storage class ";
744 ss << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_STORAGE_CLASS,
745 uint32_t(GetStorageClass(inst)));
746 ss << ".";
747 return ss.str();
748 }
749
ValidateBool(const Decoration & decoration,const Instruction & inst,const std::function<spv_result_t (const std::string & message)> & diag)750 spv_result_t BuiltInsValidator::ValidateBool(
751 const Decoration& decoration, const Instruction& inst,
752 const std::function<spv_result_t(const std::string& message)>& diag) {
753 uint32_t underlying_type = 0;
754 if (spv_result_t error =
755 GetUnderlyingType(_, decoration, inst, &underlying_type)) {
756 return error;
757 }
758
759 if (!_.IsBoolScalarType(underlying_type)) {
760 return diag(GetDefinitionDesc(decoration, inst) + " is not a bool scalar.");
761 }
762
763 return SPV_SUCCESS;
764 }
765
ValidateI(const Decoration & decoration,const Instruction & inst,const std::function<spv_result_t (const std::string & message)> & diag)766 spv_result_t BuiltInsValidator::ValidateI(
767 const Decoration& decoration, const Instruction& inst,
768 const std::function<spv_result_t(const std::string& message)>& diag) {
769 uint32_t underlying_type = 0;
770 if (spv_result_t error =
771 GetUnderlyingType(_, decoration, inst, &underlying_type)) {
772 return error;
773 }
774
775 if (!_.IsIntScalarType(underlying_type)) {
776 return diag(GetDefinitionDesc(decoration, inst) + " is not an int scalar.");
777 }
778
779 return SPV_SUCCESS;
780 }
781
ValidateI32(const Decoration & decoration,const Instruction & inst,const std::function<spv_result_t (const std::string & message)> & diag)782 spv_result_t BuiltInsValidator::ValidateI32(
783 const Decoration& decoration, const Instruction& inst,
784 const std::function<spv_result_t(const std::string& message)>& diag) {
785 uint32_t underlying_type = 0;
786 if (spv_result_t error =
787 GetUnderlyingType(_, decoration, inst, &underlying_type)) {
788 return error;
789 }
790
791 return ValidateI32Helper(decoration, inst, diag, underlying_type);
792 }
793
ValidateOptionalArrayedI32(const Decoration & decoration,const Instruction & inst,const std::function<spv_result_t (const std::string & message)> & diag)794 spv_result_t BuiltInsValidator::ValidateOptionalArrayedI32(
795 const Decoration& decoration, const Instruction& inst,
796 const std::function<spv_result_t(const std::string& message)>& diag) {
797 uint32_t underlying_type = 0;
798 if (spv_result_t error =
799 GetUnderlyingType(_, decoration, inst, &underlying_type)) {
800 return error;
801 }
802
803 // Strip the array, if present.
804 if (_.GetIdOpcode(underlying_type) == spv::Op::OpTypeArray) {
805 underlying_type = _.FindDef(underlying_type)->word(2u);
806 }
807
808 return ValidateI32Helper(decoration, inst, diag, underlying_type);
809 }
810
ValidateI32Helper(const Decoration & decoration,const Instruction & inst,const std::function<spv_result_t (const std::string & message)> & diag,uint32_t underlying_type)811 spv_result_t BuiltInsValidator::ValidateI32Helper(
812 const Decoration& decoration, const Instruction& inst,
813 const std::function<spv_result_t(const std::string& message)>& diag,
814 uint32_t underlying_type) {
815 if (!_.IsIntScalarType(underlying_type)) {
816 return diag(GetDefinitionDesc(decoration, inst) + " is not an int scalar.");
817 }
818
819 const uint32_t bit_width = _.GetBitWidth(underlying_type);
820 if (bit_width != 32) {
821 std::ostringstream ss;
822 ss << GetDefinitionDesc(decoration, inst) << " has bit width " << bit_width
823 << ".";
824 return diag(ss.str());
825 }
826
827 return SPV_SUCCESS;
828 }
829
ValidateOptionalArrayedF32(const Decoration & decoration,const Instruction & inst,const std::function<spv_result_t (const std::string & message)> & diag)830 spv_result_t BuiltInsValidator::ValidateOptionalArrayedF32(
831 const Decoration& decoration, const Instruction& inst,
832 const std::function<spv_result_t(const std::string& message)>& diag) {
833 uint32_t underlying_type = 0;
834 if (spv_result_t error =
835 GetUnderlyingType(_, decoration, inst, &underlying_type)) {
836 return error;
837 }
838
839 // Strip the array, if present.
840 if (_.GetIdOpcode(underlying_type) == spv::Op::OpTypeArray) {
841 underlying_type = _.FindDef(underlying_type)->word(2u);
842 }
843
844 return ValidateF32Helper(decoration, inst, diag, underlying_type);
845 }
846
ValidateF32(const Decoration & decoration,const Instruction & inst,const std::function<spv_result_t (const std::string & message)> & diag)847 spv_result_t BuiltInsValidator::ValidateF32(
848 const Decoration& decoration, const Instruction& inst,
849 const std::function<spv_result_t(const std::string& message)>& diag) {
850 uint32_t underlying_type = 0;
851 if (spv_result_t error =
852 GetUnderlyingType(_, decoration, inst, &underlying_type)) {
853 return error;
854 }
855
856 return ValidateF32Helper(decoration, inst, diag, underlying_type);
857 }
858
ValidateF32Helper(const Decoration & decoration,const Instruction & inst,const std::function<spv_result_t (const std::string & message)> & diag,uint32_t underlying_type)859 spv_result_t BuiltInsValidator::ValidateF32Helper(
860 const Decoration& decoration, const Instruction& inst,
861 const std::function<spv_result_t(const std::string& message)>& diag,
862 uint32_t underlying_type) {
863 if (!_.IsFloatScalarType(underlying_type)) {
864 return diag(GetDefinitionDesc(decoration, inst) +
865 " is not a float scalar.");
866 }
867
868 const uint32_t bit_width = _.GetBitWidth(underlying_type);
869 if (bit_width != 32) {
870 std::ostringstream ss;
871 ss << GetDefinitionDesc(decoration, inst) << " has bit width " << bit_width
872 << ".";
873 return diag(ss.str());
874 }
875
876 return SPV_SUCCESS;
877 }
878
ValidateI32Vec(const Decoration & decoration,const Instruction & inst,uint32_t num_components,const std::function<spv_result_t (const std::string & message)> & diag)879 spv_result_t BuiltInsValidator::ValidateI32Vec(
880 const Decoration& decoration, const Instruction& inst,
881 uint32_t num_components,
882 const std::function<spv_result_t(const std::string& message)>& diag) {
883 uint32_t underlying_type = 0;
884 if (spv_result_t error =
885 GetUnderlyingType(_, decoration, inst, &underlying_type)) {
886 return error;
887 }
888
889 if (!_.IsIntVectorType(underlying_type)) {
890 return diag(GetDefinitionDesc(decoration, inst) + " is not an int vector.");
891 }
892
893 const uint32_t actual_num_components = _.GetDimension(underlying_type);
894 if (_.GetDimension(underlying_type) != num_components) {
895 std::ostringstream ss;
896 ss << GetDefinitionDesc(decoration, inst) << " has "
897 << actual_num_components << " components.";
898 return diag(ss.str());
899 }
900
901 const uint32_t bit_width = _.GetBitWidth(underlying_type);
902 if (bit_width != 32) {
903 std::ostringstream ss;
904 ss << GetDefinitionDesc(decoration, inst)
905 << " has components with bit width " << bit_width << ".";
906 return diag(ss.str());
907 }
908
909 return SPV_SUCCESS;
910 }
911
ValidateOptionalArrayedF32Vec(const Decoration & decoration,const Instruction & inst,uint32_t num_components,const std::function<spv_result_t (const std::string & message)> & diag)912 spv_result_t BuiltInsValidator::ValidateOptionalArrayedF32Vec(
913 const Decoration& decoration, const Instruction& inst,
914 uint32_t num_components,
915 const std::function<spv_result_t(const std::string& message)>& diag) {
916 uint32_t underlying_type = 0;
917 if (spv_result_t error =
918 GetUnderlyingType(_, decoration, inst, &underlying_type)) {
919 return error;
920 }
921
922 // Strip the array, if present.
923 if (_.GetIdOpcode(underlying_type) == spv::Op::OpTypeArray) {
924 underlying_type = _.FindDef(underlying_type)->word(2u);
925 }
926
927 return ValidateF32VecHelper(decoration, inst, num_components, diag,
928 underlying_type);
929 }
930
ValidateF32Vec(const Decoration & decoration,const Instruction & inst,uint32_t num_components,const std::function<spv_result_t (const std::string & message)> & diag)931 spv_result_t BuiltInsValidator::ValidateF32Vec(
932 const Decoration& decoration, const Instruction& inst,
933 uint32_t num_components,
934 const std::function<spv_result_t(const std::string& message)>& diag) {
935 uint32_t underlying_type = 0;
936 if (spv_result_t error =
937 GetUnderlyingType(_, decoration, inst, &underlying_type)) {
938 return error;
939 }
940
941 return ValidateF32VecHelper(decoration, inst, num_components, diag,
942 underlying_type);
943 }
944
ValidateF32VecHelper(const Decoration & decoration,const Instruction & inst,uint32_t num_components,const std::function<spv_result_t (const std::string & message)> & diag,uint32_t underlying_type)945 spv_result_t BuiltInsValidator::ValidateF32VecHelper(
946 const Decoration& decoration, const Instruction& inst,
947 uint32_t num_components,
948 const std::function<spv_result_t(const std::string& message)>& diag,
949 uint32_t underlying_type) {
950 if (!_.IsFloatVectorType(underlying_type)) {
951 return diag(GetDefinitionDesc(decoration, inst) +
952 " is not a float vector.");
953 }
954
955 const uint32_t actual_num_components = _.GetDimension(underlying_type);
956 if (_.GetDimension(underlying_type) != num_components) {
957 std::ostringstream ss;
958 ss << GetDefinitionDesc(decoration, inst) << " has "
959 << actual_num_components << " components.";
960 return diag(ss.str());
961 }
962
963 const uint32_t bit_width = _.GetBitWidth(underlying_type);
964 if (bit_width != 32) {
965 std::ostringstream ss;
966 ss << GetDefinitionDesc(decoration, inst)
967 << " has components with bit width " << bit_width << ".";
968 return diag(ss.str());
969 }
970
971 return SPV_SUCCESS;
972 }
973
ValidateI32Arr(const Decoration & decoration,const Instruction & inst,const std::function<spv_result_t (const std::string & message)> & diag)974 spv_result_t BuiltInsValidator::ValidateI32Arr(
975 const Decoration& decoration, const Instruction& inst,
976 const std::function<spv_result_t(const std::string& message)>& diag) {
977 uint32_t underlying_type = 0;
978 if (spv_result_t error =
979 GetUnderlyingType(_, decoration, inst, &underlying_type)) {
980 return error;
981 }
982
983 const Instruction* const type_inst = _.FindDef(underlying_type);
984 if (type_inst->opcode() != spv::Op::OpTypeArray) {
985 return diag(GetDefinitionDesc(decoration, inst) + " is not an array.");
986 }
987
988 const uint32_t component_type = type_inst->word(2);
989 if (!_.IsIntScalarType(component_type)) {
990 return diag(GetDefinitionDesc(decoration, inst) +
991 " components are not int scalar.");
992 }
993
994 const uint32_t bit_width = _.GetBitWidth(component_type);
995 if (bit_width != 32) {
996 std::ostringstream ss;
997 ss << GetDefinitionDesc(decoration, inst)
998 << " has components with bit width " << bit_width << ".";
999 return diag(ss.str());
1000 }
1001
1002 return SPV_SUCCESS;
1003 }
1004
ValidateF32Arr(const Decoration & decoration,const Instruction & inst,uint32_t num_components,const std::function<spv_result_t (const std::string & message)> & diag)1005 spv_result_t BuiltInsValidator::ValidateF32Arr(
1006 const Decoration& decoration, const Instruction& inst,
1007 uint32_t num_components,
1008 const std::function<spv_result_t(const std::string& message)>& diag) {
1009 uint32_t underlying_type = 0;
1010 if (spv_result_t error =
1011 GetUnderlyingType(_, decoration, inst, &underlying_type)) {
1012 return error;
1013 }
1014
1015 return ValidateF32ArrHelper(decoration, inst, num_components, diag,
1016 underlying_type);
1017 }
1018
ValidateOptionalArrayedF32Arr(const Decoration & decoration,const Instruction & inst,uint32_t num_components,const std::function<spv_result_t (const std::string & message)> & diag)1019 spv_result_t BuiltInsValidator::ValidateOptionalArrayedF32Arr(
1020 const Decoration& decoration, const Instruction& inst,
1021 uint32_t num_components,
1022 const std::function<spv_result_t(const std::string& message)>& diag) {
1023 uint32_t underlying_type = 0;
1024 if (spv_result_t error =
1025 GetUnderlyingType(_, decoration, inst, &underlying_type)) {
1026 return error;
1027 }
1028
1029 // Strip an extra layer of arraying if present.
1030 if (_.GetIdOpcode(underlying_type) == spv::Op::OpTypeArray) {
1031 uint32_t subtype = _.FindDef(underlying_type)->word(2u);
1032 if (_.GetIdOpcode(subtype) == spv::Op::OpTypeArray) {
1033 underlying_type = subtype;
1034 }
1035 }
1036
1037 return ValidateF32ArrHelper(decoration, inst, num_components, diag,
1038 underlying_type);
1039 }
1040
ValidateF32ArrHelper(const Decoration & decoration,const Instruction & inst,uint32_t num_components,const std::function<spv_result_t (const std::string & message)> & diag,uint32_t underlying_type)1041 spv_result_t BuiltInsValidator::ValidateF32ArrHelper(
1042 const Decoration& decoration, const Instruction& inst,
1043 uint32_t num_components,
1044 const std::function<spv_result_t(const std::string& message)>& diag,
1045 uint32_t underlying_type) {
1046 const Instruction* const type_inst = _.FindDef(underlying_type);
1047 if (type_inst->opcode() != spv::Op::OpTypeArray) {
1048 return diag(GetDefinitionDesc(decoration, inst) + " is not an array.");
1049 }
1050
1051 const uint32_t component_type = type_inst->word(2);
1052 if (!_.IsFloatScalarType(component_type)) {
1053 return diag(GetDefinitionDesc(decoration, inst) +
1054 " components are not float scalar.");
1055 }
1056
1057 const uint32_t bit_width = _.GetBitWidth(component_type);
1058 if (bit_width != 32) {
1059 std::ostringstream ss;
1060 ss << GetDefinitionDesc(decoration, inst)
1061 << " has components with bit width " << bit_width << ".";
1062 return diag(ss.str());
1063 }
1064
1065 if (num_components != 0) {
1066 uint64_t actual_num_components = 0;
1067 if (!_.GetConstantValUint64(type_inst->word(3), &actual_num_components)) {
1068 assert(0 && "Array type definition is corrupt");
1069 }
1070 if (actual_num_components != num_components) {
1071 std::ostringstream ss;
1072 ss << GetDefinitionDesc(decoration, inst) << " has "
1073 << actual_num_components << " components.";
1074 return diag(ss.str());
1075 }
1076 }
1077
1078 return SPV_SUCCESS;
1079 }
1080
ValidateF32Mat(const Decoration & decoration,const Instruction & inst,uint32_t req_num_rows,uint32_t req_num_columns,const std::function<spv_result_t (const std::string & message)> & diag)1081 spv_result_t BuiltInsValidator::ValidateF32Mat(
1082 const Decoration& decoration, const Instruction& inst,
1083 uint32_t req_num_rows, uint32_t req_num_columns,
1084 const std::function<spv_result_t(const std::string& message)>& diag) {
1085 uint32_t underlying_type = 0;
1086 uint32_t num_rows = 0;
1087 uint32_t num_cols = 0;
1088 uint32_t col_type = 0;
1089 uint32_t component_type = 0;
1090 if (spv_result_t error =
1091 GetUnderlyingType(_, decoration, inst, &underlying_type)) {
1092 return error;
1093 }
1094 if (!_.GetMatrixTypeInfo(underlying_type, &num_rows, &num_cols, &col_type,
1095 &component_type) ||
1096 num_rows != req_num_rows || num_cols != req_num_columns) {
1097 std::ostringstream ss;
1098 ss << GetDefinitionDesc(decoration, inst) << " has columns " << num_cols
1099 << " and rows " << num_rows << " not equal to expected "
1100 << req_num_columns << "x" << req_num_rows << ".";
1101 return diag(ss.str());
1102 }
1103
1104 return ValidateF32VecHelper(decoration, inst, req_num_rows, diag, col_type);
1105 }
1106
ValidateNotCalledWithExecutionModel(int vuid,const char * comment,spv::ExecutionModel execution_model,const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)1107 spv_result_t BuiltInsValidator::ValidateNotCalledWithExecutionModel(
1108 int vuid, const char* comment, spv::ExecutionModel execution_model,
1109 const Decoration& decoration, const Instruction& built_in_inst,
1110 const Instruction& referenced_inst,
1111 const Instruction& referenced_from_inst) {
1112 if (function_id_) {
1113 if (execution_models_.count(execution_model)) {
1114 const char* execution_model_str = _.grammar().lookupOperandName(
1115 SPV_OPERAND_TYPE_EXECUTION_MODEL, uint32_t(execution_model));
1116 const char* built_in_str = _.grammar().lookupOperandName(
1117 SPV_OPERAND_TYPE_BUILT_IN, decoration.params()[0]);
1118 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1119 << (vuid < 0 ? std::string("") : _.VkErrorID(vuid)) << comment
1120 << " " << GetIdDesc(referenced_inst) << " depends on "
1121 << GetIdDesc(built_in_inst) << " which is decorated with BuiltIn "
1122 << built_in_str << "."
1123 << " Id <" << referenced_inst.id() << "> is later referenced by "
1124 << GetIdDesc(referenced_from_inst) << " in function <"
1125 << function_id_ << "> which is called with execution model "
1126 << execution_model_str << ".";
1127 }
1128 } else {
1129 // Propagate this rule to all dependant ids in the global scope.
1130 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
1131 std::bind(&BuiltInsValidator::ValidateNotCalledWithExecutionModel, this,
1132 vuid, comment, execution_model, decoration, built_in_inst,
1133 referenced_from_inst, std::placeholders::_1));
1134 }
1135 return SPV_SUCCESS;
1136 }
1137
ValidateClipOrCullDistanceAtDefinition(const Decoration & decoration,const Instruction & inst)1138 spv_result_t BuiltInsValidator::ValidateClipOrCullDistanceAtDefinition(
1139 const Decoration& decoration, const Instruction& inst) {
1140 // Seed at reference checks with this built-in.
1141 return ValidateClipOrCullDistanceAtReference(decoration, inst, inst, inst);
1142 }
1143
ValidateClipOrCullDistanceAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)1144 spv_result_t BuiltInsValidator::ValidateClipOrCullDistanceAtReference(
1145 const Decoration& decoration, const Instruction& built_in_inst,
1146 const Instruction& referenced_inst,
1147 const Instruction& referenced_from_inst) {
1148 uint32_t operand = decoration.params()[0];
1149 if (spvIsVulkanEnv(_.context()->target_env)) {
1150 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
1151 if (storage_class != spv::StorageClass::Max &&
1152 storage_class != spv::StorageClass::Input &&
1153 storage_class != spv::StorageClass::Output) {
1154 uint32_t vuid = (spv::BuiltIn(decoration.params()[0]) == spv::BuiltIn::ClipDistance) ? 4190 : 4199;
1155 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1156 << _.VkErrorID(vuid) << "Vulkan spec allows BuiltIn "
1157 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
1158 operand)
1159 << " to be only used for variables with Input or Output storage "
1160 "class. "
1161 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1162 referenced_from_inst)
1163 << " " << GetStorageClassDesc(referenced_from_inst);
1164 }
1165
1166 if (storage_class == spv::StorageClass::Input) {
1167 assert(function_id_ == 0);
1168 uint32_t vuid = (spv::BuiltIn(decoration.params()[0]) == spv::BuiltIn::ClipDistance) ? 4188 : 4197;
1169 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1170 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, vuid,
1171 "Vulkan spec doesn't allow BuiltIn ClipDistance/CullDistance to be "
1172 "used for variables with Input storage class if execution model is "
1173 "Vertex.",
1174 spv::ExecutionModel::Vertex, decoration, built_in_inst,
1175 referenced_from_inst, std::placeholders::_1));
1176 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1177 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, vuid,
1178 "Vulkan spec doesn't allow BuiltIn ClipDistance/CullDistance to be "
1179 "used for variables with Input storage class if execution model is "
1180 "MeshNV.",
1181 spv::ExecutionModel::MeshNV, decoration, built_in_inst,
1182 referenced_from_inst, std::placeholders::_1));
1183 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1184 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, vuid,
1185 "Vulkan spec doesn't allow BuiltIn ClipDistance/CullDistance to be "
1186 "used for variables with Input storage class if execution model is "
1187 "MeshEXT.",
1188 spv::ExecutionModel::MeshEXT, decoration, built_in_inst,
1189 referenced_from_inst, std::placeholders::_1));
1190 }
1191
1192 if (storage_class == spv::StorageClass::Output) {
1193 assert(function_id_ == 0);
1194 uint32_t vuid = (spv::BuiltIn(decoration.params()[0]) == spv::BuiltIn::ClipDistance) ? 4189 : 4198;
1195 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1196 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, vuid,
1197 "Vulkan spec doesn't allow BuiltIn ClipDistance/CullDistance to be "
1198 "used for variables with Output storage class if execution model is "
1199 "Fragment.",
1200 spv::ExecutionModel::Fragment, decoration, built_in_inst,
1201 referenced_from_inst, std::placeholders::_1));
1202 }
1203
1204 for (const spv::ExecutionModel execution_model : execution_models_) {
1205 switch (execution_model) {
1206 case spv::ExecutionModel::Fragment:
1207 case spv::ExecutionModel::Vertex: {
1208 if (spv_result_t error = ValidateF32Arr(
1209 decoration, built_in_inst, /* Any number of components */ 0,
1210 [this, &decoration, &referenced_from_inst](
1211 const std::string& message) -> spv_result_t {
1212 uint32_t vuid =
1213 (spv::BuiltIn(decoration.params()[0]) == spv::BuiltIn::ClipDistance)
1214 ? 4191
1215 : 4200;
1216 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1217 << _.VkErrorID(vuid)
1218 << "According to the Vulkan spec BuiltIn "
1219 << _.grammar().lookupOperandName(
1220 SPV_OPERAND_TYPE_BUILT_IN,
1221 decoration.params()[0])
1222 << " variable needs to be a 32-bit float array. "
1223 << message;
1224 })) {
1225 return error;
1226 }
1227 break;
1228 }
1229 case spv::ExecutionModel::TessellationControl:
1230 case spv::ExecutionModel::TessellationEvaluation:
1231 case spv::ExecutionModel::Geometry:
1232 case spv::ExecutionModel::MeshNV:
1233 case spv::ExecutionModel::MeshEXT: {
1234 if (decoration.struct_member_index() != Decoration::kInvalidMember) {
1235 // The outer level of array is applied on the variable.
1236 if (spv_result_t error = ValidateF32Arr(
1237 decoration, built_in_inst, /* Any number of components */ 0,
1238 [this, &decoration, &referenced_from_inst](
1239 const std::string& message) -> spv_result_t {
1240 uint32_t vuid =
1241 (spv::BuiltIn(decoration.params()[0]) == spv::BuiltIn::ClipDistance)
1242 ? 4191
1243 : 4200;
1244 return _.diag(SPV_ERROR_INVALID_DATA,
1245 &referenced_from_inst)
1246 << _.VkErrorID(vuid)
1247 << "According to the Vulkan spec BuiltIn "
1248 << _.grammar().lookupOperandName(
1249 SPV_OPERAND_TYPE_BUILT_IN,
1250 decoration.params()[0])
1251 << " variable needs to be a 32-bit float array. "
1252 << message;
1253 })) {
1254 return error;
1255 }
1256 } else {
1257 if (spv_result_t error = ValidateOptionalArrayedF32Arr(
1258 decoration, built_in_inst, /* Any number of components */ 0,
1259 [this, &decoration, &referenced_from_inst](
1260 const std::string& message) -> spv_result_t {
1261 uint32_t vuid =
1262 (spv::BuiltIn(decoration.params()[0]) == spv::BuiltIn::ClipDistance)
1263 ? 4191
1264 : 4200;
1265 return _.diag(SPV_ERROR_INVALID_DATA,
1266 &referenced_from_inst)
1267 << _.VkErrorID(vuid)
1268 << "According to the Vulkan spec BuiltIn "
1269 << _.grammar().lookupOperandName(
1270 SPV_OPERAND_TYPE_BUILT_IN,
1271 decoration.params()[0])
1272 << " variable needs to be a 32-bit float array. "
1273 << message;
1274 })) {
1275 return error;
1276 }
1277 }
1278 break;
1279 }
1280
1281 default: {
1282 uint32_t vuid =
1283 (spv::BuiltIn(decoration.params()[0]) == spv::BuiltIn::ClipDistance) ? 4187 : 4196;
1284 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1285 << _.VkErrorID(vuid) << "Vulkan spec allows BuiltIn "
1286 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
1287 operand)
1288 << " to be used only with Fragment, Vertex, "
1289 "TessellationControl, TessellationEvaluation or Geometry "
1290 "execution models. "
1291 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1292 referenced_from_inst, execution_model);
1293 }
1294 }
1295 }
1296 }
1297
1298 if (function_id_ == 0) {
1299 // Propagate this rule to all dependant ids in the global scope.
1300 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
1301 std::bind(&BuiltInsValidator::ValidateClipOrCullDistanceAtReference,
1302 this, decoration, built_in_inst, referenced_from_inst,
1303 std::placeholders::_1));
1304 }
1305
1306 return SPV_SUCCESS;
1307 }
1308
ValidateFragCoordAtDefinition(const Decoration & decoration,const Instruction & inst)1309 spv_result_t BuiltInsValidator::ValidateFragCoordAtDefinition(
1310 const Decoration& decoration, const Instruction& inst) {
1311 if (spvIsVulkanEnv(_.context()->target_env)) {
1312 if (spv_result_t error = ValidateF32Vec(
1313 decoration, inst, 4,
1314 [this, &inst](const std::string& message) -> spv_result_t {
1315 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
1316 << _.VkErrorID(4212) << "According to the "
1317 << spvLogStringForEnv(_.context()->target_env)
1318 << " spec BuiltIn FragCoord "
1319 "variable needs to be a 4-component 32-bit float "
1320 "vector. "
1321 << message;
1322 })) {
1323 return error;
1324 }
1325 }
1326
1327 // Seed at reference checks with this built-in.
1328 return ValidateFragCoordAtReference(decoration, inst, inst, inst);
1329 }
1330
ValidateFragCoordAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)1331 spv_result_t BuiltInsValidator::ValidateFragCoordAtReference(
1332 const Decoration& decoration, const Instruction& built_in_inst,
1333 const Instruction& referenced_inst,
1334 const Instruction& referenced_from_inst) {
1335 if (spvIsVulkanEnv(_.context()->target_env)) {
1336 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
1337 if (storage_class != spv::StorageClass::Max &&
1338 storage_class != spv::StorageClass::Input) {
1339 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1340 << _.VkErrorID(4211) << spvLogStringForEnv(_.context()->target_env)
1341 << " spec allows BuiltIn FragCoord to be only used for "
1342 "variables with Input storage class. "
1343 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1344 referenced_from_inst)
1345 << " " << GetStorageClassDesc(referenced_from_inst);
1346 }
1347
1348 for (const spv::ExecutionModel execution_model : execution_models_) {
1349 if (execution_model != spv::ExecutionModel::Fragment) {
1350 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1351 << _.VkErrorID(4210)
1352 << spvLogStringForEnv(_.context()->target_env)
1353 << " spec allows BuiltIn FragCoord to be used only with "
1354 "Fragment execution model. "
1355 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1356 referenced_from_inst, execution_model);
1357 }
1358 }
1359 }
1360
1361 if (function_id_ == 0) {
1362 // Propagate this rule to all dependant ids in the global scope.
1363 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1364 &BuiltInsValidator::ValidateFragCoordAtReference, this, decoration,
1365 built_in_inst, referenced_from_inst, std::placeholders::_1));
1366 }
1367
1368 return SPV_SUCCESS;
1369 }
1370
ValidateFragDepthAtDefinition(const Decoration & decoration,const Instruction & inst)1371 spv_result_t BuiltInsValidator::ValidateFragDepthAtDefinition(
1372 const Decoration& decoration, const Instruction& inst) {
1373 if (spvIsVulkanEnv(_.context()->target_env)) {
1374 if (spv_result_t error = ValidateF32(
1375 decoration, inst,
1376 [this, &inst](const std::string& message) -> spv_result_t {
1377 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
1378 << _.VkErrorID(4215) << "According to the "
1379 << spvLogStringForEnv(_.context()->target_env)
1380 << " spec BuiltIn FragDepth "
1381 "variable needs to be a 32-bit float scalar. "
1382 << message;
1383 })) {
1384 return error;
1385 }
1386 }
1387
1388 // Seed at reference checks with this built-in.
1389 return ValidateFragDepthAtReference(decoration, inst, inst, inst);
1390 }
1391
ValidateFragDepthAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)1392 spv_result_t BuiltInsValidator::ValidateFragDepthAtReference(
1393 const Decoration& decoration, const Instruction& built_in_inst,
1394 const Instruction& referenced_inst,
1395 const Instruction& referenced_from_inst) {
1396 if (spvIsVulkanEnv(_.context()->target_env)) {
1397 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
1398 if (storage_class != spv::StorageClass::Max &&
1399 storage_class != spv::StorageClass::Output) {
1400 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1401 << _.VkErrorID(4214) << spvLogStringForEnv(_.context()->target_env)
1402 << " spec allows BuiltIn FragDepth to be only used for "
1403 "variables with Output storage class. "
1404 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1405 referenced_from_inst)
1406 << " " << GetStorageClassDesc(referenced_from_inst);
1407 }
1408
1409 for (const spv::ExecutionModel execution_model : execution_models_) {
1410 if (execution_model != spv::ExecutionModel::Fragment) {
1411 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1412 << _.VkErrorID(4213)
1413 << spvLogStringForEnv(_.context()->target_env)
1414 << " spec allows BuiltIn FragDepth to be used only with "
1415 "Fragment execution model. "
1416 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1417 referenced_from_inst, execution_model);
1418 }
1419 }
1420
1421 for (const uint32_t entry_point : *entry_points_) {
1422 // Every entry point from which this function is called needs to have
1423 // Execution Mode DepthReplacing.
1424 const auto* modes = _.GetExecutionModes(entry_point);
1425 if (!modes || !modes->count(spv::ExecutionMode::DepthReplacing)) {
1426 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1427 << _.VkErrorID(4216)
1428 << spvLogStringForEnv(_.context()->target_env)
1429 << " spec requires DepthReplacing execution mode to be "
1430 "declared when using BuiltIn FragDepth. "
1431 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1432 referenced_from_inst);
1433 }
1434 }
1435 }
1436
1437 if (function_id_ == 0) {
1438 // Propagate this rule to all dependant ids in the global scope.
1439 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1440 &BuiltInsValidator::ValidateFragDepthAtReference, this, decoration,
1441 built_in_inst, referenced_from_inst, std::placeholders::_1));
1442 }
1443
1444 return SPV_SUCCESS;
1445 }
1446
ValidateFrontFacingAtDefinition(const Decoration & decoration,const Instruction & inst)1447 spv_result_t BuiltInsValidator::ValidateFrontFacingAtDefinition(
1448 const Decoration& decoration, const Instruction& inst) {
1449 if (spvIsVulkanEnv(_.context()->target_env)) {
1450 if (spv_result_t error = ValidateBool(
1451 decoration, inst,
1452 [this, &inst](const std::string& message) -> spv_result_t {
1453 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
1454 << _.VkErrorID(4231) << "According to the "
1455 << spvLogStringForEnv(_.context()->target_env)
1456 << " spec BuiltIn FrontFacing "
1457 "variable needs to be a bool scalar. "
1458 << message;
1459 })) {
1460 return error;
1461 }
1462 }
1463
1464 // Seed at reference checks with this built-in.
1465 return ValidateFrontFacingAtReference(decoration, inst, inst, inst);
1466 }
1467
ValidateFrontFacingAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)1468 spv_result_t BuiltInsValidator::ValidateFrontFacingAtReference(
1469 const Decoration& decoration, const Instruction& built_in_inst,
1470 const Instruction& referenced_inst,
1471 const Instruction& referenced_from_inst) {
1472 if (spvIsVulkanEnv(_.context()->target_env)) {
1473 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
1474 if (storage_class != spv::StorageClass::Max &&
1475 storage_class != spv::StorageClass::Input) {
1476 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1477 << _.VkErrorID(4230) << spvLogStringForEnv(_.context()->target_env)
1478 << " spec allows BuiltIn FrontFacing to be only used for "
1479 "variables with Input storage class. "
1480 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1481 referenced_from_inst)
1482 << " " << GetStorageClassDesc(referenced_from_inst);
1483 }
1484
1485 for (const spv::ExecutionModel execution_model : execution_models_) {
1486 if (execution_model != spv::ExecutionModel::Fragment) {
1487 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1488 << _.VkErrorID(4229)
1489 << spvLogStringForEnv(_.context()->target_env)
1490 << " spec allows BuiltIn FrontFacing to be used only with "
1491 "Fragment execution model. "
1492 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1493 referenced_from_inst, execution_model);
1494 }
1495 }
1496 }
1497
1498 if (function_id_ == 0) {
1499 // Propagate this rule to all dependant ids in the global scope.
1500 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1501 &BuiltInsValidator::ValidateFrontFacingAtReference, this, decoration,
1502 built_in_inst, referenced_from_inst, std::placeholders::_1));
1503 }
1504
1505 return SPV_SUCCESS;
1506 }
1507
ValidateHelperInvocationAtDefinition(const Decoration & decoration,const Instruction & inst)1508 spv_result_t BuiltInsValidator::ValidateHelperInvocationAtDefinition(
1509 const Decoration& decoration, const Instruction& inst) {
1510 if (spvIsVulkanEnv(_.context()->target_env)) {
1511 if (spv_result_t error = ValidateBool(
1512 decoration, inst,
1513 [this, &inst](const std::string& message) -> spv_result_t {
1514 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
1515 << _.VkErrorID(4241)
1516 << "According to the Vulkan spec BuiltIn HelperInvocation "
1517 "variable needs to be a bool scalar. "
1518 << message;
1519 })) {
1520 return error;
1521 }
1522 }
1523
1524 // Seed at reference checks with this built-in.
1525 return ValidateHelperInvocationAtReference(decoration, inst, inst, inst);
1526 }
1527
ValidateHelperInvocationAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)1528 spv_result_t BuiltInsValidator::ValidateHelperInvocationAtReference(
1529 const Decoration& decoration, const Instruction& built_in_inst,
1530 const Instruction& referenced_inst,
1531 const Instruction& referenced_from_inst) {
1532 if (spvIsVulkanEnv(_.context()->target_env)) {
1533 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
1534 if (storage_class != spv::StorageClass::Max &&
1535 storage_class != spv::StorageClass::Input) {
1536 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1537 << _.VkErrorID(4240)
1538 << "Vulkan spec allows BuiltIn HelperInvocation to be only used "
1539 "for variables with Input storage class. "
1540 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1541 referenced_from_inst)
1542 << " " << GetStorageClassDesc(referenced_from_inst);
1543 }
1544
1545 for (const spv::ExecutionModel execution_model : execution_models_) {
1546 if (execution_model != spv::ExecutionModel::Fragment) {
1547 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1548 << _.VkErrorID(4239)
1549 << "Vulkan spec allows BuiltIn HelperInvocation to be used only "
1550 "with Fragment execution model. "
1551 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1552 referenced_from_inst, execution_model);
1553 }
1554 }
1555 }
1556
1557 if (function_id_ == 0) {
1558 // Propagate this rule to all dependant ids in the global scope.
1559 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
1560 std::bind(&BuiltInsValidator::ValidateHelperInvocationAtReference, this,
1561 decoration, built_in_inst, referenced_from_inst,
1562 std::placeholders::_1));
1563 }
1564
1565 return SPV_SUCCESS;
1566 }
1567
ValidateInvocationIdAtDefinition(const Decoration & decoration,const Instruction & inst)1568 spv_result_t BuiltInsValidator::ValidateInvocationIdAtDefinition(
1569 const Decoration& decoration, const Instruction& inst) {
1570 if (spvIsVulkanEnv(_.context()->target_env)) {
1571 if (spv_result_t error = ValidateI32(
1572 decoration, inst,
1573 [this, &inst](const std::string& message) -> spv_result_t {
1574 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
1575 << _.VkErrorID(4259)
1576 << "According to the Vulkan spec BuiltIn InvocationId "
1577 "variable needs to be a 32-bit int scalar. "
1578 << message;
1579 })) {
1580 return error;
1581 }
1582 }
1583
1584 // Seed at reference checks with this built-in.
1585 return ValidateInvocationIdAtReference(decoration, inst, inst, inst);
1586 }
1587
ValidateInvocationIdAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)1588 spv_result_t BuiltInsValidator::ValidateInvocationIdAtReference(
1589 const Decoration& decoration, const Instruction& built_in_inst,
1590 const Instruction& referenced_inst,
1591 const Instruction& referenced_from_inst) {
1592 if (spvIsVulkanEnv(_.context()->target_env)) {
1593 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
1594 if (storage_class != spv::StorageClass::Max &&
1595 storage_class != spv::StorageClass::Input) {
1596 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1597 << _.VkErrorID(4258)
1598 << "Vulkan spec allows BuiltIn InvocationId to be only used for "
1599 "variables with Input storage class. "
1600 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1601 referenced_from_inst)
1602 << " " << GetStorageClassDesc(referenced_from_inst);
1603 }
1604
1605 for (const spv::ExecutionModel execution_model : execution_models_) {
1606 if (execution_model != spv::ExecutionModel::TessellationControl &&
1607 execution_model != spv::ExecutionModel::Geometry) {
1608 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1609 << _.VkErrorID(4257)
1610 << "Vulkan spec allows BuiltIn InvocationId to be used only "
1611 "with TessellationControl or Geometry execution models. "
1612 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1613 referenced_from_inst, execution_model);
1614 }
1615 }
1616 }
1617
1618 if (function_id_ == 0) {
1619 // Propagate this rule to all dependant ids in the global scope.
1620 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1621 &BuiltInsValidator::ValidateInvocationIdAtReference, this, decoration,
1622 built_in_inst, referenced_from_inst, std::placeholders::_1));
1623 }
1624
1625 return SPV_SUCCESS;
1626 }
1627
ValidateInstanceIndexAtDefinition(const Decoration & decoration,const Instruction & inst)1628 spv_result_t BuiltInsValidator::ValidateInstanceIndexAtDefinition(
1629 const Decoration& decoration, const Instruction& inst) {
1630 if (spvIsVulkanEnv(_.context()->target_env)) {
1631 if (spv_result_t error = ValidateI32(
1632 decoration, inst,
1633 [this, &inst](const std::string& message) -> spv_result_t {
1634 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
1635 << _.VkErrorID(4265) << "According to the "
1636 << spvLogStringForEnv(_.context()->target_env)
1637 << " spec BuiltIn InstanceIndex "
1638 "variable needs to be a 32-bit int scalar. "
1639 << message;
1640 })) {
1641 return error;
1642 }
1643 }
1644
1645 // Seed at reference checks with this built-in.
1646 return ValidateInstanceIndexAtReference(decoration, inst, inst, inst);
1647 }
1648
ValidateInstanceIndexAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)1649 spv_result_t BuiltInsValidator::ValidateInstanceIndexAtReference(
1650 const Decoration& decoration, const Instruction& built_in_inst,
1651 const Instruction& referenced_inst,
1652 const Instruction& referenced_from_inst) {
1653 if (spvIsVulkanEnv(_.context()->target_env)) {
1654 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
1655 if (storage_class != spv::StorageClass::Max &&
1656 storage_class != spv::StorageClass::Input) {
1657 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1658 << _.VkErrorID(4264) << spvLogStringForEnv(_.context()->target_env)
1659 << " spec allows BuiltIn InstanceIndex to be only used for "
1660 "variables with Input storage class. "
1661 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1662 referenced_from_inst)
1663 << " " << GetStorageClassDesc(referenced_from_inst);
1664 }
1665
1666 for (const spv::ExecutionModel execution_model : execution_models_) {
1667 if (execution_model != spv::ExecutionModel::Vertex) {
1668 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1669 << _.VkErrorID(4263)
1670 << spvLogStringForEnv(_.context()->target_env)
1671 << " spec allows BuiltIn InstanceIndex to be used only "
1672 "with Vertex execution model. "
1673 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1674 referenced_from_inst, execution_model);
1675 }
1676 }
1677 }
1678
1679 if (function_id_ == 0) {
1680 // Propagate this rule to all dependant ids in the global scope.
1681 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1682 &BuiltInsValidator::ValidateInstanceIndexAtReference, this, decoration,
1683 built_in_inst, referenced_from_inst, std::placeholders::_1));
1684 }
1685
1686 return SPV_SUCCESS;
1687 }
1688
ValidatePatchVerticesAtDefinition(const Decoration & decoration,const Instruction & inst)1689 spv_result_t BuiltInsValidator::ValidatePatchVerticesAtDefinition(
1690 const Decoration& decoration, const Instruction& inst) {
1691 if (spvIsVulkanEnv(_.context()->target_env)) {
1692 if (spv_result_t error = ValidateI32(
1693 decoration, inst,
1694 [this, &inst](const std::string& message) -> spv_result_t {
1695 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
1696 << _.VkErrorID(4310)
1697 << "According to the Vulkan spec BuiltIn PatchVertices "
1698 "variable needs to be a 32-bit int scalar. "
1699 << message;
1700 })) {
1701 return error;
1702 }
1703 }
1704
1705 // Seed at reference checks with this built-in.
1706 return ValidatePatchVerticesAtReference(decoration, inst, inst, inst);
1707 }
1708
ValidatePatchVerticesAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)1709 spv_result_t BuiltInsValidator::ValidatePatchVerticesAtReference(
1710 const Decoration& decoration, const Instruction& built_in_inst,
1711 const Instruction& referenced_inst,
1712 const Instruction& referenced_from_inst) {
1713 if (spvIsVulkanEnv(_.context()->target_env)) {
1714 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
1715 if (storage_class != spv::StorageClass::Max &&
1716 storage_class != spv::StorageClass::Input) {
1717 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1718 << _.VkErrorID(4309)
1719 << "Vulkan spec allows BuiltIn PatchVertices to be only used for "
1720 "variables with Input storage class. "
1721 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1722 referenced_from_inst)
1723 << " " << GetStorageClassDesc(referenced_from_inst);
1724 }
1725
1726 for (const spv::ExecutionModel execution_model : execution_models_) {
1727 if (execution_model != spv::ExecutionModel::TessellationControl &&
1728 execution_model != spv::ExecutionModel::TessellationEvaluation) {
1729 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1730 << _.VkErrorID(4308)
1731 << "Vulkan spec allows BuiltIn PatchVertices to be used only "
1732 "with TessellationControl or TessellationEvaluation "
1733 "execution models. "
1734 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1735 referenced_from_inst, execution_model);
1736 }
1737 }
1738 }
1739
1740 if (function_id_ == 0) {
1741 // Propagate this rule to all dependant ids in the global scope.
1742 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1743 &BuiltInsValidator::ValidatePatchVerticesAtReference, this, decoration,
1744 built_in_inst, referenced_from_inst, std::placeholders::_1));
1745 }
1746
1747 return SPV_SUCCESS;
1748 }
1749
ValidatePointCoordAtDefinition(const Decoration & decoration,const Instruction & inst)1750 spv_result_t BuiltInsValidator::ValidatePointCoordAtDefinition(
1751 const Decoration& decoration, const Instruction& inst) {
1752 if (spvIsVulkanEnv(_.context()->target_env)) {
1753 if (spv_result_t error = ValidateF32Vec(
1754 decoration, inst, 2,
1755 [this, &inst](const std::string& message) -> spv_result_t {
1756 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
1757 << _.VkErrorID(4313)
1758 << "According to the Vulkan spec BuiltIn PointCoord "
1759 "variable needs to be a 2-component 32-bit float "
1760 "vector. "
1761 << message;
1762 })) {
1763 return error;
1764 }
1765 }
1766
1767 // Seed at reference checks with this built-in.
1768 return ValidatePointCoordAtReference(decoration, inst, inst, inst);
1769 }
1770
ValidatePointCoordAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)1771 spv_result_t BuiltInsValidator::ValidatePointCoordAtReference(
1772 const Decoration& decoration, const Instruction& built_in_inst,
1773 const Instruction& referenced_inst,
1774 const Instruction& referenced_from_inst) {
1775 if (spvIsVulkanEnv(_.context()->target_env)) {
1776 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
1777 if (storage_class != spv::StorageClass::Max &&
1778 storage_class != spv::StorageClass::Input) {
1779 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1780 << _.VkErrorID(4312)
1781 << "Vulkan spec allows BuiltIn PointCoord to be only used for "
1782 "variables with Input storage class. "
1783 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1784 referenced_from_inst)
1785 << " " << GetStorageClassDesc(referenced_from_inst);
1786 }
1787
1788 for (const spv::ExecutionModel execution_model : execution_models_) {
1789 if (execution_model != spv::ExecutionModel::Fragment) {
1790 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1791 << _.VkErrorID(4311)
1792 << "Vulkan spec allows BuiltIn PointCoord to be used only with "
1793 "Fragment execution model. "
1794 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1795 referenced_from_inst, execution_model);
1796 }
1797 }
1798 }
1799
1800 if (function_id_ == 0) {
1801 // Propagate this rule to all dependant ids in the global scope.
1802 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1803 &BuiltInsValidator::ValidatePointCoordAtReference, this, decoration,
1804 built_in_inst, referenced_from_inst, std::placeholders::_1));
1805 }
1806
1807 return SPV_SUCCESS;
1808 }
1809
ValidatePointSizeAtDefinition(const Decoration & decoration,const Instruction & inst)1810 spv_result_t BuiltInsValidator::ValidatePointSizeAtDefinition(
1811 const Decoration& decoration, const Instruction& inst) {
1812 // Seed at reference checks with this built-in.
1813 return ValidatePointSizeAtReference(decoration, inst, inst, inst);
1814 }
1815
ValidatePointSizeAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)1816 spv_result_t BuiltInsValidator::ValidatePointSizeAtReference(
1817 const Decoration& decoration, const Instruction& built_in_inst,
1818 const Instruction& referenced_inst,
1819 const Instruction& referenced_from_inst) {
1820 if (spvIsVulkanEnv(_.context()->target_env)) {
1821 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
1822 if (storage_class != spv::StorageClass::Max &&
1823 storage_class != spv::StorageClass::Input &&
1824 storage_class != spv::StorageClass::Output) {
1825 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1826 << _.VkErrorID(4316)
1827 << "Vulkan spec allows BuiltIn PointSize to be only used for "
1828 "variables with Input or Output storage class. "
1829 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1830 referenced_from_inst)
1831 << " " << GetStorageClassDesc(referenced_from_inst);
1832 }
1833
1834 if (storage_class == spv::StorageClass::Input) {
1835 assert(function_id_ == 0);
1836 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1837 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 4315,
1838 "Vulkan spec doesn't allow BuiltIn PointSize to be used for "
1839 "variables with Input storage class if execution model is "
1840 "Vertex.",
1841 spv::ExecutionModel::Vertex, decoration, built_in_inst,
1842 referenced_from_inst, std::placeholders::_1));
1843 }
1844
1845 for (const spv::ExecutionModel execution_model : execution_models_) {
1846 switch (execution_model) {
1847 case spv::ExecutionModel::Vertex: {
1848 if (spv_result_t error = ValidateF32(
1849 decoration, built_in_inst,
1850 [this, &referenced_from_inst](
1851 const std::string& message) -> spv_result_t {
1852 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1853 << _.VkErrorID(4317)
1854 << "According to the Vulkan spec BuiltIn PointSize "
1855 "variable needs to be a 32-bit float scalar. "
1856 << message;
1857 })) {
1858 return error;
1859 }
1860 break;
1861 }
1862 case spv::ExecutionModel::TessellationControl:
1863 case spv::ExecutionModel::TessellationEvaluation:
1864 case spv::ExecutionModel::Geometry:
1865 case spv::ExecutionModel::MeshNV:
1866 case spv::ExecutionModel::MeshEXT: {
1867 // PointSize can be a per-vertex variable for tessellation control,
1868 // tessellation evaluation and geometry shader stages. In such cases
1869 // variables will have an array of 32-bit floats.
1870 if (decoration.struct_member_index() != Decoration::kInvalidMember) {
1871 // The array is on the variable, so this must be a 32-bit float.
1872 if (spv_result_t error = ValidateF32(
1873 decoration, built_in_inst,
1874 [this, &referenced_from_inst](
1875 const std::string& message) -> spv_result_t {
1876 return _.diag(SPV_ERROR_INVALID_DATA,
1877 &referenced_from_inst)
1878 << _.VkErrorID(4317)
1879 << "According to the Vulkan spec BuiltIn "
1880 "PointSize variable needs to be a 32-bit "
1881 "float scalar. "
1882 << message;
1883 })) {
1884 return error;
1885 }
1886 } else {
1887 if (spv_result_t error = ValidateOptionalArrayedF32(
1888 decoration, built_in_inst,
1889 [this, &referenced_from_inst](
1890 const std::string& message) -> spv_result_t {
1891 return _.diag(SPV_ERROR_INVALID_DATA,
1892 &referenced_from_inst)
1893 << _.VkErrorID(4317)
1894 << "According to the Vulkan spec BuiltIn "
1895 "PointSize variable needs to be a 32-bit "
1896 "float scalar. "
1897 << message;
1898 })) {
1899 return error;
1900 }
1901 }
1902 break;
1903 }
1904
1905 default: {
1906 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1907 << _.VkErrorID(4314)
1908 << "Vulkan spec allows BuiltIn PointSize to be used only with "
1909 "Vertex, TessellationControl, TessellationEvaluation or "
1910 "Geometry execution models. "
1911 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1912 referenced_from_inst, execution_model);
1913 }
1914 }
1915 }
1916 }
1917
1918 if (function_id_ == 0) {
1919 // Propagate this rule to all dependant ids in the global scope.
1920 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1921 &BuiltInsValidator::ValidatePointSizeAtReference, this, decoration,
1922 built_in_inst, referenced_from_inst, std::placeholders::_1));
1923 }
1924
1925 return SPV_SUCCESS;
1926 }
1927
ValidatePositionAtDefinition(const Decoration & decoration,const Instruction & inst)1928 spv_result_t BuiltInsValidator::ValidatePositionAtDefinition(
1929 const Decoration& decoration, const Instruction& inst) {
1930 // Seed at reference checks with this built-in.
1931 return ValidatePositionAtReference(decoration, inst, inst, inst);
1932 }
1933
ValidatePositionAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)1934 spv_result_t BuiltInsValidator::ValidatePositionAtReference(
1935 const Decoration& decoration, const Instruction& built_in_inst,
1936 const Instruction& referenced_inst,
1937 const Instruction& referenced_from_inst) {
1938 if (spvIsVulkanEnv(_.context()->target_env)) {
1939 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
1940 if (storage_class != spv::StorageClass::Max &&
1941 storage_class != spv::StorageClass::Input &&
1942 storage_class != spv::StorageClass::Output) {
1943 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1944 << _.VkErrorID(4320) << "Vulkan spec allows BuiltIn Position to be only used for "
1945 "variables with Input or Output storage class. "
1946 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1947 referenced_from_inst)
1948 << " " << GetStorageClassDesc(referenced_from_inst);
1949 }
1950
1951 if (storage_class == spv::StorageClass::Input) {
1952 assert(function_id_ == 0);
1953 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1954 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 4319,
1955 "Vulkan spec doesn't allow BuiltIn Position to be used "
1956 "for variables "
1957 "with Input storage class if execution model is Vertex.",
1958 spv::ExecutionModel::Vertex, decoration, built_in_inst,
1959 referenced_from_inst, std::placeholders::_1));
1960 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1961 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 4319,
1962 "Vulkan spec doesn't allow BuiltIn Position to be used "
1963 "for variables "
1964 "with Input storage class if execution model is MeshNV.",
1965 spv::ExecutionModel::MeshNV, decoration, built_in_inst,
1966 referenced_from_inst, std::placeholders::_1));
1967 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1968 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 4319,
1969 "Vulkan spec doesn't allow BuiltIn Position to be used "
1970 "for variables "
1971 "with Input storage class if execution model is MeshEXT.",
1972 spv::ExecutionModel::MeshEXT, decoration, built_in_inst,
1973 referenced_from_inst, std::placeholders::_1));
1974 }
1975
1976 for (const spv::ExecutionModel execution_model : execution_models_) {
1977 switch (execution_model) {
1978 case spv::ExecutionModel::Vertex: {
1979 if (spv_result_t error = ValidateF32Vec(
1980 decoration, built_in_inst, 4,
1981 [this, &referenced_from_inst](
1982 const std::string& message) -> spv_result_t {
1983 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1984 << _.VkErrorID(4321)
1985 << "According to the Vulkan spec BuiltIn Position "
1986 "variable needs to be a 4-component 32-bit float "
1987 "vector. "
1988 << message;
1989 })) {
1990 return error;
1991 }
1992 break;
1993 }
1994 case spv::ExecutionModel::Geometry:
1995 case spv::ExecutionModel::TessellationControl:
1996 case spv::ExecutionModel::TessellationEvaluation:
1997 case spv::ExecutionModel::MeshNV:
1998 case spv::ExecutionModel::MeshEXT: {
1999 // Position can be a per-vertex variable for tessellation control,
2000 // tessellation evaluation, geometry and mesh shader stages. In such
2001 // cases variables will have an array of 4-component 32-bit float
2002 // vectors.
2003 if (decoration.struct_member_index() != Decoration::kInvalidMember) {
2004 // The array is on the variable, so this must be a 4-component
2005 // 32-bit float vector.
2006 if (spv_result_t error = ValidateF32Vec(
2007 decoration, built_in_inst, 4,
2008 [this, &referenced_from_inst](
2009 const std::string& message) -> spv_result_t {
2010 return _.diag(SPV_ERROR_INVALID_DATA,
2011 &referenced_from_inst)
2012 << _.VkErrorID(4321)
2013 << "According to the Vulkan spec BuiltIn Position "
2014 "variable needs to be a 4-component 32-bit "
2015 "float vector. "
2016 << message;
2017 })) {
2018 return error;
2019 }
2020 } else {
2021 if (spv_result_t error = ValidateOptionalArrayedF32Vec(
2022 decoration, built_in_inst, 4,
2023 [this, &referenced_from_inst](
2024 const std::string& message) -> spv_result_t {
2025 return _.diag(SPV_ERROR_INVALID_DATA,
2026 &referenced_from_inst)
2027 << _.VkErrorID(4321)
2028 << "According to the Vulkan spec BuiltIn Position "
2029 "variable needs to be a 4-component 32-bit "
2030 "float vector. "
2031 << message;
2032 })) {
2033 return error;
2034 }
2035 }
2036 break;
2037 }
2038
2039 default: {
2040 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2041 << _.VkErrorID(4318)
2042 << "Vulkan spec allows BuiltIn Position to be used only "
2043 "with Vertex, TessellationControl, TessellationEvaluation"
2044 " or Geometry execution models. "
2045 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2046 referenced_from_inst, execution_model);
2047 }
2048 }
2049 }
2050 }
2051
2052 if (function_id_ == 0) {
2053 // Propagate this rule to all dependant ids in the global scope.
2054 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2055 &BuiltInsValidator::ValidatePositionAtReference, this, decoration,
2056 built_in_inst, referenced_from_inst, std::placeholders::_1));
2057 }
2058
2059 return SPV_SUCCESS;
2060 }
2061
ValidatePrimitiveIdAtDefinition(const Decoration & decoration,const Instruction & inst)2062 spv_result_t BuiltInsValidator::ValidatePrimitiveIdAtDefinition(
2063 const Decoration& decoration, const Instruction& inst) {
2064 if (spvIsVulkanEnv(_.context()->target_env)) {
2065 // PrimitiveId can be a per-primitive variable for mesh shader stage.
2066 // In such cases variable will have an array of 32-bit integers.
2067 if (decoration.struct_member_index() != Decoration::kInvalidMember) {
2068 // This must be a 32-bit int scalar.
2069 if (spv_result_t error = ValidateI32(
2070 decoration, inst,
2071 [this, &inst](const std::string& message) -> spv_result_t {
2072 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2073 << _.VkErrorID(4337)
2074 << "According to the Vulkan spec BuiltIn PrimitiveId "
2075 "variable needs to be a 32-bit int scalar. "
2076 << message;
2077 })) {
2078 return error;
2079 }
2080 } else {
2081 if (spv_result_t error = ValidateOptionalArrayedI32(
2082 decoration, inst,
2083 [this, &inst](const std::string& message) -> spv_result_t {
2084 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2085 << _.VkErrorID(4337)
2086 << "According to the Vulkan spec BuiltIn PrimitiveId "
2087 "variable needs to be a 32-bit int scalar. "
2088 << message;
2089 })) {
2090 return error;
2091 }
2092 }
2093 }
2094
2095 // Seed at reference checks with this built-in.
2096 return ValidatePrimitiveIdAtReference(decoration, inst, inst, inst);
2097 }
2098
ValidatePrimitiveIdAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)2099 spv_result_t BuiltInsValidator::ValidatePrimitiveIdAtReference(
2100 const Decoration& decoration, const Instruction& built_in_inst,
2101 const Instruction& referenced_inst,
2102 const Instruction& referenced_from_inst) {
2103 if (spvIsVulkanEnv(_.context()->target_env)) {
2104 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
2105 if (storage_class != spv::StorageClass::Max &&
2106 storage_class != spv::StorageClass::Input &&
2107 storage_class != spv::StorageClass::Output) {
2108 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2109 << "Vulkan spec allows BuiltIn PrimitiveId to be only used for "
2110 "variables with Input or Output storage class. "
2111 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2112 referenced_from_inst)
2113 << " " << GetStorageClassDesc(referenced_from_inst);
2114 }
2115
2116 if (storage_class == spv::StorageClass::Output) {
2117 assert(function_id_ == 0);
2118 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2119 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 4334,
2120 "Vulkan spec doesn't allow BuiltIn PrimitiveId to be used for "
2121 "variables with Output storage class if execution model is "
2122 "TessellationControl.",
2123 spv::ExecutionModel::TessellationControl, decoration, built_in_inst,
2124 referenced_from_inst, std::placeholders::_1));
2125 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2126 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 4334,
2127 "Vulkan spec doesn't allow BuiltIn PrimitiveId to be used for "
2128 "variables with Output storage class if execution model is "
2129 "TessellationEvaluation.",
2130 spv::ExecutionModel::TessellationEvaluation, decoration, built_in_inst,
2131 referenced_from_inst, std::placeholders::_1));
2132 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2133 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 4334,
2134 "Vulkan spec doesn't allow BuiltIn PrimitiveId to be used for "
2135 "variables with Output storage class if execution model is "
2136 "Fragment.",
2137 spv::ExecutionModel::Fragment, decoration, built_in_inst,
2138 referenced_from_inst, std::placeholders::_1));
2139 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2140 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 4334,
2141 "Vulkan spec doesn't allow BuiltIn PrimitiveId to be used for "
2142 "variables with Output storage class if execution model is "
2143 "IntersectionKHR.",
2144 spv::ExecutionModel::IntersectionKHR, decoration, built_in_inst,
2145 referenced_from_inst, std::placeholders::_1));
2146 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2147 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 4334,
2148 "Vulkan spec doesn't allow BuiltIn PrimitiveId to be used for "
2149 "variables with Output storage class if execution model is "
2150 "AnyHitKHR.",
2151 spv::ExecutionModel::AnyHitKHR, decoration, built_in_inst,
2152 referenced_from_inst, std::placeholders::_1));
2153 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2154 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 4334,
2155 "Vulkan spec doesn't allow BuiltIn PrimitiveId to be used for "
2156 "variables with Output storage class if execution model is "
2157 "ClosestHitKHR.",
2158 spv::ExecutionModel::ClosestHitKHR, decoration, built_in_inst,
2159 referenced_from_inst, std::placeholders::_1));
2160 }
2161
2162 for (const spv::ExecutionModel execution_model : execution_models_) {
2163 switch (execution_model) {
2164 case spv::ExecutionModel::Fragment:
2165 case spv::ExecutionModel::TessellationControl:
2166 case spv::ExecutionModel::TessellationEvaluation:
2167 case spv::ExecutionModel::Geometry:
2168 case spv::ExecutionModel::MeshNV:
2169 case spv::ExecutionModel::MeshEXT:
2170 case spv::ExecutionModel::IntersectionKHR:
2171 case spv::ExecutionModel::AnyHitKHR:
2172 case spv::ExecutionModel::ClosestHitKHR: {
2173 // Ok.
2174 break;
2175 }
2176
2177 default: {
2178 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2179 << _.VkErrorID(4330)
2180 << "Vulkan spec allows BuiltIn PrimitiveId to be used only "
2181 "with Fragment, TessellationControl, "
2182 "TessellationEvaluation, Geometry, MeshNV, MeshEXT, "
2183 "IntersectionKHR, AnyHitKHR, and ClosestHitKHR execution models. "
2184 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2185 referenced_from_inst, execution_model);
2186 }
2187 }
2188 }
2189 }
2190
2191 if (function_id_ == 0) {
2192 // Propagate this rule to all dependant ids in the global scope.
2193 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2194 &BuiltInsValidator::ValidatePrimitiveIdAtReference, this, decoration,
2195 built_in_inst, referenced_from_inst, std::placeholders::_1));
2196 }
2197
2198 return SPV_SUCCESS;
2199 }
2200
ValidateSampleIdAtDefinition(const Decoration & decoration,const Instruction & inst)2201 spv_result_t BuiltInsValidator::ValidateSampleIdAtDefinition(
2202 const Decoration& decoration, const Instruction& inst) {
2203 if (spvIsVulkanEnv(_.context()->target_env)) {
2204 if (spv_result_t error = ValidateI32(
2205 decoration, inst,
2206 [this, &inst](const std::string& message) -> spv_result_t {
2207 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2208 << _.VkErrorID(4356)
2209 << "According to the Vulkan spec BuiltIn SampleId "
2210 "variable needs to be a 32-bit int scalar. "
2211 << message;
2212 })) {
2213 return error;
2214 }
2215 }
2216
2217 // Seed at reference checks with this built-in.
2218 return ValidateSampleIdAtReference(decoration, inst, inst, inst);
2219 }
2220
ValidateSampleIdAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)2221 spv_result_t BuiltInsValidator::ValidateSampleIdAtReference(
2222 const Decoration& decoration, const Instruction& built_in_inst,
2223 const Instruction& referenced_inst,
2224 const Instruction& referenced_from_inst) {
2225 if (spvIsVulkanEnv(_.context()->target_env)) {
2226 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
2227 if (storage_class != spv::StorageClass::Max &&
2228 storage_class != spv::StorageClass::Input) {
2229 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2230 << _.VkErrorID(4355)
2231 << "Vulkan spec allows BuiltIn SampleId to be only used for "
2232 "variables with Input storage class. "
2233 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2234 referenced_from_inst)
2235 << " " << GetStorageClassDesc(referenced_from_inst);
2236 }
2237
2238 for (const spv::ExecutionModel execution_model : execution_models_) {
2239 if (execution_model != spv::ExecutionModel::Fragment) {
2240 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2241 << _.VkErrorID(4354)
2242 << "Vulkan spec allows BuiltIn SampleId to be used only with "
2243 "Fragment execution model. "
2244 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2245 referenced_from_inst, execution_model);
2246 }
2247 }
2248 }
2249
2250 if (function_id_ == 0) {
2251 // Propagate this rule to all dependant ids in the global scope.
2252 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2253 &BuiltInsValidator::ValidateSampleIdAtReference, this, decoration,
2254 built_in_inst, referenced_from_inst, std::placeholders::_1));
2255 }
2256
2257 return SPV_SUCCESS;
2258 }
2259
ValidateSampleMaskAtDefinition(const Decoration & decoration,const Instruction & inst)2260 spv_result_t BuiltInsValidator::ValidateSampleMaskAtDefinition(
2261 const Decoration& decoration, const Instruction& inst) {
2262 if (spvIsVulkanEnv(_.context()->target_env)) {
2263 if (spv_result_t error = ValidateI32Arr(
2264 decoration, inst,
2265 [this, &inst](const std::string& message) -> spv_result_t {
2266 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2267 << _.VkErrorID(4359)
2268 << "According to the Vulkan spec BuiltIn SampleMask "
2269 "variable needs to be a 32-bit int array. "
2270 << message;
2271 })) {
2272 return error;
2273 }
2274 }
2275
2276 // Seed at reference checks with this built-in.
2277 return ValidateSampleMaskAtReference(decoration, inst, inst, inst);
2278 }
2279
ValidateSampleMaskAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)2280 spv_result_t BuiltInsValidator::ValidateSampleMaskAtReference(
2281 const Decoration& decoration, const Instruction& built_in_inst,
2282 const Instruction& referenced_inst,
2283 const Instruction& referenced_from_inst) {
2284 if (spvIsVulkanEnv(_.context()->target_env)) {
2285 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
2286 if (storage_class != spv::StorageClass::Max &&
2287 storage_class != spv::StorageClass::Input &&
2288 storage_class != spv::StorageClass::Output) {
2289 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2290 << _.VkErrorID(4358)
2291 << "Vulkan spec allows BuiltIn SampleMask to be only used for "
2292 "variables with Input or Output storage class. "
2293 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2294 referenced_from_inst)
2295 << " " << GetStorageClassDesc(referenced_from_inst);
2296 }
2297
2298 for (const spv::ExecutionModel execution_model : execution_models_) {
2299 if (execution_model != spv::ExecutionModel::Fragment) {
2300 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2301 << _.VkErrorID(4357)
2302 << "Vulkan spec allows BuiltIn SampleMask to be used only "
2303 "with "
2304 "Fragment execution model. "
2305 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2306 referenced_from_inst, execution_model);
2307 }
2308 }
2309 }
2310
2311 if (function_id_ == 0) {
2312 // Propagate this rule to all dependant ids in the global scope.
2313 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2314 &BuiltInsValidator::ValidateSampleMaskAtReference, this, decoration,
2315 built_in_inst, referenced_from_inst, std::placeholders::_1));
2316 }
2317
2318 return SPV_SUCCESS;
2319 }
2320
ValidateSamplePositionAtDefinition(const Decoration & decoration,const Instruction & inst)2321 spv_result_t BuiltInsValidator::ValidateSamplePositionAtDefinition(
2322 const Decoration& decoration, const Instruction& inst) {
2323 if (spvIsVulkanEnv(_.context()->target_env)) {
2324 if (spv_result_t error = ValidateF32Vec(
2325 decoration, inst, 2,
2326 [this, &inst](const std::string& message) -> spv_result_t {
2327 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2328 << _.VkErrorID(4362)
2329 << "According to the Vulkan spec BuiltIn SamplePosition "
2330 "variable needs to be a 2-component 32-bit float "
2331 "vector. "
2332 << message;
2333 })) {
2334 return error;
2335 }
2336 }
2337
2338 // Seed at reference checks with this built-in.
2339 return ValidateSamplePositionAtReference(decoration, inst, inst, inst);
2340 }
2341
ValidateSamplePositionAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)2342 spv_result_t BuiltInsValidator::ValidateSamplePositionAtReference(
2343 const Decoration& decoration, const Instruction& built_in_inst,
2344 const Instruction& referenced_inst,
2345 const Instruction& referenced_from_inst) {
2346 if (spvIsVulkanEnv(_.context()->target_env)) {
2347 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
2348 if (storage_class != spv::StorageClass::Max &&
2349 storage_class != spv::StorageClass::Input) {
2350 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2351 << _.VkErrorID(4361)
2352 << "Vulkan spec allows BuiltIn SamplePosition to be only used "
2353 "for "
2354 "variables with Input storage class. "
2355 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2356 referenced_from_inst)
2357 << " " << GetStorageClassDesc(referenced_from_inst);
2358 }
2359
2360 for (const spv::ExecutionModel execution_model : execution_models_) {
2361 if (execution_model != spv::ExecutionModel::Fragment) {
2362 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2363 << _.VkErrorID(4360)
2364 << "Vulkan spec allows BuiltIn SamplePosition to be used only "
2365 "with "
2366 "Fragment execution model. "
2367 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2368 referenced_from_inst, execution_model);
2369 }
2370 }
2371 }
2372
2373 if (function_id_ == 0) {
2374 // Propagate this rule to all dependant ids in the global scope.
2375 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2376 &BuiltInsValidator::ValidateSamplePositionAtReference, this, decoration,
2377 built_in_inst, referenced_from_inst, std::placeholders::_1));
2378 }
2379
2380 return SPV_SUCCESS;
2381 }
2382
ValidateTessCoordAtDefinition(const Decoration & decoration,const Instruction & inst)2383 spv_result_t BuiltInsValidator::ValidateTessCoordAtDefinition(
2384 const Decoration& decoration, const Instruction& inst) {
2385 if (spvIsVulkanEnv(_.context()->target_env)) {
2386 if (spv_result_t error = ValidateF32Vec(
2387 decoration, inst, 3,
2388 [this, &inst](const std::string& message) -> spv_result_t {
2389 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2390 << _.VkErrorID(4389)
2391 << "According to the Vulkan spec BuiltIn TessCoord "
2392 "variable needs to be a 3-component 32-bit float "
2393 "vector. "
2394 << message;
2395 })) {
2396 return error;
2397 }
2398 }
2399
2400 // Seed at reference checks with this built-in.
2401 return ValidateTessCoordAtReference(decoration, inst, inst, inst);
2402 }
2403
ValidateTessCoordAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)2404 spv_result_t BuiltInsValidator::ValidateTessCoordAtReference(
2405 const Decoration& decoration, const Instruction& built_in_inst,
2406 const Instruction& referenced_inst,
2407 const Instruction& referenced_from_inst) {
2408 if (spvIsVulkanEnv(_.context()->target_env)) {
2409 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
2410 if (storage_class != spv::StorageClass::Max &&
2411 storage_class != spv::StorageClass::Input) {
2412 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2413 << _.VkErrorID(4388)
2414 << "Vulkan spec allows BuiltIn TessCoord to be only used for "
2415 "variables with Input storage class. "
2416 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2417 referenced_from_inst)
2418 << " " << GetStorageClassDesc(referenced_from_inst);
2419 }
2420
2421 for (const spv::ExecutionModel execution_model : execution_models_) {
2422 if (execution_model != spv::ExecutionModel::TessellationEvaluation) {
2423 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2424 << _.VkErrorID(4387)
2425 << "Vulkan spec allows BuiltIn TessCoord to be used only with "
2426 "TessellationEvaluation execution model. "
2427 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2428 referenced_from_inst, execution_model);
2429 }
2430 }
2431 }
2432
2433 if (function_id_ == 0) {
2434 // Propagate this rule to all dependant ids in the global scope.
2435 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2436 &BuiltInsValidator::ValidateTessCoordAtReference, this, decoration,
2437 built_in_inst, referenced_from_inst, std::placeholders::_1));
2438 }
2439
2440 return SPV_SUCCESS;
2441 }
2442
ValidateTessLevelOuterAtDefinition(const Decoration & decoration,const Instruction & inst)2443 spv_result_t BuiltInsValidator::ValidateTessLevelOuterAtDefinition(
2444 const Decoration& decoration, const Instruction& inst) {
2445 if (spvIsVulkanEnv(_.context()->target_env)) {
2446 if (spv_result_t error = ValidateF32Arr(
2447 decoration, inst, 4,
2448 [this, &inst](const std::string& message) -> spv_result_t {
2449 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2450 << _.VkErrorID(4393)
2451 << "According to the Vulkan spec BuiltIn TessLevelOuter "
2452 "variable needs to be a 4-component 32-bit float "
2453 "array. "
2454 << message;
2455 })) {
2456 return error;
2457 }
2458 }
2459
2460 // Seed at reference checks with this built-in.
2461 return ValidateTessLevelAtReference(decoration, inst, inst, inst);
2462 }
2463
ValidateTessLevelInnerAtDefinition(const Decoration & decoration,const Instruction & inst)2464 spv_result_t BuiltInsValidator::ValidateTessLevelInnerAtDefinition(
2465 const Decoration& decoration, const Instruction& inst) {
2466 if (spvIsVulkanEnv(_.context()->target_env)) {
2467 if (spv_result_t error = ValidateF32Arr(
2468 decoration, inst, 2,
2469 [this, &inst](const std::string& message) -> spv_result_t {
2470 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2471 << _.VkErrorID(4397)
2472 << "According to the Vulkan spec BuiltIn TessLevelOuter "
2473 "variable needs to be a 2-component 32-bit float "
2474 "array. "
2475 << message;
2476 })) {
2477 return error;
2478 }
2479 }
2480
2481 // Seed at reference checks with this built-in.
2482 return ValidateTessLevelAtReference(decoration, inst, inst, inst);
2483 }
2484
ValidateTessLevelAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)2485 spv_result_t BuiltInsValidator::ValidateTessLevelAtReference(
2486 const Decoration& decoration, const Instruction& built_in_inst,
2487 const Instruction& referenced_inst,
2488 const Instruction& referenced_from_inst) {
2489 uint32_t operand = decoration.params()[0];
2490 if (spvIsVulkanEnv(_.context()->target_env)) {
2491 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
2492 if (storage_class != spv::StorageClass::Max &&
2493 storage_class != spv::StorageClass::Input &&
2494 storage_class != spv::StorageClass::Output) {
2495 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2496 << "Vulkan spec allows BuiltIn "
2497 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2498 operand)
2499 << " to be only used for variables with Input or Output storage "
2500 "class. "
2501 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2502 referenced_from_inst)
2503 << " " << GetStorageClassDesc(referenced_from_inst);
2504 }
2505
2506 if (storage_class == spv::StorageClass::Input) {
2507 assert(function_id_ == 0);
2508 uint32_t vuid = (spv::BuiltIn(decoration.params()[0]) == spv::BuiltIn::TessLevelOuter) ? 4391 : 4395;
2509 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2510 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, vuid,
2511 "Vulkan spec doesn't allow TessLevelOuter/TessLevelInner to be "
2512 "used "
2513 "for variables with Input storage class if execution model is "
2514 "TessellationControl.",
2515 spv::ExecutionModel::TessellationControl, decoration, built_in_inst,
2516 referenced_from_inst, std::placeholders::_1));
2517 }
2518
2519 if (storage_class == spv::StorageClass::Output) {
2520 assert(function_id_ == 0);
2521 uint32_t vuid = (spv::BuiltIn(decoration.params()[0]) == spv::BuiltIn::TessLevelOuter) ? 4392 : 4396;
2522 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2523 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, vuid,
2524 "Vulkan spec doesn't allow TessLevelOuter/TessLevelInner to be "
2525 "used "
2526 "for variables with Output storage class if execution model is "
2527 "TessellationEvaluation.",
2528 spv::ExecutionModel::TessellationEvaluation, decoration, built_in_inst,
2529 referenced_from_inst, std::placeholders::_1));
2530 }
2531
2532 for (const spv::ExecutionModel execution_model : execution_models_) {
2533 switch (execution_model) {
2534 case spv::ExecutionModel::TessellationControl:
2535 case spv::ExecutionModel::TessellationEvaluation: {
2536 // Ok.
2537 break;
2538 }
2539
2540 default: {
2541 uint32_t vuid = (spv::BuiltIn(operand) == spv::BuiltIn::TessLevelOuter) ? 4390 : 4394;
2542 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2543 << _.VkErrorID(vuid) << "Vulkan spec allows BuiltIn "
2544 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2545 operand)
2546 << " to be used only with TessellationControl or "
2547 "TessellationEvaluation execution models. "
2548 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2549 referenced_from_inst, execution_model);
2550 }
2551 }
2552 }
2553 }
2554
2555 if (function_id_ == 0) {
2556 // Propagate this rule to all dependant ids in the global scope.
2557 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2558 &BuiltInsValidator::ValidateTessLevelAtReference, this, decoration,
2559 built_in_inst, referenced_from_inst, std::placeholders::_1));
2560 }
2561
2562 return SPV_SUCCESS;
2563 }
2564
ValidateVertexIndexAtDefinition(const Decoration & decoration,const Instruction & inst)2565 spv_result_t BuiltInsValidator::ValidateVertexIndexAtDefinition(
2566 const Decoration& decoration, const Instruction& inst) {
2567 if (spvIsVulkanEnv(_.context()->target_env)) {
2568 if (spv_result_t error = ValidateI32(
2569 decoration, inst,
2570 [this, &inst](const std::string& message) -> spv_result_t {
2571 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2572 << _.VkErrorID(4400) << "According to the "
2573 << spvLogStringForEnv(_.context()->target_env)
2574 << " spec BuiltIn VertexIndex variable needs to be a "
2575 "32-bit int scalar. "
2576 << message;
2577 })) {
2578 return error;
2579 }
2580 }
2581
2582 // Seed at reference checks with this built-in.
2583 return ValidateVertexIndexAtReference(decoration, inst, inst, inst);
2584 }
2585
ValidateVertexIdAtDefinition(const Decoration & decoration,const Instruction & inst)2586 spv_result_t BuiltInsValidator::ValidateVertexIdAtDefinition(
2587 const Decoration& decoration, const Instruction& inst) {
2588 (void)decoration;
2589 if (spvIsVulkanEnv(_.context()->target_env)) {
2590 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2591 << "Vulkan spec doesn't allow BuiltIn VertexId "
2592 "to be used.";
2593 }
2594
2595 return SPV_SUCCESS;
2596 }
2597
ValidateLocalInvocationIndexAtDefinition(const Decoration & decoration,const Instruction & inst)2598 spv_result_t BuiltInsValidator::ValidateLocalInvocationIndexAtDefinition(
2599 const Decoration& decoration, const Instruction& inst) {
2600 // Seed at reference checks with this built-in.
2601 return ValidateLocalInvocationIndexAtReference(decoration, inst, inst, inst);
2602 }
2603
ValidateLocalInvocationIndexAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction &,const Instruction & referenced_from_inst)2604 spv_result_t BuiltInsValidator::ValidateLocalInvocationIndexAtReference(
2605 const Decoration& decoration, const Instruction& built_in_inst,
2606 const Instruction&,
2607 const Instruction& referenced_from_inst) {
2608 if (function_id_ == 0) {
2609 // Propagate this rule to all dependant ids in the global scope.
2610 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
2611 std::bind(&BuiltInsValidator::ValidateLocalInvocationIndexAtReference,
2612 this, decoration, built_in_inst, referenced_from_inst,
2613 std::placeholders::_1));
2614 }
2615
2616 return SPV_SUCCESS;
2617 }
2618
ValidateVertexIndexAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)2619 spv_result_t BuiltInsValidator::ValidateVertexIndexAtReference(
2620 const Decoration& decoration, const Instruction& built_in_inst,
2621 const Instruction& referenced_inst,
2622 const Instruction& referenced_from_inst) {
2623 if (spvIsVulkanEnv(_.context()->target_env)) {
2624 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
2625 if (storage_class != spv::StorageClass::Max &&
2626 storage_class != spv::StorageClass::Input) {
2627 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2628 << _.VkErrorID(4399) << spvLogStringForEnv(_.context()->target_env)
2629 << " spec allows BuiltIn VertexIndex to be only used for "
2630 "variables with Input storage class. "
2631 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2632 referenced_from_inst)
2633 << " " << GetStorageClassDesc(referenced_from_inst);
2634 }
2635
2636 for (const spv::ExecutionModel execution_model : execution_models_) {
2637 if (execution_model != spv::ExecutionModel::Vertex) {
2638 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2639 << _.VkErrorID(4398)
2640 << spvLogStringForEnv(_.context()->target_env)
2641 << " spec allows BuiltIn VertexIndex to be used only with "
2642 "Vertex execution model. "
2643 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2644 referenced_from_inst, execution_model);
2645 }
2646 }
2647 }
2648
2649 if (function_id_ == 0) {
2650 // Propagate this rule to all dependant ids in the global scope.
2651 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2652 &BuiltInsValidator::ValidateVertexIndexAtReference, this, decoration,
2653 built_in_inst, referenced_from_inst, std::placeholders::_1));
2654 }
2655
2656 return SPV_SUCCESS;
2657 }
2658
ValidateLayerOrViewportIndexAtDefinition(const Decoration & decoration,const Instruction & inst)2659 spv_result_t BuiltInsValidator::ValidateLayerOrViewportIndexAtDefinition(
2660 const Decoration& decoration, const Instruction& inst) {
2661 if (spvIsVulkanEnv(_.context()->target_env)) {
2662 // This can be a per-primitive variable for mesh shader stage.
2663 // In such cases variable will have an array of 32-bit integers.
2664 if (decoration.struct_member_index() != Decoration::kInvalidMember) {
2665 // This must be a 32-bit int scalar.
2666 if (spv_result_t error = ValidateI32(
2667 decoration, inst,
2668 [this, &decoration,
2669 &inst](const std::string& message) -> spv_result_t {
2670 uint32_t vuid =
2671 (spv::BuiltIn(decoration.params()[0]) == spv::BuiltIn::Layer) ? 4276 : 4408;
2672 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2673 << _.VkErrorID(vuid)
2674 << "According to the Vulkan spec BuiltIn "
2675 << _.grammar().lookupOperandName(
2676 SPV_OPERAND_TYPE_BUILT_IN, decoration.params()[0])
2677 << "variable needs to be a 32-bit int scalar. "
2678 << message;
2679 })) {
2680 return error;
2681 }
2682 } else {
2683 if (spv_result_t error = ValidateOptionalArrayedI32(
2684 decoration, inst,
2685 [this, &decoration,
2686 &inst](const std::string& message) -> spv_result_t {
2687 uint32_t vuid =
2688 (spv::BuiltIn(decoration.params()[0]) == spv::BuiltIn::Layer) ? 4276 : 4408;
2689 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2690 << _.VkErrorID(vuid)
2691 << "According to the Vulkan spec BuiltIn "
2692 << _.grammar().lookupOperandName(
2693 SPV_OPERAND_TYPE_BUILT_IN, decoration.params()[0])
2694 << "variable needs to be a 32-bit int scalar. "
2695 << message;
2696 })) {
2697 return error;
2698 }
2699 }
2700 }
2701
2702 // Seed at reference checks with this built-in.
2703 return ValidateLayerOrViewportIndexAtReference(decoration, inst, inst, inst);
2704 }
2705
ValidateLayerOrViewportIndexAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)2706 spv_result_t BuiltInsValidator::ValidateLayerOrViewportIndexAtReference(
2707 const Decoration& decoration, const Instruction& built_in_inst,
2708 const Instruction& referenced_inst,
2709 const Instruction& referenced_from_inst) {
2710 uint32_t operand = decoration.params()[0];
2711 if (spvIsVulkanEnv(_.context()->target_env)) {
2712 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
2713 if (storage_class != spv::StorageClass::Max &&
2714 storage_class != spv::StorageClass::Input &&
2715 storage_class != spv::StorageClass::Output) {
2716 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2717 << "Vulkan spec allows BuiltIn "
2718 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2719 operand)
2720 << " to be only used for variables with Input or Output storage "
2721 "class. "
2722 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2723 referenced_from_inst)
2724 << " " << GetStorageClassDesc(referenced_from_inst);
2725 }
2726
2727 if (storage_class == spv::StorageClass::Input) {
2728 assert(function_id_ == 0);
2729 for (const auto em :
2730 {spv::ExecutionModel::Vertex, spv::ExecutionModel::TessellationEvaluation,
2731 spv::ExecutionModel::Geometry, spv::ExecutionModel::MeshNV,
2732 spv::ExecutionModel::MeshEXT}) {
2733 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
2734 std::bind(&BuiltInsValidator::ValidateNotCalledWithExecutionModel,
2735 this, ((spv::BuiltIn(operand) == spv::BuiltIn::Layer) ? 4274 : 4406),
2736 "Vulkan spec doesn't allow BuiltIn Layer and "
2737 "ViewportIndex to be "
2738 "used for variables with Input storage class if "
2739 "execution model is Vertex, TessellationEvaluation, "
2740 "Geometry, MeshNV or MeshEXT.",
2741 em, decoration, built_in_inst, referenced_from_inst,
2742 std::placeholders::_1));
2743 }
2744 }
2745
2746 if (storage_class == spv::StorageClass::Output) {
2747 assert(function_id_ == 0);
2748 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
2749 std::bind(&BuiltInsValidator::ValidateNotCalledWithExecutionModel,
2750 this, ((spv::BuiltIn(operand) == spv::BuiltIn::Layer) ? 4275 : 4407),
2751 "Vulkan spec doesn't allow BuiltIn Layer and "
2752 "ViewportIndex to be "
2753 "used for variables with Output storage class if "
2754 "execution model is "
2755 "Fragment.",
2756 spv::ExecutionModel::Fragment, decoration, built_in_inst,
2757 referenced_from_inst, std::placeholders::_1));
2758 }
2759
2760 for (const spv::ExecutionModel execution_model : execution_models_) {
2761 switch (execution_model) {
2762 case spv::ExecutionModel::Geometry:
2763 case spv::ExecutionModel::Fragment:
2764 case spv::ExecutionModel::MeshNV:
2765 case spv::ExecutionModel::MeshEXT:
2766 // Ok.
2767 break;
2768 case spv::ExecutionModel::Vertex:
2769 case spv::ExecutionModel::TessellationEvaluation: {
2770 if (!_.HasCapability(spv::Capability::ShaderViewportIndexLayerEXT)) {
2771 if (spv::BuiltIn(operand) == spv::BuiltIn::ViewportIndex &&
2772 _.HasCapability(spv::Capability::ShaderViewportIndex))
2773 break; // Ok
2774 if (spv::BuiltIn(operand) == spv::BuiltIn::Layer &&
2775 _.HasCapability(spv::Capability::ShaderLayer))
2776 break; // Ok
2777
2778 const char* capability = "ShaderViewportIndexLayerEXT";
2779
2780 if (spv::BuiltIn(operand) == spv::BuiltIn::ViewportIndex)
2781 capability = "ShaderViewportIndexLayerEXT or ShaderViewportIndex";
2782 if (spv::BuiltIn(operand) == spv::BuiltIn::Layer)
2783 capability = "ShaderViewportIndexLayerEXT or ShaderLayer";
2784
2785 uint32_t vuid = (spv::BuiltIn(operand) == spv::BuiltIn::Layer) ? 4273 : 4405;
2786 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2787 << _.VkErrorID(vuid) << "Using BuiltIn "
2788 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2789 operand)
2790 << " in Vertex or Tessellation execution model requires the "
2791 << capability << " capability.";
2792 }
2793 break;
2794 }
2795 default: {
2796 uint32_t vuid = (spv::BuiltIn(operand) == spv::BuiltIn::Layer) ? 4272 : 4404;
2797 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2798 << _.VkErrorID(vuid) << "Vulkan spec allows BuiltIn "
2799 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2800 operand)
2801 << " to be used only with Vertex, TessellationEvaluation, "
2802 "Geometry, or Fragment execution models. "
2803 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2804 referenced_from_inst, execution_model);
2805 }
2806 }
2807 }
2808 }
2809
2810 if (function_id_ == 0) {
2811 // Propagate this rule to all dependant ids in the global scope.
2812 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
2813 std::bind(&BuiltInsValidator::ValidateLayerOrViewportIndexAtReference,
2814 this, decoration, built_in_inst, referenced_from_inst,
2815 std::placeholders::_1));
2816 }
2817
2818 return SPV_SUCCESS;
2819 }
2820
ValidateFragmentShaderF32Vec3InputAtDefinition(const Decoration & decoration,const Instruction & inst)2821 spv_result_t BuiltInsValidator::ValidateFragmentShaderF32Vec3InputAtDefinition(
2822 const Decoration& decoration, const Instruction& inst) {
2823 if (spvIsVulkanEnv(_.context()->target_env)) {
2824 const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
2825 if (spv_result_t error = ValidateF32Vec(
2826 decoration, inst, 3,
2827 [this, &inst, builtin](const std::string& message) -> spv_result_t {
2828 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
2829 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2830 << _.VkErrorID(vuid) << "According to the "
2831 << spvLogStringForEnv(_.context()->target_env)
2832 << " spec BuiltIn "
2833 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2834 uint32_t(builtin))
2835 << " variable needs to be a 3-component 32-bit float "
2836 "vector. "
2837 << message;
2838 })) {
2839 return error;
2840 }
2841 }
2842
2843 // Seed at reference checks with this built-in.
2844 return ValidateFragmentShaderF32Vec3InputAtReference(decoration, inst, inst,
2845 inst);
2846 }
2847
ValidateFragmentShaderF32Vec3InputAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)2848 spv_result_t BuiltInsValidator::ValidateFragmentShaderF32Vec3InputAtReference(
2849 const Decoration& decoration, const Instruction& built_in_inst,
2850 const Instruction& referenced_inst,
2851 const Instruction& referenced_from_inst) {
2852
2853 if (spvIsVulkanEnv(_.context()->target_env)) {
2854 const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
2855 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
2856 if (storage_class != spv::StorageClass::Max &&
2857 storage_class != spv::StorageClass::Input) {
2858 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
2859 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2860 << _.VkErrorID(vuid) << spvLogStringForEnv(_.context()->target_env)
2861 << " spec allows BuiltIn "
2862 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
2863 << " to be only used for variables with Input storage class. "
2864 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2865 referenced_from_inst)
2866 << " " << GetStorageClassDesc(referenced_from_inst);
2867 }
2868
2869 for (const spv::ExecutionModel execution_model : execution_models_) {
2870 if (execution_model != spv::ExecutionModel::Fragment) {
2871 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel);
2872 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2873 << _.VkErrorID(vuid)
2874 << spvLogStringForEnv(_.context()->target_env)
2875 << " spec allows BuiltIn "
2876 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
2877 << " to be used only with Fragment execution model. "
2878 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2879 referenced_from_inst, execution_model);
2880 }
2881 }
2882 }
2883
2884 if (function_id_ == 0) {
2885 // Propagate this rule to all dependant ids in the global scope.
2886 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2887 &BuiltInsValidator::ValidateFragmentShaderF32Vec3InputAtReference, this,
2888 decoration, built_in_inst, referenced_from_inst,
2889 std::placeholders::_1));
2890 }
2891
2892 return SPV_SUCCESS;
2893 }
2894
ValidateComputeShaderI32Vec3InputAtDefinition(const Decoration & decoration,const Instruction & inst)2895 spv_result_t BuiltInsValidator::ValidateComputeShaderI32Vec3InputAtDefinition(
2896 const Decoration& decoration, const Instruction& inst) {
2897 if (spvIsVulkanEnv(_.context()->target_env)) {
2898 const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
2899 if (spv_result_t error = ValidateI32Vec(
2900 decoration, inst, 3,
2901 [this, &inst, builtin](const std::string& message) -> spv_result_t {
2902 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
2903 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2904 << _.VkErrorID(vuid) << "According to the "
2905 << spvLogStringForEnv(_.context()->target_env)
2906 << " spec BuiltIn "
2907 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2908 uint32_t(builtin))
2909 << " variable needs to be a 3-component 32-bit int "
2910 "vector. "
2911 << message;
2912 })) {
2913 return error;
2914 }
2915 }
2916
2917 // Seed at reference checks with this built-in.
2918 return ValidateComputeShaderI32Vec3InputAtReference(decoration, inst, inst,
2919 inst);
2920 }
2921
ValidateComputeShaderI32Vec3InputAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)2922 spv_result_t BuiltInsValidator::ValidateComputeShaderI32Vec3InputAtReference(
2923 const Decoration& decoration, const Instruction& built_in_inst,
2924 const Instruction& referenced_inst,
2925 const Instruction& referenced_from_inst) {
2926 if (spvIsVulkanEnv(_.context()->target_env)) {
2927 const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
2928 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
2929 if (storage_class != spv::StorageClass::Max &&
2930 storage_class != spv::StorageClass::Input) {
2931 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
2932 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2933 << _.VkErrorID(vuid) << spvLogStringForEnv(_.context()->target_env)
2934 << " spec allows BuiltIn "
2935 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
2936 << " to be only used for variables with Input storage class. "
2937 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2938 referenced_from_inst)
2939 << " " << GetStorageClassDesc(referenced_from_inst);
2940 }
2941
2942 for (const spv::ExecutionModel execution_model : execution_models_) {
2943 bool has_vulkan_model = execution_model == spv::ExecutionModel::GLCompute ||
2944 execution_model == spv::ExecutionModel::TaskNV ||
2945 execution_model == spv::ExecutionModel::MeshNV ||
2946 execution_model == spv::ExecutionModel::TaskEXT ||
2947 execution_model == spv::ExecutionModel::MeshEXT;
2948
2949 if (spvIsVulkanEnv(_.context()->target_env) && !has_vulkan_model) {
2950 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel);
2951 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2952 << _.VkErrorID(vuid)
2953 << spvLogStringForEnv(_.context()->target_env)
2954 << " spec allows BuiltIn "
2955 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
2956 << " to be used only with GLCompute, MeshNV, TaskNV, MeshEXT or"
2957 << " TaskEXT execution model. "
2958 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2959 referenced_from_inst, execution_model);
2960 }
2961 }
2962 }
2963
2964 if (function_id_ == 0) {
2965 // Propagate this rule to all dependant ids in the global scope.
2966 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2967 &BuiltInsValidator::ValidateComputeShaderI32Vec3InputAtReference, this,
2968 decoration, built_in_inst, referenced_from_inst,
2969 std::placeholders::_1));
2970 }
2971
2972 return SPV_SUCCESS;
2973 }
2974
ValidateComputeI32InputAtDefinition(const Decoration & decoration,const Instruction & inst)2975 spv_result_t BuiltInsValidator::ValidateComputeI32InputAtDefinition(
2976 const Decoration& decoration, const Instruction& inst) {
2977 if (spvIsVulkanEnv(_.context()->target_env)) {
2978 const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
2979 if (decoration.struct_member_index() != Decoration::kInvalidMember) {
2980 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2981 << "BuiltIn "
2982 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
2983 << " cannot be used as a member decoration ";
2984 }
2985 if (spv_result_t error = ValidateI32(
2986 decoration, inst,
2987 [this, &inst, builtin](const std::string& message) -> spv_result_t {
2988 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
2989 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2990 << _.VkErrorID(vuid)
2991 << "According to the "
2992 << spvLogStringForEnv(_.context()->target_env)
2993 << " spec BuiltIn "
2994 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
2995 << " variable needs to be a 32-bit int "
2996 "vector. "
2997 << message;
2998 })) {
2999 return error;
3000 }
3001 }
3002
3003 // Seed at reference checks with this built-in.
3004 return ValidateComputeI32InputAtReference(decoration, inst, inst, inst);
3005 }
3006
ValidateComputeI32InputAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3007 spv_result_t BuiltInsValidator::ValidateComputeI32InputAtReference(
3008 const Decoration& decoration, const Instruction& built_in_inst,
3009 const Instruction& referenced_inst,
3010 const Instruction& referenced_from_inst) {
3011 if (spvIsVulkanEnv(_.context()->target_env)) {
3012 const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
3013 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
3014 if (storage_class != spv::StorageClass::Max &&
3015 storage_class != spv::StorageClass::Input) {
3016 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
3017 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3018 << _.VkErrorID(vuid)
3019 << spvLogStringForEnv(_.context()->target_env)
3020 << " spec allows BuiltIn "
3021 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
3022 << " to be only used for variables with Input storage class. "
3023 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3024 referenced_from_inst)
3025 << " " << GetStorageClassDesc(referenced_from_inst);
3026 }
3027
3028 for (const spv::ExecutionModel execution_model : execution_models_) {
3029 bool has_vulkan_model = execution_model == spv::ExecutionModel::GLCompute ||
3030 execution_model == spv::ExecutionModel::TaskNV ||
3031 execution_model == spv::ExecutionModel::MeshNV ||
3032 execution_model == spv::ExecutionModel::TaskEXT ||
3033 execution_model == spv::ExecutionModel::MeshEXT;
3034 if (spvIsVulkanEnv(_.context()->target_env) && !has_vulkan_model) {
3035 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel);
3036 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3037 << _.VkErrorID(vuid)
3038 << spvLogStringForEnv(_.context()->target_env)
3039 << " spec allows BuiltIn "
3040 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
3041 << " to be used only with GLCompute, MeshNV, TaskNV, MeshEXT or "
3042 << "TaskEXT execution model. "
3043 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3044 referenced_from_inst, execution_model);
3045 }
3046 }
3047 }
3048
3049 if (function_id_ == 0) {
3050 // Propagate this rule to all dependant ids in the global scope.
3051 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
3052 std::bind(&BuiltInsValidator::ValidateComputeI32InputAtReference, this,
3053 decoration, built_in_inst, referenced_from_inst,
3054 std::placeholders::_1));
3055 }
3056
3057 return SPV_SUCCESS;
3058 }
3059
ValidateI32InputAtDefinition(const Decoration & decoration,const Instruction & inst)3060 spv_result_t BuiltInsValidator::ValidateI32InputAtDefinition(
3061 const Decoration& decoration, const Instruction& inst) {
3062 if (spvIsVulkanEnv(_.context()->target_env)) {
3063 const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
3064 if (decoration.struct_member_index() != Decoration::kInvalidMember) {
3065 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3066 << "BuiltIn "
3067 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
3068 << " cannot be used as a member decoration ";
3069 }
3070 if (spv_result_t error = ValidateI32(
3071 decoration, inst,
3072 [this, &inst, builtin](const std::string& message) -> spv_result_t {
3073 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
3074 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3075 << _.VkErrorID(vuid)
3076 << "According to the "
3077 << spvLogStringForEnv(_.context()->target_env)
3078 << " spec BuiltIn "
3079 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
3080 << " variable needs to be a 32-bit int. " << message;
3081 })) {
3082 return error;
3083 }
3084
3085 const spv::StorageClass storage_class = GetStorageClass(inst);
3086 if (storage_class != spv::StorageClass::Max &&
3087 storage_class != spv::StorageClass::Input) {
3088 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
3089 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3090 << _.VkErrorID(vuid)
3091 << spvLogStringForEnv(_.context()->target_env)
3092 << " spec allows BuiltIn "
3093 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
3094 << " to be only used for variables with Input storage class. "
3095 << GetReferenceDesc(decoration, inst, inst, inst) << " "
3096 << GetStorageClassDesc(inst);
3097 }
3098 }
3099
3100 return SPV_SUCCESS;
3101 }
3102
ValidateI32Vec4InputAtDefinition(const Decoration & decoration,const Instruction & inst)3103 spv_result_t BuiltInsValidator::ValidateI32Vec4InputAtDefinition(
3104 const Decoration& decoration, const Instruction& inst) {
3105 if (spvIsVulkanEnv(_.context()->target_env)) {
3106 const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
3107 if (decoration.struct_member_index() != Decoration::kInvalidMember) {
3108 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3109 << "BuiltIn "
3110 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
3111 << " cannot be used as a member decoration ";
3112 }
3113 if (spv_result_t error = ValidateI32Vec(
3114 decoration, inst, 4,
3115 [this, &inst, builtin](const std::string& message) -> spv_result_t {
3116 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
3117 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3118 << _.VkErrorID(vuid)
3119 << "According to the "
3120 << spvLogStringForEnv(_.context()->target_env)
3121 << " spec BuiltIn "
3122 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
3123 << " variable needs to be a 4-component 32-bit int "
3124 "vector. "
3125 << message;
3126 })) {
3127 return error;
3128 }
3129
3130 const spv::StorageClass storage_class = GetStorageClass(inst);
3131 if (storage_class != spv::StorageClass::Max &&
3132 storage_class != spv::StorageClass::Input) {
3133 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
3134 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3135 << _.VkErrorID(vuid)
3136 << spvLogStringForEnv(_.context()->target_env)
3137 << " spec allows BuiltIn "
3138 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
3139 << " to be only used for variables with Input storage class. "
3140 << GetReferenceDesc(decoration, inst, inst, inst) << " "
3141 << GetStorageClassDesc(inst);
3142 }
3143 }
3144
3145 return SPV_SUCCESS;
3146 }
3147
ValidateWorkgroupSizeAtDefinition(const Decoration & decoration,const Instruction & inst)3148 spv_result_t BuiltInsValidator::ValidateWorkgroupSizeAtDefinition(
3149 const Decoration& decoration, const Instruction& inst) {
3150 if (spvIsVulkanEnv(_.context()->target_env)) {
3151 if (spvIsVulkanEnv(_.context()->target_env) &&
3152 !spvOpcodeIsConstant(inst.opcode())) {
3153 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3154 << _.VkErrorID(4426)
3155 << "Vulkan spec requires BuiltIn WorkgroupSize to be a "
3156 "constant. "
3157 << GetIdDesc(inst) << " is not a constant.";
3158 }
3159
3160 if (spv_result_t error = ValidateI32Vec(
3161 decoration, inst, 3,
3162 [this, &inst](const std::string& message) -> spv_result_t {
3163 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3164 << _.VkErrorID(4427) << "According to the "
3165 << spvLogStringForEnv(_.context()->target_env)
3166 << " spec BuiltIn WorkgroupSize variable needs to be a "
3167 "3-component 32-bit int vector. "
3168 << message;
3169 })) {
3170 return error;
3171 }
3172 }
3173
3174 // Seed at reference checks with this built-in.
3175 return ValidateWorkgroupSizeAtReference(decoration, inst, inst, inst);
3176 }
3177
ValidateWorkgroupSizeAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3178 spv_result_t BuiltInsValidator::ValidateWorkgroupSizeAtReference(
3179 const Decoration& decoration, const Instruction& built_in_inst,
3180 const Instruction& referenced_inst,
3181 const Instruction& referenced_from_inst) {
3182 if (spvIsVulkanEnv(_.context()->target_env)) {
3183 for (const spv::ExecutionModel execution_model : execution_models_) {
3184 if (execution_model != spv::ExecutionModel::GLCompute &&
3185 execution_model != spv::ExecutionModel::TaskNV &&
3186 execution_model != spv::ExecutionModel::MeshNV &&
3187 execution_model != spv::ExecutionModel::TaskEXT &&
3188 execution_model != spv::ExecutionModel::MeshEXT) {
3189 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3190 << _.VkErrorID(4425)
3191 << spvLogStringForEnv(_.context()->target_env)
3192 << " spec allows BuiltIn "
3193 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3194 decoration.params()[0])
3195 << " to be used only with GLCompute, MeshNV, TaskNV, MeshEXT or "
3196 << "TaskEXT execution model. "
3197 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3198 referenced_from_inst, execution_model);
3199 }
3200 }
3201 }
3202
3203 if (function_id_ == 0) {
3204 // Propagate this rule to all dependant ids in the global scope.
3205 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
3206 &BuiltInsValidator::ValidateWorkgroupSizeAtReference, this, decoration,
3207 built_in_inst, referenced_from_inst, std::placeholders::_1));
3208 }
3209
3210 return SPV_SUCCESS;
3211 }
3212
ValidateBaseInstanceOrVertexAtDefinition(const Decoration & decoration,const Instruction & inst)3213 spv_result_t BuiltInsValidator::ValidateBaseInstanceOrVertexAtDefinition(
3214 const Decoration& decoration, const Instruction& inst) {
3215 if (spvIsVulkanEnv(_.context()->target_env)) {
3216 if (spv_result_t error = ValidateI32(
3217 decoration, inst,
3218 [this, &inst,
3219 &decoration](const std::string& message) -> spv_result_t {
3220 uint32_t vuid = (spv::BuiltIn(decoration.params()[0]) == spv::BuiltIn::BaseInstance)
3221 ? 4183
3222 : 4186;
3223 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3224 << _.VkErrorID(vuid)
3225 << "According to the Vulkan spec BuiltIn "
3226 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3227 decoration.params()[0])
3228 << " variable needs to be a 32-bit int scalar. "
3229 << message;
3230 })) {
3231 return error;
3232 }
3233 }
3234
3235 return ValidateBaseInstanceOrVertexAtReference(decoration, inst, inst, inst);
3236 }
3237
ValidateBaseInstanceOrVertexAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3238 spv_result_t BuiltInsValidator::ValidateBaseInstanceOrVertexAtReference(
3239 const Decoration& decoration, const Instruction& built_in_inst,
3240 const Instruction& referenced_inst,
3241 const Instruction& referenced_from_inst) {
3242 uint32_t operand = decoration.params()[0];
3243 if (spvIsVulkanEnv(_.context()->target_env)) {
3244 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
3245 if (storage_class != spv::StorageClass::Max &&
3246 storage_class != spv::StorageClass::Input) {
3247 uint32_t vuid = (spv::BuiltIn(operand) == spv::BuiltIn::BaseInstance) ? 4182 : 4185;
3248 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3249 << _.VkErrorID(vuid) << "Vulkan spec allows BuiltIn "
3250 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3251 operand)
3252 << " to be only used for variables with Input storage class. "
3253 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3254 referenced_from_inst)
3255 << " " << GetStorageClassDesc(referenced_from_inst);
3256 }
3257
3258 for (const spv::ExecutionModel execution_model : execution_models_) {
3259 if (execution_model != spv::ExecutionModel::Vertex) {
3260 uint32_t vuid = (spv::BuiltIn(operand) == spv::BuiltIn::BaseInstance) ? 4181 : 4184;
3261 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3262 << _.VkErrorID(vuid) << "Vulkan spec allows BuiltIn "
3263 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3264 operand)
3265 << " to be used only with Vertex execution model. "
3266 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3267 referenced_from_inst, execution_model);
3268 }
3269 }
3270 }
3271
3272 if (function_id_ == 0) {
3273 // Propagate this rule to all dependant ids in the global scope.
3274 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
3275 std::bind(&BuiltInsValidator::ValidateBaseInstanceOrVertexAtReference,
3276 this, decoration, built_in_inst, referenced_from_inst,
3277 std::placeholders::_1));
3278 }
3279
3280 return SPV_SUCCESS;
3281 }
3282
ValidateDrawIndexAtDefinition(const Decoration & decoration,const Instruction & inst)3283 spv_result_t BuiltInsValidator::ValidateDrawIndexAtDefinition(
3284 const Decoration& decoration, const Instruction& inst) {
3285 if (spvIsVulkanEnv(_.context()->target_env)) {
3286 if (spv_result_t error = ValidateI32(
3287 decoration, inst,
3288 [this, &inst,
3289 &decoration](const std::string& message) -> spv_result_t {
3290 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3291 << _.VkErrorID(4209)
3292 << "According to the Vulkan spec BuiltIn "
3293 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3294 decoration.params()[0])
3295 << " variable needs to be a 32-bit int scalar. "
3296 << message;
3297 })) {
3298 return error;
3299 }
3300 }
3301
3302 return ValidateDrawIndexAtReference(decoration, inst, inst, inst);
3303 }
3304
ValidateDrawIndexAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3305 spv_result_t BuiltInsValidator::ValidateDrawIndexAtReference(
3306 const Decoration& decoration, const Instruction& built_in_inst,
3307 const Instruction& referenced_inst,
3308 const Instruction& referenced_from_inst) {
3309 uint32_t operand = decoration.params()[0];
3310 if (spvIsVulkanEnv(_.context()->target_env)) {
3311 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
3312 if (storage_class != spv::StorageClass::Max &&
3313 storage_class != spv::StorageClass::Input) {
3314 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3315 << _.VkErrorID(4208) << "Vulkan spec allows BuiltIn "
3316 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3317 operand)
3318 << " to be only used for variables with Input storage class. "
3319 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3320 referenced_from_inst)
3321 << " " << GetStorageClassDesc(referenced_from_inst);
3322 }
3323
3324 for (const spv::ExecutionModel execution_model : execution_models_) {
3325 if (execution_model != spv::ExecutionModel::Vertex &&
3326 execution_model != spv::ExecutionModel::MeshNV &&
3327 execution_model != spv::ExecutionModel::TaskNV &&
3328 execution_model != spv::ExecutionModel::MeshEXT &&
3329 execution_model != spv::ExecutionModel::TaskEXT) {
3330 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3331 << _.VkErrorID(4207) << "Vulkan spec allows BuiltIn "
3332 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3333 operand)
3334 << " to be used only with Vertex, MeshNV, TaskNV , MeshEXT or"
3335 << " TaskEXT execution "
3336 "model. "
3337 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3338 referenced_from_inst, execution_model);
3339 }
3340 }
3341 }
3342
3343 if (function_id_ == 0) {
3344 // Propagate this rule to all dependant ids in the global scope.
3345 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
3346 &BuiltInsValidator::ValidateDrawIndexAtReference, this, decoration,
3347 built_in_inst, referenced_from_inst, std::placeholders::_1));
3348 }
3349
3350 return SPV_SUCCESS;
3351 }
3352
ValidateViewIndexAtDefinition(const Decoration & decoration,const Instruction & inst)3353 spv_result_t BuiltInsValidator::ValidateViewIndexAtDefinition(
3354 const Decoration& decoration, const Instruction& inst) {
3355 if (spvIsVulkanEnv(_.context()->target_env)) {
3356 if (spv_result_t error = ValidateI32(
3357 decoration, inst,
3358 [this, &inst,
3359 &decoration](const std::string& message) -> spv_result_t {
3360 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3361 << _.VkErrorID(4403)
3362 << "According to the Vulkan spec BuiltIn "
3363 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3364 decoration.params()[0])
3365 << " variable needs to be a 32-bit int scalar. "
3366 << message;
3367 })) {
3368 return error;
3369 }
3370 }
3371
3372 return ValidateViewIndexAtReference(decoration, inst, inst, inst);
3373 }
3374
ValidateViewIndexAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3375 spv_result_t BuiltInsValidator::ValidateViewIndexAtReference(
3376 const Decoration& decoration, const Instruction& built_in_inst,
3377 const Instruction& referenced_inst,
3378 const Instruction& referenced_from_inst) {
3379 uint32_t operand = decoration.params()[0];
3380 if (spvIsVulkanEnv(_.context()->target_env)) {
3381 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
3382 if (storage_class != spv::StorageClass::Max &&
3383 storage_class != spv::StorageClass::Input) {
3384 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3385 << _.VkErrorID(4402) << "Vulkan spec allows BuiltIn "
3386 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3387 operand)
3388 << " to be only used for variables with Input storage class. "
3389 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3390 referenced_from_inst)
3391 << " " << GetStorageClassDesc(referenced_from_inst);
3392 }
3393
3394 for (const spv::ExecutionModel execution_model : execution_models_) {
3395 if (execution_model == spv::ExecutionModel::GLCompute) {
3396 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3397 << _.VkErrorID(4401) << "Vulkan spec allows BuiltIn "
3398 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3399 operand)
3400 << " to be not be used with GLCompute execution model. "
3401 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3402 referenced_from_inst, execution_model);
3403 }
3404 }
3405 }
3406
3407 if (function_id_ == 0) {
3408 // Propagate this rule to all dependant ids in the global scope.
3409 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
3410 &BuiltInsValidator::ValidateViewIndexAtReference, this, decoration,
3411 built_in_inst, referenced_from_inst, std::placeholders::_1));
3412 }
3413
3414 return SPV_SUCCESS;
3415 }
3416
ValidateDeviceIndexAtDefinition(const Decoration & decoration,const Instruction & inst)3417 spv_result_t BuiltInsValidator::ValidateDeviceIndexAtDefinition(
3418 const Decoration& decoration, const Instruction& inst) {
3419 if (spvIsVulkanEnv(_.context()->target_env)) {
3420 if (spv_result_t error = ValidateI32(
3421 decoration, inst,
3422 [this, &inst,
3423 &decoration](const std::string& message) -> spv_result_t {
3424 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3425 << _.VkErrorID(4206)
3426 << "According to the Vulkan spec BuiltIn "
3427 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3428 decoration.params()[0])
3429 << " variable needs to be a 32-bit int scalar. "
3430 << message;
3431 })) {
3432 return error;
3433 }
3434 }
3435
3436 return ValidateDeviceIndexAtReference(decoration, inst, inst, inst);
3437 }
3438
ValidateDeviceIndexAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3439 spv_result_t BuiltInsValidator::ValidateDeviceIndexAtReference(
3440 const Decoration& decoration, const Instruction& built_in_inst,
3441 const Instruction& referenced_inst,
3442 const Instruction& referenced_from_inst) {
3443 uint32_t operand = decoration.params()[0];
3444 if (spvIsVulkanEnv(_.context()->target_env)) {
3445 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
3446 if (storage_class != spv::StorageClass::Max &&
3447 storage_class != spv::StorageClass::Input) {
3448 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3449 << _.VkErrorID(4205) << "Vulkan spec allows BuiltIn "
3450 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3451 operand)
3452 << " to be only used for variables with Input storage class. "
3453 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3454 referenced_from_inst)
3455 << " " << GetStorageClassDesc(referenced_from_inst);
3456 }
3457 }
3458
3459 if (function_id_ == 0) {
3460 // Propagate this rule to all dependant ids in the global scope.
3461 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
3462 &BuiltInsValidator::ValidateDeviceIndexAtReference, this, decoration,
3463 built_in_inst, referenced_from_inst, std::placeholders::_1));
3464 }
3465
3466 return SPV_SUCCESS;
3467 }
3468
ValidateFragInvocationCountAtDefinition(const Decoration & decoration,const Instruction & inst)3469 spv_result_t BuiltInsValidator::ValidateFragInvocationCountAtDefinition(const Decoration& decoration,
3470 const Instruction& inst) {
3471
3472 if (spvIsVulkanEnv(_.context()->target_env)) {
3473 const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
3474 if (spv_result_t error = ValidateI32(
3475 decoration, inst,
3476 [this, &inst, &builtin](const std::string& message) -> spv_result_t {
3477 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
3478 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3479 << _.VkErrorID(vuid) << "According to the "
3480 << spvLogStringForEnv(_.context()->target_env)
3481 << " spec BuiltIn "
3482 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3483 uint32_t(builtin))
3484 << " variable needs to be a 32-bit int scalar. "
3485 << message;
3486 })) {
3487 return error;
3488 }
3489 }
3490
3491 return ValidateFragInvocationCountAtReference(decoration, inst, inst, inst);
3492 }
3493
ValidateFragInvocationCountAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3494 spv_result_t BuiltInsValidator::ValidateFragInvocationCountAtReference(
3495 const Decoration& decoration, const Instruction& built_in_inst,
3496 const Instruction& referenced_inst,
3497 const Instruction& referenced_from_inst) {
3498
3499 if (spvIsVulkanEnv(_.context()->target_env)) {
3500 const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
3501 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
3502 if (storage_class != spv::StorageClass::Max &&
3503 storage_class != spv::StorageClass::Input) {
3504 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
3505 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3506 << _.VkErrorID(vuid) << spvLogStringForEnv(_.context()->target_env)
3507 << " spec allows BuiltIn "
3508 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
3509 << " to be only used for variables with Input storage class. "
3510 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3511 referenced_from_inst)
3512 << " " << GetStorageClassDesc(referenced_from_inst);
3513 }
3514
3515 for (const spv::ExecutionModel execution_model : execution_models_) {
3516 if (execution_model != spv::ExecutionModel::Fragment) {
3517 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel);
3518 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3519 << _.VkErrorID(vuid)
3520 << spvLogStringForEnv(_.context()->target_env)
3521 << " spec allows BuiltIn "
3522 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
3523 << " to be used only with Fragment execution model. "
3524 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3525 referenced_from_inst, execution_model);
3526 }
3527 }
3528 }
3529
3530 if (function_id_ == 0) {
3531 // Propagate this rule to all dependant ids in the global scope.
3532 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
3533 &BuiltInsValidator::ValidateFragInvocationCountAtReference, this, decoration,
3534 built_in_inst, referenced_from_inst, std::placeholders::_1));
3535 }
3536
3537 return SPV_SUCCESS;
3538 }
3539
ValidateFragSizeAtDefinition(const Decoration & decoration,const Instruction & inst)3540 spv_result_t BuiltInsValidator::ValidateFragSizeAtDefinition(const Decoration& decoration,
3541 const Instruction& inst) {
3542 if (spvIsVulkanEnv(_.context()->target_env)) {
3543 const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
3544 if (spv_result_t error = ValidateI32Vec(
3545 decoration, inst, 2,
3546 [this, &inst, &builtin](const std::string& message) -> spv_result_t {
3547 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
3548 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3549 << _.VkErrorID(vuid) << "According to the "
3550 << spvLogStringForEnv(_.context()->target_env)
3551 << " spec BuiltIn "
3552 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3553 uint32_t(builtin))
3554 << " variable needs to be a 2-component 32-bit int vector. "
3555 << message;
3556 })) {
3557 return error;
3558 }
3559 }
3560
3561 return ValidateFragSizeAtReference(decoration, inst, inst, inst);
3562 }
3563
ValidateFragSizeAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3564 spv_result_t BuiltInsValidator::ValidateFragSizeAtReference(
3565 const Decoration& decoration, const Instruction& built_in_inst,
3566 const Instruction& referenced_inst,
3567 const Instruction& referenced_from_inst) {
3568
3569 if (spvIsVulkanEnv(_.context()->target_env)) {
3570 const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
3571 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
3572 if (storage_class != spv::StorageClass::Max &&
3573 storage_class != spv::StorageClass::Input) {
3574 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
3575 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3576 << _.VkErrorID(vuid) << spvLogStringForEnv(_.context()->target_env)
3577 << " spec allows BuiltIn "
3578 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
3579 << " to be only used for variables with Input storage class. "
3580 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3581 referenced_from_inst)
3582 << " " << GetStorageClassDesc(referenced_from_inst);
3583 }
3584
3585 for (const spv::ExecutionModel execution_model : execution_models_) {
3586 if (execution_model != spv::ExecutionModel::Fragment) {
3587 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel);
3588 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3589 << _.VkErrorID(vuid)
3590 << spvLogStringForEnv(_.context()->target_env)
3591 << " spec allows BuiltIn "
3592 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
3593 << " to be used only with Fragment execution model. "
3594 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3595 referenced_from_inst, execution_model);
3596 }
3597 }
3598 }
3599
3600 if (function_id_ == 0) {
3601 // Propagate this rule to all dependant ids in the global scope.
3602 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
3603 &BuiltInsValidator::ValidateFragSizeAtReference, this, decoration,
3604 built_in_inst, referenced_from_inst, std::placeholders::_1));
3605 }
3606
3607 return SPV_SUCCESS;
3608 }
3609
ValidateFragStencilRefAtDefinition(const Decoration & decoration,const Instruction & inst)3610 spv_result_t BuiltInsValidator::ValidateFragStencilRefAtDefinition(const Decoration& decoration,
3611 const Instruction& inst) {
3612 if (spvIsVulkanEnv(_.context()->target_env)) {
3613 const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
3614 if (spv_result_t error = ValidateI(
3615 decoration, inst,
3616 [this, &inst, &builtin](const std::string& message) -> spv_result_t {
3617 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
3618 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3619 << _.VkErrorID(vuid) << "According to the "
3620 << spvLogStringForEnv(_.context()->target_env)
3621 << " spec BuiltIn "
3622 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3623 uint32_t(builtin))
3624 << " variable needs to be a int scalar. "
3625 << message;
3626 })) {
3627 return error;
3628 }
3629 }
3630
3631 return ValidateFragStencilRefAtReference(decoration, inst, inst, inst);
3632 }
3633
ValidateFragStencilRefAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3634 spv_result_t BuiltInsValidator::ValidateFragStencilRefAtReference(
3635 const Decoration& decoration, const Instruction& built_in_inst,
3636 const Instruction& referenced_inst,
3637 const Instruction& referenced_from_inst) {
3638
3639 if (spvIsVulkanEnv(_.context()->target_env)) {
3640 const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
3641 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
3642 if (storage_class != spv::StorageClass::Max &&
3643 storage_class != spv::StorageClass::Output) {
3644 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
3645 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3646 << _.VkErrorID(vuid) << spvLogStringForEnv(_.context()->target_env)
3647 << " spec allows BuiltIn "
3648 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
3649 << " to be only used for variables with Output storage class. "
3650 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3651 referenced_from_inst)
3652 << " " << GetStorageClassDesc(referenced_from_inst);
3653 }
3654
3655 for (const spv::ExecutionModel execution_model : execution_models_) {
3656 if (execution_model != spv::ExecutionModel::Fragment) {
3657 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel);
3658 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3659 << _.VkErrorID(vuid)
3660 << spvLogStringForEnv(_.context()->target_env)
3661 << " spec allows BuiltIn "
3662 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
3663 << " to be used only with Fragment execution model. "
3664 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3665 referenced_from_inst, execution_model);
3666 }
3667 }
3668 }
3669
3670 if (function_id_ == 0) {
3671 // Propagate this rule to all dependant ids in the global scope.
3672 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
3673 &BuiltInsValidator::ValidateFragStencilRefAtReference, this, decoration,
3674 built_in_inst, referenced_from_inst, std::placeholders::_1));
3675 }
3676
3677 return SPV_SUCCESS;
3678 }
3679
ValidateFullyCoveredAtDefinition(const Decoration & decoration,const Instruction & inst)3680 spv_result_t BuiltInsValidator::ValidateFullyCoveredAtDefinition(const Decoration& decoration,
3681 const Instruction& inst) {
3682 if (spvIsVulkanEnv(_.context()->target_env)) {
3683 const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
3684 if (spv_result_t error = ValidateBool(
3685 decoration, inst,
3686 [this, &inst, &builtin](const std::string& message) -> spv_result_t {
3687 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
3688 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3689 << _.VkErrorID(vuid) << "According to the "
3690 << spvLogStringForEnv(_.context()->target_env)
3691 << " spec BuiltIn "
3692 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3693 uint32_t(builtin))
3694 << " variable needs to be a bool scalar. "
3695 << message;
3696 })) {
3697 return error;
3698 }
3699 }
3700
3701 return ValidateFullyCoveredAtReference(decoration, inst, inst, inst);
3702 }
3703
ValidateFullyCoveredAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3704 spv_result_t BuiltInsValidator::ValidateFullyCoveredAtReference(
3705 const Decoration& decoration, const Instruction& built_in_inst,
3706 const Instruction& referenced_inst,
3707 const Instruction& referenced_from_inst) {
3708
3709 if (spvIsVulkanEnv(_.context()->target_env)) {
3710 const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
3711 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
3712 if (storage_class != spv::StorageClass::Max &&
3713 storage_class != spv::StorageClass::Input) {
3714 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
3715 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3716 << _.VkErrorID(vuid) << spvLogStringForEnv(_.context()->target_env)
3717 << " spec allows BuiltIn "
3718 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
3719 << " to be only used for variables with Input storage class. "
3720 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3721 referenced_from_inst)
3722 << " " << GetStorageClassDesc(referenced_from_inst);
3723 }
3724
3725 for (const spv::ExecutionModel execution_model : execution_models_) {
3726 if (execution_model != spv::ExecutionModel::Fragment) {
3727 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel);
3728 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3729 << _.VkErrorID(vuid)
3730 << spvLogStringForEnv(_.context()->target_env)
3731 << " spec allows BuiltIn "
3732 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
3733 << " to be used only with Fragment execution model. "
3734 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3735 referenced_from_inst, execution_model);
3736 }
3737 }
3738 }
3739
3740 if (function_id_ == 0) {
3741 // Propagate this rule to all dependant ids in the global scope.
3742 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
3743 &BuiltInsValidator::ValidateFullyCoveredAtReference, this, decoration,
3744 built_in_inst, referenced_from_inst, std::placeholders::_1));
3745 }
3746
3747 return SPV_SUCCESS;
3748 }
3749
ValidateNVSMOrARMCoreBuiltinsAtDefinition(const Decoration & decoration,const Instruction & inst)3750 spv_result_t BuiltInsValidator::ValidateNVSMOrARMCoreBuiltinsAtDefinition(
3751 const Decoration& decoration, const Instruction& inst) {
3752 if (spvIsVulkanEnv(_.context()->target_env)) {
3753 if (spv_result_t error = ValidateI32(
3754 decoration, inst,
3755 [this, &inst,
3756 &decoration](const std::string& message) -> spv_result_t {
3757 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3758 << "According to the "
3759 << spvLogStringForEnv(_.context()->target_env)
3760 << " spec BuiltIn "
3761 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3762 decoration.params()[0])
3763 << " variable needs to be a 32-bit int scalar. "
3764 << message;
3765 })) {
3766 return error;
3767 }
3768 }
3769
3770 // Seed at reference checks with this built-in.
3771 return ValidateNVSMOrARMCoreBuiltinsAtReference(decoration, inst, inst, inst);
3772 }
3773
ValidateNVSMOrARMCoreBuiltinsAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3774 spv_result_t BuiltInsValidator::ValidateNVSMOrARMCoreBuiltinsAtReference(
3775 const Decoration& decoration, const Instruction& built_in_inst,
3776 const Instruction& referenced_inst,
3777 const Instruction& referenced_from_inst) {
3778 if (spvIsVulkanEnv(_.context()->target_env)) {
3779 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
3780 if (storage_class != spv::StorageClass::Max &&
3781 storage_class != spv::StorageClass::Input) {
3782 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3783 << spvLogStringForEnv(_.context()->target_env)
3784 << " spec allows BuiltIn "
3785 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3786 decoration.params()[0])
3787 << " to be only used for "
3788 "variables with Input storage class. "
3789 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3790 referenced_from_inst)
3791 << " " << GetStorageClassDesc(referenced_from_inst);
3792 }
3793 }
3794
3795 if (function_id_ == 0) {
3796 // Propagate this rule to all dependant ids in the global scope.
3797 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
3798 &BuiltInsValidator::ValidateNVSMOrARMCoreBuiltinsAtReference, this, decoration,
3799 built_in_inst, referenced_from_inst, std::placeholders::_1));
3800 }
3801
3802 return SPV_SUCCESS;
3803 }
3804
ValidatePrimitiveShadingRateAtDefinition(const Decoration & decoration,const Instruction & inst)3805 spv_result_t BuiltInsValidator::ValidatePrimitiveShadingRateAtDefinition(
3806 const Decoration& decoration, const Instruction& inst) {
3807 if (spvIsVulkanEnv(_.context()->target_env)) {
3808 if (spv_result_t error = ValidateI32(
3809 decoration, inst,
3810 [this, &inst,
3811 &decoration](const std::string& message) -> spv_result_t {
3812 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3813 << _.VkErrorID(4486)
3814 << "According to the Vulkan spec BuiltIn "
3815 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3816 decoration.params()[0])
3817 << " variable needs to be a 32-bit int scalar. "
3818 << message;
3819 })) {
3820 return error;
3821 }
3822 }
3823
3824 // Seed at reference checks with this built-in.
3825 return ValidatePrimitiveShadingRateAtReference(decoration, inst, inst, inst);
3826 }
3827
ValidatePrimitiveShadingRateAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3828 spv_result_t BuiltInsValidator::ValidatePrimitiveShadingRateAtReference(
3829 const Decoration& decoration, const Instruction& built_in_inst,
3830 const Instruction& referenced_inst,
3831 const Instruction& referenced_from_inst) {
3832 if (spvIsVulkanEnv(_.context()->target_env)) {
3833 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
3834 if (storage_class != spv::StorageClass::Max &&
3835 storage_class != spv::StorageClass::Output) {
3836 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3837 << _.VkErrorID(4485) << "Vulkan spec allows BuiltIn "
3838 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3839 decoration.params()[0])
3840 << " to be only used for variables with Output storage class. "
3841 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3842 referenced_from_inst)
3843 << " " << GetStorageClassDesc(referenced_from_inst);
3844 }
3845
3846 for (const spv::ExecutionModel execution_model : execution_models_) {
3847 switch (execution_model) {
3848 case spv::ExecutionModel::Vertex:
3849 case spv::ExecutionModel::Geometry:
3850 case spv::ExecutionModel::MeshNV:
3851 case spv::ExecutionModel::MeshEXT:
3852 break;
3853 default: {
3854 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3855 << _.VkErrorID(4484) << "Vulkan spec allows BuiltIn "
3856 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3857 decoration.params()[0])
3858 << " to be used only with Vertex, Geometry, or MeshNV "
3859 "execution models. "
3860 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3861 referenced_from_inst, execution_model);
3862 }
3863 }
3864 }
3865 }
3866
3867 if (function_id_ == 0) {
3868 // Propagate this rule to all dependant ids in the global scope.
3869 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
3870 std::bind(&BuiltInsValidator::ValidatePrimitiveShadingRateAtReference,
3871 this, decoration, built_in_inst, referenced_from_inst,
3872 std::placeholders::_1));
3873 }
3874
3875 return SPV_SUCCESS;
3876 }
3877
ValidateShadingRateAtDefinition(const Decoration & decoration,const Instruction & inst)3878 spv_result_t BuiltInsValidator::ValidateShadingRateAtDefinition(
3879 const Decoration& decoration, const Instruction& inst) {
3880 if (spvIsVulkanEnv(_.context()->target_env)) {
3881 if (spv_result_t error = ValidateI32(
3882 decoration, inst,
3883 [this, &inst,
3884 &decoration](const std::string& message) -> spv_result_t {
3885 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3886 << _.VkErrorID(4492)
3887 << "According to the Vulkan spec BuiltIn "
3888 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3889 decoration.params()[0])
3890 << " variable needs to be a 32-bit int scalar. "
3891 << message;
3892 })) {
3893 return error;
3894 }
3895 }
3896
3897 // Seed at reference checks with this built-in.
3898 return ValidateShadingRateAtReference(decoration, inst, inst, inst);
3899 }
3900
ValidateShadingRateAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3901 spv_result_t BuiltInsValidator::ValidateShadingRateAtReference(
3902 const Decoration& decoration, const Instruction& built_in_inst,
3903 const Instruction& referenced_inst,
3904 const Instruction& referenced_from_inst) {
3905 if (spvIsVulkanEnv(_.context()->target_env)) {
3906 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
3907 if (storage_class != spv::StorageClass::Max &&
3908 storage_class != spv::StorageClass::Input) {
3909 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3910 << _.VkErrorID(4491) << "Vulkan spec allows BuiltIn "
3911 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3912 decoration.params()[0])
3913 << " to be only used for variables with Input storage class. "
3914 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3915 referenced_from_inst)
3916 << " " << GetStorageClassDesc(referenced_from_inst);
3917 }
3918
3919 for (const spv::ExecutionModel execution_model : execution_models_) {
3920 if (execution_model != spv::ExecutionModel::Fragment) {
3921 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3922 << _.VkErrorID(4490) << "Vulkan spec allows BuiltIn "
3923 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3924 decoration.params()[0])
3925 << " to be used only with the Fragment execution model. "
3926 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3927 referenced_from_inst, execution_model);
3928 }
3929 }
3930 }
3931
3932 if (function_id_ == 0) {
3933 // Propagate this rule to all dependant ids in the global scope.
3934 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
3935 &BuiltInsValidator::ValidateShadingRateAtReference, this, decoration,
3936 built_in_inst, referenced_from_inst, std::placeholders::_1));
3937 }
3938
3939 return SPV_SUCCESS;
3940 }
3941
ValidateRayTracingBuiltinsAtDefinition(const Decoration & decoration,const Instruction & inst)3942 spv_result_t BuiltInsValidator::ValidateRayTracingBuiltinsAtDefinition(
3943 const Decoration& decoration, const Instruction& inst) {
3944 if (spvIsVulkanEnv(_.context()->target_env)) {
3945 const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
3946 switch (builtin) {
3947 case spv::BuiltIn::HitTNV:
3948 case spv::BuiltIn::RayTminKHR:
3949 case spv::BuiltIn::RayTmaxKHR:
3950 // f32 scalar
3951 if (spv_result_t error = ValidateF32(
3952 decoration, inst,
3953 [this, &inst,
3954 builtin](const std::string& message) -> spv_result_t {
3955 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
3956 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3957 << _.VkErrorID(vuid)
3958 << "According to the Vulkan spec BuiltIn "
3959 << _.grammar().lookupOperandName(
3960 SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
3961 << " variable needs to be a 32-bit float scalar. "
3962 << message;
3963 })) {
3964 return error;
3965 }
3966 break;
3967 case spv::BuiltIn::HitKindKHR:
3968 case spv::BuiltIn::InstanceCustomIndexKHR:
3969 case spv::BuiltIn::InstanceId:
3970 case spv::BuiltIn::RayGeometryIndexKHR:
3971 case spv::BuiltIn::IncomingRayFlagsKHR:
3972 case spv::BuiltIn::CullMaskKHR:
3973 // i32 scalar
3974 if (spv_result_t error = ValidateI32(
3975 decoration, inst,
3976 [this, &inst,
3977 builtin](const std::string& message) -> spv_result_t {
3978 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
3979 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3980 << _.VkErrorID(vuid)
3981 << "According to the Vulkan spec BuiltIn "
3982 << _.grammar().lookupOperandName(
3983 SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
3984 << " variable needs to be a 32-bit int scalar. "
3985 << message;
3986 })) {
3987 return error;
3988 }
3989 break;
3990 case spv::BuiltIn::ObjectRayDirectionKHR:
3991 case spv::BuiltIn::ObjectRayOriginKHR:
3992 case spv::BuiltIn::WorldRayDirectionKHR:
3993 case spv::BuiltIn::WorldRayOriginKHR:
3994 // f32 vec3
3995 if (spv_result_t error = ValidateF32Vec(
3996 decoration, inst, 3,
3997 [this, &inst,
3998 builtin](const std::string& message) -> spv_result_t {
3999 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
4000 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
4001 << _.VkErrorID(vuid)
4002 << "According to the Vulkan spec BuiltIn "
4003 << _.grammar().lookupOperandName(
4004 SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
4005 << " variable needs to be a 3-component 32-bit float "
4006 "vector. "
4007 << message;
4008 })) {
4009 return error;
4010 }
4011 break;
4012 case spv::BuiltIn::LaunchIdKHR:
4013 case spv::BuiltIn::LaunchSizeKHR:
4014 // i32 vec3
4015 if (spv_result_t error = ValidateI32Vec(
4016 decoration, inst, 3,
4017 [this, &inst,
4018 builtin](const std::string& message) -> spv_result_t {
4019 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
4020 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
4021 << _.VkErrorID(vuid)
4022 << "According to the Vulkan spec BuiltIn "
4023 << _.grammar().lookupOperandName(
4024 SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
4025 << " variable needs to be a 3-component 32-bit int "
4026 "vector. "
4027 << message;
4028 })) {
4029 return error;
4030 }
4031 break;
4032 case spv::BuiltIn::ObjectToWorldKHR:
4033 case spv::BuiltIn::WorldToObjectKHR:
4034 // f32 mat4x3
4035 if (spv_result_t error = ValidateF32Mat(
4036 decoration, inst, 3, 4,
4037 [this, &inst,
4038 builtin](const std::string& message) -> spv_result_t {
4039 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
4040 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
4041 << _.VkErrorID(vuid)
4042 << "According to the Vulkan spec BuiltIn "
4043 << _.grammar().lookupOperandName(
4044 SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
4045 << " variable needs to be a matrix with"
4046 << " 4 columns of 3-component vectors of 32-bit "
4047 "floats. "
4048 << message;
4049 })) {
4050 return error;
4051 }
4052 break;
4053 default:
4054 assert(0 && "Unexpected ray tracing builtin");
4055 break;
4056 }
4057 }
4058
4059 // Seed at reference checks with this built-in.
4060 return ValidateRayTracingBuiltinsAtReference(decoration, inst, inst, inst);
4061 }
4062
ValidateRayTracingBuiltinsAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)4063 spv_result_t BuiltInsValidator::ValidateRayTracingBuiltinsAtReference(
4064 const Decoration& decoration, const Instruction& built_in_inst,
4065 const Instruction& referenced_inst,
4066 const Instruction& referenced_from_inst) {
4067 if (spvIsVulkanEnv(_.context()->target_env)) {
4068 const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
4069 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
4070 if (storage_class != spv::StorageClass::Max &&
4071 storage_class != spv::StorageClass::Input) {
4072 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
4073 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
4074 << _.VkErrorID(vuid) << "Vulkan spec allows BuiltIn "
4075 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
4076 decoration.params()[0])
4077 << " to be only used for variables with Input storage class. "
4078 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
4079 referenced_from_inst)
4080 << " " << GetStorageClassDesc(referenced_from_inst);
4081 }
4082
4083 for (const spv::ExecutionModel execution_model : execution_models_) {
4084 if (!IsExecutionModelValidForRtBuiltIn(builtin, execution_model)) {
4085 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel);
4086 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
4087 << _.VkErrorID(vuid) << "Vulkan spec does not allow BuiltIn "
4088 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
4089 decoration.params()[0])
4090 << " to be used with the execution model "
4091 << _.grammar().lookupOperandName(
4092 SPV_OPERAND_TYPE_EXECUTION_MODEL, uint32_t(execution_model))
4093 << ".\n"
4094 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
4095 referenced_from_inst, execution_model);
4096 }
4097 }
4098 }
4099
4100 if (function_id_ == 0) {
4101 // Propagate this rule to all dependant ids in the global scope.
4102 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
4103 std::bind(&BuiltInsValidator::ValidateRayTracingBuiltinsAtReference,
4104 this, decoration, built_in_inst, referenced_from_inst,
4105 std::placeholders::_1));
4106 }
4107
4108 return SPV_SUCCESS;
4109 }
4110
ValidateSingleBuiltInAtDefinition(const Decoration & decoration,const Instruction & inst)4111 spv_result_t BuiltInsValidator::ValidateSingleBuiltInAtDefinition(
4112 const Decoration& decoration, const Instruction& inst) {
4113 const spv::BuiltIn label = spv::BuiltIn(decoration.params()[0]);
4114
4115 if (!spvIsVulkanEnv(_.context()->target_env)) {
4116 // Early return. All currently implemented rules are based on Vulkan spec.
4117 //
4118 // TODO: If you are adding validation rules for environments other than
4119 // Vulkan (or general rules which are not environment independent), then
4120 // you need to modify or remove this condition. Consider also adding early
4121 // returns into BuiltIn-specific rules, so that the system doesn't spawn new
4122 // rules which don't do anything.
4123 return SPV_SUCCESS;
4124 }
4125
4126 // If you are adding a new BuiltIn enum, please register it here.
4127 // If the newly added enum has validation rules associated with it
4128 // consider leaving a TODO and/or creating an issue.
4129 switch (label) {
4130 case spv::BuiltIn::ClipDistance:
4131 case spv::BuiltIn::CullDistance: {
4132 return ValidateClipOrCullDistanceAtDefinition(decoration, inst);
4133 }
4134 case spv::BuiltIn::FragCoord: {
4135 return ValidateFragCoordAtDefinition(decoration, inst);
4136 }
4137 case spv::BuiltIn::FragDepth: {
4138 return ValidateFragDepthAtDefinition(decoration, inst);
4139 }
4140 case spv::BuiltIn::FrontFacing: {
4141 return ValidateFrontFacingAtDefinition(decoration, inst);
4142 }
4143 case spv::BuiltIn::GlobalInvocationId:
4144 case spv::BuiltIn::LocalInvocationId:
4145 case spv::BuiltIn::NumWorkgroups:
4146 case spv::BuiltIn::WorkgroupId: {
4147 return ValidateComputeShaderI32Vec3InputAtDefinition(decoration, inst);
4148 }
4149 case spv::BuiltIn::BaryCoordKHR:
4150 case spv::BuiltIn::BaryCoordNoPerspKHR: {
4151 return ValidateFragmentShaderF32Vec3InputAtDefinition(decoration, inst);
4152 }
4153 case spv::BuiltIn::HelperInvocation: {
4154 return ValidateHelperInvocationAtDefinition(decoration, inst);
4155 }
4156 case spv::BuiltIn::InvocationId: {
4157 return ValidateInvocationIdAtDefinition(decoration, inst);
4158 }
4159 case spv::BuiltIn::InstanceIndex: {
4160 return ValidateInstanceIndexAtDefinition(decoration, inst);
4161 }
4162 case spv::BuiltIn::Layer:
4163 case spv::BuiltIn::ViewportIndex: {
4164 return ValidateLayerOrViewportIndexAtDefinition(decoration, inst);
4165 }
4166 case spv::BuiltIn::PatchVertices: {
4167 return ValidatePatchVerticesAtDefinition(decoration, inst);
4168 }
4169 case spv::BuiltIn::PointCoord: {
4170 return ValidatePointCoordAtDefinition(decoration, inst);
4171 }
4172 case spv::BuiltIn::PointSize: {
4173 return ValidatePointSizeAtDefinition(decoration, inst);
4174 }
4175 case spv::BuiltIn::Position: {
4176 return ValidatePositionAtDefinition(decoration, inst);
4177 }
4178 case spv::BuiltIn::PrimitiveId: {
4179 return ValidatePrimitiveIdAtDefinition(decoration, inst);
4180 }
4181 case spv::BuiltIn::SampleId: {
4182 return ValidateSampleIdAtDefinition(decoration, inst);
4183 }
4184 case spv::BuiltIn::SampleMask: {
4185 return ValidateSampleMaskAtDefinition(decoration, inst);
4186 }
4187 case spv::BuiltIn::SamplePosition: {
4188 return ValidateSamplePositionAtDefinition(decoration, inst);
4189 }
4190 case spv::BuiltIn::SubgroupId:
4191 case spv::BuiltIn::NumSubgroups: {
4192 return ValidateComputeI32InputAtDefinition(decoration, inst);
4193 }
4194 case spv::BuiltIn::SubgroupLocalInvocationId:
4195 case spv::BuiltIn::SubgroupSize: {
4196 return ValidateI32InputAtDefinition(decoration, inst);
4197 }
4198 case spv::BuiltIn::SubgroupEqMask:
4199 case spv::BuiltIn::SubgroupGeMask:
4200 case spv::BuiltIn::SubgroupGtMask:
4201 case spv::BuiltIn::SubgroupLeMask:
4202 case spv::BuiltIn::SubgroupLtMask: {
4203 return ValidateI32Vec4InputAtDefinition(decoration, inst);
4204 }
4205 case spv::BuiltIn::TessCoord: {
4206 return ValidateTessCoordAtDefinition(decoration, inst);
4207 }
4208 case spv::BuiltIn::TessLevelOuter: {
4209 return ValidateTessLevelOuterAtDefinition(decoration, inst);
4210 }
4211 case spv::BuiltIn::TessLevelInner: {
4212 return ValidateTessLevelInnerAtDefinition(decoration, inst);
4213 }
4214 case spv::BuiltIn::VertexIndex: {
4215 return ValidateVertexIndexAtDefinition(decoration, inst);
4216 }
4217 case spv::BuiltIn::WorkgroupSize: {
4218 return ValidateWorkgroupSizeAtDefinition(decoration, inst);
4219 }
4220 case spv::BuiltIn::VertexId: {
4221 return ValidateVertexIdAtDefinition(decoration, inst);
4222 }
4223 case spv::BuiltIn::LocalInvocationIndex: {
4224 return ValidateLocalInvocationIndexAtDefinition(decoration, inst);
4225 }
4226 case spv::BuiltIn::CoreIDARM:
4227 case spv::BuiltIn::CoreCountARM:
4228 case spv::BuiltIn::CoreMaxIDARM:
4229 case spv::BuiltIn::WarpIDARM:
4230 case spv::BuiltIn::WarpMaxIDARM:
4231 case spv::BuiltIn::WarpsPerSMNV:
4232 case spv::BuiltIn::SMCountNV:
4233 case spv::BuiltIn::WarpIDNV:
4234 case spv::BuiltIn::SMIDNV: {
4235 return ValidateNVSMOrARMCoreBuiltinsAtDefinition(decoration, inst);
4236 }
4237 case spv::BuiltIn::BaseInstance:
4238 case spv::BuiltIn::BaseVertex: {
4239 return ValidateBaseInstanceOrVertexAtDefinition(decoration, inst);
4240 }
4241 case spv::BuiltIn::DrawIndex: {
4242 return ValidateDrawIndexAtDefinition(decoration, inst);
4243 }
4244 case spv::BuiltIn::ViewIndex: {
4245 return ValidateViewIndexAtDefinition(decoration, inst);
4246 }
4247 case spv::BuiltIn::DeviceIndex: {
4248 return ValidateDeviceIndexAtDefinition(decoration, inst);
4249 }
4250 case spv::BuiltIn::FragInvocationCountEXT: {
4251 // alias spv::BuiltIn::InvocationsPerPixelNV
4252 return ValidateFragInvocationCountAtDefinition(decoration, inst);
4253 }
4254 case spv::BuiltIn::FragSizeEXT: {
4255 // alias spv::BuiltIn::FragmentSizeNV
4256 return ValidateFragSizeAtDefinition(decoration, inst);
4257 }
4258 case spv::BuiltIn::FragStencilRefEXT: {
4259 return ValidateFragStencilRefAtDefinition(decoration, inst);
4260 }
4261 case spv::BuiltIn::FullyCoveredEXT:{
4262 return ValidateFullyCoveredAtDefinition(decoration, inst);
4263 }
4264 // Ray tracing builtins
4265 case spv::BuiltIn::HitKindKHR: // alias spv::BuiltIn::HitKindNV
4266 case spv::BuiltIn::HitTNV: // NOT present in KHR
4267 case spv::BuiltIn::InstanceId:
4268 case spv::BuiltIn::LaunchIdKHR: // alias spv::BuiltIn::LaunchIdNV
4269 case spv::BuiltIn::LaunchSizeKHR: // alias spv::BuiltIn::LaunchSizeNV
4270 case spv::BuiltIn::WorldRayOriginKHR: // alias spv::BuiltIn::WorldRayOriginNV
4271 case spv::BuiltIn::WorldRayDirectionKHR: // alias spv::BuiltIn::WorldRayDirectionNV
4272 case spv::BuiltIn::ObjectRayOriginKHR: // alias spv::BuiltIn::ObjectRayOriginNV
4273 case spv::BuiltIn::ObjectRayDirectionKHR: // alias
4274 // spv::BuiltIn::ObjectRayDirectionNV
4275 case spv::BuiltIn::RayTminKHR: // alias spv::BuiltIn::RayTminNV
4276 case spv::BuiltIn::RayTmaxKHR: // alias spv::BuiltIn::RayTmaxNV
4277 case spv::BuiltIn::InstanceCustomIndexKHR: // alias
4278 // spv::BuiltIn::InstanceCustomIndexNV
4279 case spv::BuiltIn::ObjectToWorldKHR: // alias spv::BuiltIn::ObjectToWorldNV
4280 case spv::BuiltIn::WorldToObjectKHR: // alias spv::BuiltIn::WorldToObjectNV
4281 case spv::BuiltIn::IncomingRayFlagsKHR: // alias spv::BuiltIn::IncomingRayFlagsNV
4282 case spv::BuiltIn::RayGeometryIndexKHR: // NOT present in NV
4283 case spv::BuiltIn::CullMaskKHR: {
4284 return ValidateRayTracingBuiltinsAtDefinition(decoration, inst);
4285 }
4286 case spv::BuiltIn::PrimitiveShadingRateKHR: {
4287 return ValidatePrimitiveShadingRateAtDefinition(decoration, inst);
4288 }
4289 case spv::BuiltIn::ShadingRateKHR: {
4290 return ValidateShadingRateAtDefinition(decoration, inst);
4291 }
4292 default:
4293 // No validation rules (for the moment).
4294 break;
4295 }
4296 return SPV_SUCCESS;
4297 }
4298
ValidateBuiltInsAtDefinition()4299 spv_result_t BuiltInsValidator::ValidateBuiltInsAtDefinition() {
4300 for (const auto& kv : _.id_decorations()) {
4301 const uint32_t id = kv.first;
4302 const auto& decorations = kv.second;
4303 if (decorations.empty()) {
4304 continue;
4305 }
4306
4307 const Instruction* inst = _.FindDef(id);
4308 assert(inst);
4309
4310 for (const auto& decoration : kv.second) {
4311 if (decoration.dec_type() != spv::Decoration::BuiltIn) {
4312 continue;
4313 }
4314
4315 if (spv_result_t error =
4316 ValidateSingleBuiltInAtDefinition(decoration, *inst)) {
4317 return error;
4318 }
4319 }
4320 }
4321
4322 return SPV_SUCCESS;
4323 }
4324
Run()4325 spv_result_t BuiltInsValidator::Run() {
4326 // First pass: validate all built-ins at definition and seed
4327 // id_to_at_reference_checks_ with built-ins.
4328 if (auto error = ValidateBuiltInsAtDefinition()) {
4329 return error;
4330 }
4331
4332 if (id_to_at_reference_checks_.empty()) {
4333 // No validation tasks were seeded. Nothing else to do.
4334 return SPV_SUCCESS;
4335 }
4336
4337 // Second pass: validate every id reference in the module using
4338 // rules in id_to_at_reference_checks_.
4339 for (const Instruction& inst : _.ordered_instructions()) {
4340 Update(inst);
4341
4342 std::set<uint32_t> already_checked;
4343
4344 for (const auto& operand : inst.operands()) {
4345 if (!spvIsIdType(operand.type)) {
4346 // Not id.
4347 continue;
4348 }
4349
4350 const uint32_t id = inst.word(operand.offset);
4351 if (id == inst.id()) {
4352 // No need to check result id.
4353 continue;
4354 }
4355
4356 if (!already_checked.insert(id).second) {
4357 // The instruction has already referenced this id.
4358 continue;
4359 }
4360
4361 // Instruction references the id. Run all checks associated with the id
4362 // on the instruction. id_to_at_reference_checks_ can be modified in the
4363 // process, iterators are safe because it's a tree-based map.
4364 const auto it = id_to_at_reference_checks_.find(id);
4365 if (it != id_to_at_reference_checks_.end()) {
4366 for (const auto& check : it->second) {
4367 if (spv_result_t error = check(inst)) {
4368 return error;
4369 }
4370 }
4371 }
4372 }
4373 }
4374
4375 return SPV_SUCCESS;
4376 }
4377
4378 } // namespace
4379
4380 // Validates correctness of built-in variables.
ValidateBuiltIns(ValidationState_t & _)4381 spv_result_t ValidateBuiltIns(ValidationState_t& _) {
4382 BuiltInsValidator validator(_);
4383 return validator.Run();
4384 }
4385
4386 } // namespace val
4387 } // namespace spvtools
4388