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