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 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 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 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3075 << _.VkErrorID(4425)
3076 << spvLogStringForEnv(_.context()->target_env)
3077 << " spec allows BuiltIn "
3078 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3079 decoration.params()[0])
3080 << " to be used only with GLCompute execution model. "
3081 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3082 referenced_from_inst, execution_model);
3083 }
3084 }
3085 }
3086
3087 if (function_id_ == 0) {
3088 // Propagate this rule to all dependant ids in the global scope.
3089 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
3090 &BuiltInsValidator::ValidateWorkgroupSizeAtReference, this, decoration,
3091 built_in_inst, referenced_from_inst, std::placeholders::_1));
3092 }
3093
3094 return SPV_SUCCESS;
3095 }
3096
ValidateBaseInstanceOrVertexAtDefinition(const Decoration & decoration,const Instruction & inst)3097 spv_result_t BuiltInsValidator::ValidateBaseInstanceOrVertexAtDefinition(
3098 const Decoration& decoration, const Instruction& inst) {
3099 if (spvIsVulkanEnv(_.context()->target_env)) {
3100 if (spv_result_t error = ValidateI32(
3101 decoration, inst,
3102 [this, &inst,
3103 &decoration](const std::string& message) -> spv_result_t {
3104 uint32_t vuid = (decoration.params()[0] == SpvBuiltInBaseInstance)
3105 ? 4183
3106 : 4186;
3107 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3108 << _.VkErrorID(vuid)
3109 << "According to the Vulkan spec BuiltIn "
3110 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3111 decoration.params()[0])
3112 << " variable needs to be a 32-bit int scalar. "
3113 << message;
3114 })) {
3115 return error;
3116 }
3117 }
3118
3119 return ValidateBaseInstanceOrVertexAtReference(decoration, inst, inst, inst);
3120 }
3121
ValidateBaseInstanceOrVertexAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3122 spv_result_t BuiltInsValidator::ValidateBaseInstanceOrVertexAtReference(
3123 const Decoration& decoration, const Instruction& built_in_inst,
3124 const Instruction& referenced_inst,
3125 const Instruction& referenced_from_inst) {
3126 uint32_t operand = decoration.params()[0];
3127 if (spvIsVulkanEnv(_.context()->target_env)) {
3128 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
3129 if (storage_class != SpvStorageClassMax &&
3130 storage_class != SpvStorageClassInput) {
3131 uint32_t vuid = (operand == SpvBuiltInBaseInstance) ? 4182 : 4185;
3132 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3133 << _.VkErrorID(vuid) << "Vulkan spec allows BuiltIn "
3134 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3135 operand)
3136 << " to be only used for variables with Input storage class. "
3137 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3138 referenced_from_inst)
3139 << " " << GetStorageClassDesc(referenced_from_inst);
3140 }
3141
3142 for (const SpvExecutionModel execution_model : execution_models_) {
3143 if (execution_model != SpvExecutionModelVertex) {
3144 uint32_t vuid = (operand == SpvBuiltInBaseInstance) ? 4181 : 4184;
3145 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3146 << _.VkErrorID(vuid) << "Vulkan spec allows BuiltIn "
3147 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3148 operand)
3149 << " to be used only with Vertex execution model. "
3150 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3151 referenced_from_inst, execution_model);
3152 }
3153 }
3154 }
3155
3156 if (function_id_ == 0) {
3157 // Propagate this rule to all dependant ids in the global scope.
3158 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
3159 std::bind(&BuiltInsValidator::ValidateBaseInstanceOrVertexAtReference,
3160 this, decoration, built_in_inst, referenced_from_inst,
3161 std::placeholders::_1));
3162 }
3163
3164 return SPV_SUCCESS;
3165 }
3166
ValidateDrawIndexAtDefinition(const Decoration & decoration,const Instruction & inst)3167 spv_result_t BuiltInsValidator::ValidateDrawIndexAtDefinition(
3168 const Decoration& decoration, const Instruction& inst) {
3169 if (spvIsVulkanEnv(_.context()->target_env)) {
3170 if (spv_result_t error = ValidateI32(
3171 decoration, inst,
3172 [this, &inst,
3173 &decoration](const std::string& message) -> spv_result_t {
3174 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3175 << _.VkErrorID(4209)
3176 << "According to the Vulkan spec BuiltIn "
3177 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3178 decoration.params()[0])
3179 << " variable needs to be a 32-bit int scalar. "
3180 << message;
3181 })) {
3182 return error;
3183 }
3184 }
3185
3186 return ValidateDrawIndexAtReference(decoration, inst, inst, inst);
3187 }
3188
ValidateDrawIndexAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3189 spv_result_t BuiltInsValidator::ValidateDrawIndexAtReference(
3190 const Decoration& decoration, const Instruction& built_in_inst,
3191 const Instruction& referenced_inst,
3192 const Instruction& referenced_from_inst) {
3193 uint32_t operand = decoration.params()[0];
3194 if (spvIsVulkanEnv(_.context()->target_env)) {
3195 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
3196 if (storage_class != SpvStorageClassMax &&
3197 storage_class != SpvStorageClassInput) {
3198 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3199 << _.VkErrorID(4208) << "Vulkan spec allows BuiltIn "
3200 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3201 operand)
3202 << " to be only used for variables with Input storage class. "
3203 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3204 referenced_from_inst)
3205 << " " << GetStorageClassDesc(referenced_from_inst);
3206 }
3207
3208 for (const SpvExecutionModel execution_model : execution_models_) {
3209 if (execution_model != SpvExecutionModelVertex &&
3210 execution_model != SpvExecutionModelMeshNV &&
3211 execution_model != SpvExecutionModelTaskNV) {
3212 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3213 << _.VkErrorID(4207) << "Vulkan spec allows BuiltIn "
3214 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3215 operand)
3216 << " to be used only with Vertex, MeshNV, or TaskNV execution "
3217 "model. "
3218 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3219 referenced_from_inst, execution_model);
3220 }
3221 }
3222 }
3223
3224 if (function_id_ == 0) {
3225 // Propagate this rule to all dependant ids in the global scope.
3226 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
3227 &BuiltInsValidator::ValidateDrawIndexAtReference, this, decoration,
3228 built_in_inst, referenced_from_inst, std::placeholders::_1));
3229 }
3230
3231 return SPV_SUCCESS;
3232 }
3233
ValidateViewIndexAtDefinition(const Decoration & decoration,const Instruction & inst)3234 spv_result_t BuiltInsValidator::ValidateViewIndexAtDefinition(
3235 const Decoration& decoration, const Instruction& inst) {
3236 if (spvIsVulkanEnv(_.context()->target_env)) {
3237 if (spv_result_t error = ValidateI32(
3238 decoration, inst,
3239 [this, &inst,
3240 &decoration](const std::string& message) -> spv_result_t {
3241 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3242 << _.VkErrorID(4403)
3243 << "According to the Vulkan spec BuiltIn "
3244 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3245 decoration.params()[0])
3246 << " variable needs to be a 32-bit int scalar. "
3247 << message;
3248 })) {
3249 return error;
3250 }
3251 }
3252
3253 return ValidateViewIndexAtReference(decoration, inst, inst, inst);
3254 }
3255
ValidateViewIndexAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3256 spv_result_t BuiltInsValidator::ValidateViewIndexAtReference(
3257 const Decoration& decoration, const Instruction& built_in_inst,
3258 const Instruction& referenced_inst,
3259 const Instruction& referenced_from_inst) {
3260 uint32_t operand = decoration.params()[0];
3261 if (spvIsVulkanEnv(_.context()->target_env)) {
3262 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
3263 if (storage_class != SpvStorageClassMax &&
3264 storage_class != SpvStorageClassInput) {
3265 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3266 << _.VkErrorID(4402) << "Vulkan spec allows BuiltIn "
3267 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3268 operand)
3269 << " to be only used for variables with Input storage class. "
3270 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3271 referenced_from_inst)
3272 << " " << GetStorageClassDesc(referenced_from_inst);
3273 }
3274
3275 for (const SpvExecutionModel execution_model : execution_models_) {
3276 if (execution_model == SpvExecutionModelGLCompute) {
3277 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3278 << _.VkErrorID(4401) << "Vulkan spec allows BuiltIn "
3279 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3280 operand)
3281 << " to be not be used with GLCompute execution model. "
3282 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3283 referenced_from_inst, execution_model);
3284 }
3285 }
3286 }
3287
3288 if (function_id_ == 0) {
3289 // Propagate this rule to all dependant ids in the global scope.
3290 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
3291 &BuiltInsValidator::ValidateViewIndexAtReference, this, decoration,
3292 built_in_inst, referenced_from_inst, std::placeholders::_1));
3293 }
3294
3295 return SPV_SUCCESS;
3296 }
3297
ValidateDeviceIndexAtDefinition(const Decoration & decoration,const Instruction & inst)3298 spv_result_t BuiltInsValidator::ValidateDeviceIndexAtDefinition(
3299 const Decoration& decoration, const Instruction& inst) {
3300 if (spvIsVulkanEnv(_.context()->target_env)) {
3301 if (spv_result_t error = ValidateI32(
3302 decoration, inst,
3303 [this, &inst,
3304 &decoration](const std::string& message) -> spv_result_t {
3305 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3306 << _.VkErrorID(4206)
3307 << "According to the Vulkan spec BuiltIn "
3308 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3309 decoration.params()[0])
3310 << " variable needs to be a 32-bit int scalar. "
3311 << message;
3312 })) {
3313 return error;
3314 }
3315 }
3316
3317 return ValidateDeviceIndexAtReference(decoration, inst, inst, inst);
3318 }
3319
ValidateDeviceIndexAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3320 spv_result_t BuiltInsValidator::ValidateDeviceIndexAtReference(
3321 const Decoration& decoration, const Instruction& built_in_inst,
3322 const Instruction& referenced_inst,
3323 const Instruction& referenced_from_inst) {
3324 uint32_t operand = decoration.params()[0];
3325 if (spvIsVulkanEnv(_.context()->target_env)) {
3326 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
3327 if (storage_class != SpvStorageClassMax &&
3328 storage_class != SpvStorageClassInput) {
3329 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3330 << _.VkErrorID(4205) << "Vulkan spec allows BuiltIn "
3331 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3332 operand)
3333 << " to be only used for variables with Input storage class. "
3334 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3335 referenced_from_inst)
3336 << " " << GetStorageClassDesc(referenced_from_inst);
3337 }
3338 }
3339
3340 if (function_id_ == 0) {
3341 // Propagate this rule to all dependant ids in the global scope.
3342 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
3343 &BuiltInsValidator::ValidateDeviceIndexAtReference, this, decoration,
3344 built_in_inst, referenced_from_inst, std::placeholders::_1));
3345 }
3346
3347 return SPV_SUCCESS;
3348 }
3349
ValidateFragInvocationCountAtDefinition(const Decoration & decoration,const Instruction & inst)3350 spv_result_t BuiltInsValidator::ValidateFragInvocationCountAtDefinition(const Decoration& decoration,
3351 const Instruction& inst) {
3352
3353 if (spvIsVulkanEnv(_.context()->target_env)) {
3354 const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
3355 if (spv_result_t error = ValidateI32(
3356 decoration, inst,
3357 [this, &inst, &builtin](const std::string& message) -> spv_result_t {
3358 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
3359 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3360 << _.VkErrorID(vuid) << "According to the "
3361 << spvLogStringForEnv(_.context()->target_env)
3362 << " spec BuiltIn "
3363 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3364 builtin)
3365 << " variable needs to be a 32-bit int scalar. "
3366 << message;
3367 })) {
3368 return error;
3369 }
3370 }
3371
3372 return ValidateFragInvocationCountAtReference(decoration, inst, inst, inst);
3373 }
3374
ValidateFragInvocationCountAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3375 spv_result_t BuiltInsValidator::ValidateFragInvocationCountAtReference(
3376 const Decoration& decoration, const Instruction& built_in_inst,
3377 const Instruction& referenced_inst,
3378 const Instruction& referenced_from_inst) {
3379
3380 if (spvIsVulkanEnv(_.context()->target_env)) {
3381 const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
3382 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
3383 if (storage_class != SpvStorageClassMax &&
3384 storage_class != SpvStorageClassInput) {
3385 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
3386 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3387 << _.VkErrorID(vuid) << spvLogStringForEnv(_.context()->target_env)
3388 << " spec allows BuiltIn "
3389 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
3390 << " to be only used for variables with Input storage class. "
3391 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3392 referenced_from_inst)
3393 << " " << GetStorageClassDesc(referenced_from_inst);
3394 }
3395
3396 for (const SpvExecutionModel execution_model : execution_models_) {
3397 if (execution_model != SpvExecutionModelFragment) {
3398 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel);
3399 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3400 << _.VkErrorID(vuid)
3401 << spvLogStringForEnv(_.context()->target_env)
3402 << " spec allows BuiltIn "
3403 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
3404 << " to be used only with Fragment execution model. "
3405 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3406 referenced_from_inst, execution_model);
3407 }
3408 }
3409 }
3410
3411 if (function_id_ == 0) {
3412 // Propagate this rule to all dependant ids in the global scope.
3413 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
3414 &BuiltInsValidator::ValidateFragInvocationCountAtReference, this, decoration,
3415 built_in_inst, referenced_from_inst, std::placeholders::_1));
3416 }
3417
3418 return SPV_SUCCESS;
3419 }
3420
ValidateFragSizeAtDefinition(const Decoration & decoration,const Instruction & inst)3421 spv_result_t BuiltInsValidator::ValidateFragSizeAtDefinition(const Decoration& decoration,
3422 const Instruction& inst) {
3423 if (spvIsVulkanEnv(_.context()->target_env)) {
3424 const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
3425 if (spv_result_t error = ValidateI32Vec(
3426 decoration, inst, 2,
3427 [this, &inst, &builtin](const std::string& message) -> spv_result_t {
3428 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
3429 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3430 << _.VkErrorID(vuid) << "According to the "
3431 << spvLogStringForEnv(_.context()->target_env)
3432 << " spec BuiltIn "
3433 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3434 builtin)
3435 << " variable needs to be a 2-component 32-bit int vector. "
3436 << message;
3437 })) {
3438 return error;
3439 }
3440 }
3441
3442 return ValidateFragSizeAtReference(decoration, inst, inst, inst);
3443 }
3444
ValidateFragSizeAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3445 spv_result_t BuiltInsValidator::ValidateFragSizeAtReference(
3446 const Decoration& decoration, const Instruction& built_in_inst,
3447 const Instruction& referenced_inst,
3448 const Instruction& referenced_from_inst) {
3449
3450 if (spvIsVulkanEnv(_.context()->target_env)) {
3451 const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
3452 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
3453 if (storage_class != SpvStorageClassMax &&
3454 storage_class != SpvStorageClassInput) {
3455 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
3456 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3457 << _.VkErrorID(vuid) << spvLogStringForEnv(_.context()->target_env)
3458 << " spec allows BuiltIn "
3459 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
3460 << " to be only used for variables with Input storage class. "
3461 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3462 referenced_from_inst)
3463 << " " << GetStorageClassDesc(referenced_from_inst);
3464 }
3465
3466 for (const SpvExecutionModel execution_model : execution_models_) {
3467 if (execution_model != SpvExecutionModelFragment) {
3468 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel);
3469 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3470 << _.VkErrorID(vuid)
3471 << spvLogStringForEnv(_.context()->target_env)
3472 << " spec allows BuiltIn "
3473 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
3474 << " to be used only with Fragment execution model. "
3475 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3476 referenced_from_inst, execution_model);
3477 }
3478 }
3479 }
3480
3481 if (function_id_ == 0) {
3482 // Propagate this rule to all dependant ids in the global scope.
3483 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
3484 &BuiltInsValidator::ValidateFragSizeAtReference, this, decoration,
3485 built_in_inst, referenced_from_inst, std::placeholders::_1));
3486 }
3487
3488 return SPV_SUCCESS;
3489 }
3490
ValidateFragStencilRefAtDefinition(const Decoration & decoration,const Instruction & inst)3491 spv_result_t BuiltInsValidator::ValidateFragStencilRefAtDefinition(const Decoration& decoration,
3492 const Instruction& inst) {
3493 if (spvIsVulkanEnv(_.context()->target_env)) {
3494 const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
3495 if (spv_result_t error = ValidateI(
3496 decoration, inst,
3497 [this, &inst, &builtin](const std::string& message) -> spv_result_t {
3498 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
3499 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3500 << _.VkErrorID(vuid) << "According to the "
3501 << spvLogStringForEnv(_.context()->target_env)
3502 << " spec BuiltIn "
3503 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3504 builtin)
3505 << " variable needs to be a int scalar. "
3506 << message;
3507 })) {
3508 return error;
3509 }
3510 }
3511
3512 return ValidateFragStencilRefAtReference(decoration, inst, inst, inst);
3513 }
3514
ValidateFragStencilRefAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3515 spv_result_t BuiltInsValidator::ValidateFragStencilRefAtReference(
3516 const Decoration& decoration, const Instruction& built_in_inst,
3517 const Instruction& referenced_inst,
3518 const Instruction& referenced_from_inst) {
3519
3520 if (spvIsVulkanEnv(_.context()->target_env)) {
3521 const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
3522 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
3523 if (storage_class != SpvStorageClassMax &&
3524 storage_class != SpvStorageClassOutput) {
3525 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
3526 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3527 << _.VkErrorID(vuid) << spvLogStringForEnv(_.context()->target_env)
3528 << " spec allows BuiltIn "
3529 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
3530 << " to be only used for variables with Output storage class. "
3531 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3532 referenced_from_inst)
3533 << " " << GetStorageClassDesc(referenced_from_inst);
3534 }
3535
3536 for (const SpvExecutionModel execution_model : execution_models_) {
3537 if (execution_model != SpvExecutionModelFragment) {
3538 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel);
3539 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3540 << _.VkErrorID(vuid)
3541 << spvLogStringForEnv(_.context()->target_env)
3542 << " spec allows BuiltIn "
3543 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
3544 << " to be used only with Fragment execution model. "
3545 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3546 referenced_from_inst, execution_model);
3547 }
3548 }
3549 }
3550
3551 if (function_id_ == 0) {
3552 // Propagate this rule to all dependant ids in the global scope.
3553 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
3554 &BuiltInsValidator::ValidateFragStencilRefAtReference, this, decoration,
3555 built_in_inst, referenced_from_inst, std::placeholders::_1));
3556 }
3557
3558 return SPV_SUCCESS;
3559 }
3560
ValidateFullyCoveredAtDefinition(const Decoration & decoration,const Instruction & inst)3561 spv_result_t BuiltInsValidator::ValidateFullyCoveredAtDefinition(const Decoration& decoration,
3562 const Instruction& inst) {
3563 if (spvIsVulkanEnv(_.context()->target_env)) {
3564 const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
3565 if (spv_result_t error = ValidateBool(
3566 decoration, inst,
3567 [this, &inst, &builtin](const std::string& message) -> spv_result_t {
3568 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
3569 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3570 << _.VkErrorID(vuid) << "According to the "
3571 << spvLogStringForEnv(_.context()->target_env)
3572 << " spec BuiltIn "
3573 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3574 builtin)
3575 << " variable needs to be a bool scalar. "
3576 << message;
3577 })) {
3578 return error;
3579 }
3580 }
3581
3582 return ValidateFullyCoveredAtReference(decoration, inst, inst, inst);
3583 }
3584
ValidateFullyCoveredAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3585 spv_result_t BuiltInsValidator::ValidateFullyCoveredAtReference(
3586 const Decoration& decoration, const Instruction& built_in_inst,
3587 const Instruction& referenced_inst,
3588 const Instruction& referenced_from_inst) {
3589
3590 if (spvIsVulkanEnv(_.context()->target_env)) {
3591 const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
3592 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
3593 if (storage_class != SpvStorageClassMax &&
3594 storage_class != SpvStorageClassInput) {
3595 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
3596 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3597 << _.VkErrorID(vuid) << spvLogStringForEnv(_.context()->target_env)
3598 << " spec allows BuiltIn "
3599 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
3600 << " to be only used for variables with Input storage class. "
3601 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3602 referenced_from_inst)
3603 << " " << GetStorageClassDesc(referenced_from_inst);
3604 }
3605
3606 for (const SpvExecutionModel execution_model : execution_models_) {
3607 if (execution_model != SpvExecutionModelFragment) {
3608 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel);
3609 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3610 << _.VkErrorID(vuid)
3611 << spvLogStringForEnv(_.context()->target_env)
3612 << " spec allows BuiltIn "
3613 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
3614 << " to be used only with Fragment execution model. "
3615 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3616 referenced_from_inst, execution_model);
3617 }
3618 }
3619 }
3620
3621 if (function_id_ == 0) {
3622 // Propagate this rule to all dependant ids in the global scope.
3623 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
3624 &BuiltInsValidator::ValidateFullyCoveredAtReference, this, decoration,
3625 built_in_inst, referenced_from_inst, std::placeholders::_1));
3626 }
3627
3628 return SPV_SUCCESS;
3629 }
3630
ValidateSMBuiltinsAtDefinition(const Decoration & decoration,const Instruction & inst)3631 spv_result_t BuiltInsValidator::ValidateSMBuiltinsAtDefinition(
3632 const Decoration& decoration, const Instruction& inst) {
3633 if (spvIsVulkanEnv(_.context()->target_env)) {
3634 if (spv_result_t error = ValidateI32(
3635 decoration, inst,
3636 [this, &inst,
3637 &decoration](const std::string& message) -> spv_result_t {
3638 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3639 << "According to the "
3640 << spvLogStringForEnv(_.context()->target_env)
3641 << " spec BuiltIn "
3642 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3643 decoration.params()[0])
3644 << " variable needs to be a 32-bit int scalar. "
3645 << message;
3646 })) {
3647 return error;
3648 }
3649 }
3650
3651 // Seed at reference checks with this built-in.
3652 return ValidateSMBuiltinsAtReference(decoration, inst, inst, inst);
3653 }
3654
ValidateSMBuiltinsAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3655 spv_result_t BuiltInsValidator::ValidateSMBuiltinsAtReference(
3656 const Decoration& decoration, const Instruction& built_in_inst,
3657 const Instruction& referenced_inst,
3658 const Instruction& referenced_from_inst) {
3659 if (spvIsVulkanEnv(_.context()->target_env)) {
3660 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
3661 if (storage_class != SpvStorageClassMax &&
3662 storage_class != SpvStorageClassInput) {
3663 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3664 << spvLogStringForEnv(_.context()->target_env)
3665 << " spec allows BuiltIn "
3666 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3667 decoration.params()[0])
3668 << " to be only used for "
3669 "variables with Input storage class. "
3670 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3671 referenced_from_inst)
3672 << " " << GetStorageClassDesc(referenced_from_inst);
3673 }
3674 }
3675
3676 if (function_id_ == 0) {
3677 // Propagate this rule to all dependant ids in the global scope.
3678 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
3679 &BuiltInsValidator::ValidateSMBuiltinsAtReference, this, decoration,
3680 built_in_inst, referenced_from_inst, std::placeholders::_1));
3681 }
3682
3683 return SPV_SUCCESS;
3684 }
3685
ValidatePrimitiveShadingRateAtDefinition(const Decoration & decoration,const Instruction & inst)3686 spv_result_t BuiltInsValidator::ValidatePrimitiveShadingRateAtDefinition(
3687 const Decoration& decoration, const Instruction& inst) {
3688 if (spvIsVulkanEnv(_.context()->target_env)) {
3689 if (spv_result_t error = ValidateI32(
3690 decoration, inst,
3691 [this, &inst,
3692 &decoration](const std::string& message) -> spv_result_t {
3693 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3694 << _.VkErrorID(4486)
3695 << "According to the Vulkan spec BuiltIn "
3696 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3697 decoration.params()[0])
3698 << " variable needs to be a 32-bit int scalar. "
3699 << message;
3700 })) {
3701 return error;
3702 }
3703 }
3704
3705 // Seed at reference checks with this built-in.
3706 return ValidatePrimitiveShadingRateAtReference(decoration, inst, inst, inst);
3707 }
3708
ValidatePrimitiveShadingRateAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3709 spv_result_t BuiltInsValidator::ValidatePrimitiveShadingRateAtReference(
3710 const Decoration& decoration, const Instruction& built_in_inst,
3711 const Instruction& referenced_inst,
3712 const Instruction& referenced_from_inst) {
3713 if (spvIsVulkanEnv(_.context()->target_env)) {
3714 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
3715 if (storage_class != SpvStorageClassMax &&
3716 storage_class != SpvStorageClassOutput) {
3717 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3718 << _.VkErrorID(4485) << "Vulkan spec allows BuiltIn "
3719 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3720 decoration.params()[0])
3721 << " to be only used for variables with Output storage class. "
3722 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3723 referenced_from_inst)
3724 << " " << GetStorageClassDesc(referenced_from_inst);
3725 }
3726
3727 for (const SpvExecutionModel execution_model : execution_models_) {
3728 switch (execution_model) {
3729 case SpvExecutionModelVertex:
3730 case SpvExecutionModelGeometry:
3731 case SpvExecutionModelMeshNV:
3732 break;
3733 default: {
3734 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3735 << _.VkErrorID(4484) << "Vulkan spec allows BuiltIn "
3736 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3737 decoration.params()[0])
3738 << " to be used only with Vertex, Geometry, or MeshNV "
3739 "execution models. "
3740 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3741 referenced_from_inst, execution_model);
3742 }
3743 }
3744 }
3745 }
3746
3747 if (function_id_ == 0) {
3748 // Propagate this rule to all dependant ids in the global scope.
3749 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
3750 std::bind(&BuiltInsValidator::ValidatePrimitiveShadingRateAtReference,
3751 this, decoration, built_in_inst, referenced_from_inst,
3752 std::placeholders::_1));
3753 }
3754
3755 return SPV_SUCCESS;
3756 }
3757
ValidateShadingRateAtDefinition(const Decoration & decoration,const Instruction & inst)3758 spv_result_t BuiltInsValidator::ValidateShadingRateAtDefinition(
3759 const Decoration& decoration, const Instruction& inst) {
3760 if (spvIsVulkanEnv(_.context()->target_env)) {
3761 if (spv_result_t error = ValidateI32(
3762 decoration, inst,
3763 [this, &inst,
3764 &decoration](const std::string& message) -> spv_result_t {
3765 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3766 << _.VkErrorID(4492)
3767 << "According to the Vulkan spec BuiltIn "
3768 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3769 decoration.params()[0])
3770 << " variable needs to be a 32-bit int scalar. "
3771 << message;
3772 })) {
3773 return error;
3774 }
3775 }
3776
3777 // Seed at reference checks with this built-in.
3778 return ValidateShadingRateAtReference(decoration, inst, inst, inst);
3779 }
3780
ValidateShadingRateAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3781 spv_result_t BuiltInsValidator::ValidateShadingRateAtReference(
3782 const Decoration& decoration, const Instruction& built_in_inst,
3783 const Instruction& referenced_inst,
3784 const Instruction& referenced_from_inst) {
3785 if (spvIsVulkanEnv(_.context()->target_env)) {
3786 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
3787 if (storage_class != SpvStorageClassMax &&
3788 storage_class != SpvStorageClassInput) {
3789 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3790 << _.VkErrorID(4491) << "Vulkan spec allows BuiltIn "
3791 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3792 decoration.params()[0])
3793 << " to be only used for variables with Input storage class. "
3794 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3795 referenced_from_inst)
3796 << " " << GetStorageClassDesc(referenced_from_inst);
3797 }
3798
3799 for (const SpvExecutionModel execution_model : execution_models_) {
3800 if (execution_model != SpvExecutionModelFragment) {
3801 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3802 << _.VkErrorID(4490) << "Vulkan spec allows BuiltIn "
3803 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3804 decoration.params()[0])
3805 << " to be used only with the Fragment execution model. "
3806 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3807 referenced_from_inst, execution_model);
3808 }
3809 }
3810 }
3811
3812 if (function_id_ == 0) {
3813 // Propagate this rule to all dependant ids in the global scope.
3814 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
3815 &BuiltInsValidator::ValidateShadingRateAtReference, this, decoration,
3816 built_in_inst, referenced_from_inst, std::placeholders::_1));
3817 }
3818
3819 return SPV_SUCCESS;
3820 }
3821
ValidateRayTracingBuiltinsAtDefinition(const Decoration & decoration,const Instruction & inst)3822 spv_result_t BuiltInsValidator::ValidateRayTracingBuiltinsAtDefinition(
3823 const Decoration& decoration, const Instruction& inst) {
3824 if (spvIsVulkanEnv(_.context()->target_env)) {
3825 const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
3826 switch (builtin) {
3827 case SpvBuiltInHitTNV:
3828 case SpvBuiltInRayTminKHR:
3829 case SpvBuiltInRayTmaxKHR:
3830 // f32 scalar
3831 if (spv_result_t error = ValidateF32(
3832 decoration, inst,
3833 [this, &inst,
3834 builtin](const std::string& message) -> spv_result_t {
3835 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
3836 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3837 << _.VkErrorID(vuid)
3838 << "According to the Vulkan spec BuiltIn "
3839 << _.grammar().lookupOperandName(
3840 SPV_OPERAND_TYPE_BUILT_IN, builtin)
3841 << " variable needs to be a 32-bit float scalar. "
3842 << message;
3843 })) {
3844 return error;
3845 }
3846 break;
3847 case SpvBuiltInHitKindKHR:
3848 case SpvBuiltInInstanceCustomIndexKHR:
3849 case SpvBuiltInInstanceId:
3850 case SpvBuiltInRayGeometryIndexKHR:
3851 case SpvBuiltInIncomingRayFlagsKHR:
3852 // i32 scalar
3853 if (spv_result_t error = ValidateI32(
3854 decoration, inst,
3855 [this, &inst,
3856 builtin](const std::string& message) -> spv_result_t {
3857 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
3858 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3859 << _.VkErrorID(vuid)
3860 << "According to the Vulkan spec BuiltIn "
3861 << _.grammar().lookupOperandName(
3862 SPV_OPERAND_TYPE_BUILT_IN, builtin)
3863 << " variable needs to be a 32-bit int scalar. "
3864 << message;
3865 })) {
3866 return error;
3867 }
3868 break;
3869 case SpvBuiltInObjectRayDirectionKHR:
3870 case SpvBuiltInObjectRayOriginKHR:
3871 case SpvBuiltInWorldRayDirectionKHR:
3872 case SpvBuiltInWorldRayOriginKHR:
3873 // f32 vec3
3874 if (spv_result_t error = ValidateF32Vec(
3875 decoration, inst, 3,
3876 [this, &inst,
3877 builtin](const std::string& message) -> spv_result_t {
3878 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
3879 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3880 << _.VkErrorID(vuid)
3881 << "According to the Vulkan spec BuiltIn "
3882 << _.grammar().lookupOperandName(
3883 SPV_OPERAND_TYPE_BUILT_IN, builtin)
3884 << " variable needs to be a 3-component 32-bit float "
3885 "vector. "
3886 << message;
3887 })) {
3888 return error;
3889 }
3890 break;
3891 case SpvBuiltInLaunchIdKHR:
3892 case SpvBuiltInLaunchSizeKHR:
3893 // i32 vec3
3894 if (spv_result_t error = ValidateI32Vec(
3895 decoration, inst, 3,
3896 [this, &inst,
3897 builtin](const std::string& message) -> spv_result_t {
3898 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
3899 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3900 << _.VkErrorID(vuid)
3901 << "According to the Vulkan spec BuiltIn "
3902 << _.grammar().lookupOperandName(
3903 SPV_OPERAND_TYPE_BUILT_IN, builtin)
3904 << " variable needs to be a 3-component 32-bit int "
3905 "vector. "
3906 << message;
3907 })) {
3908 return error;
3909 }
3910 break;
3911 case SpvBuiltInObjectToWorldKHR:
3912 case SpvBuiltInWorldToObjectKHR:
3913 // f32 mat4x3
3914 if (spv_result_t error = ValidateF32Mat(
3915 decoration, inst, 3, 4,
3916 [this, &inst,
3917 builtin](const std::string& message) -> spv_result_t {
3918 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
3919 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3920 << _.VkErrorID(vuid)
3921 << "According to the Vulkan spec BuiltIn "
3922 << _.grammar().lookupOperandName(
3923 SPV_OPERAND_TYPE_BUILT_IN, builtin)
3924 << " variable needs to be a matrix with"
3925 << " 4 columns of 3-component vectors of 32-bit "
3926 "floats. "
3927 << message;
3928 })) {
3929 return error;
3930 }
3931 break;
3932 default:
3933 assert(0 && "Unexpected ray tracing builtin");
3934 break;
3935 }
3936 }
3937
3938 // Seed at reference checks with this built-in.
3939 return ValidateRayTracingBuiltinsAtReference(decoration, inst, inst, inst);
3940 }
3941
ValidateRayTracingBuiltinsAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3942 spv_result_t BuiltInsValidator::ValidateRayTracingBuiltinsAtReference(
3943 const Decoration& decoration, const Instruction& built_in_inst,
3944 const Instruction& referenced_inst,
3945 const Instruction& referenced_from_inst) {
3946 if (spvIsVulkanEnv(_.context()->target_env)) {
3947 const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
3948 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
3949 if (storage_class != SpvStorageClassMax &&
3950 storage_class != SpvStorageClassInput) {
3951 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
3952 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3953 << _.VkErrorID(vuid) << "Vulkan spec allows BuiltIn "
3954 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3955 decoration.params()[0])
3956 << " to be only used for variables with Input storage class. "
3957 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3958 referenced_from_inst)
3959 << " " << GetStorageClassDesc(referenced_from_inst);
3960 }
3961
3962 for (const SpvExecutionModel execution_model : execution_models_) {
3963 if (!IsExecutionModelValidForRtBuiltIn(builtin, execution_model)) {
3964 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel);
3965 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3966 << _.VkErrorID(vuid) << "Vulkan spec does not allow BuiltIn "
3967 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3968 decoration.params()[0])
3969 << " to be used with the execution model "
3970 << _.grammar().lookupOperandName(
3971 SPV_OPERAND_TYPE_EXECUTION_MODEL, execution_model)
3972 << ".\n"
3973 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3974 referenced_from_inst, execution_model);
3975 }
3976 }
3977 }
3978
3979 if (function_id_ == 0) {
3980 // Propagate this rule to all dependant ids in the global scope.
3981 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
3982 std::bind(&BuiltInsValidator::ValidateRayTracingBuiltinsAtReference,
3983 this, decoration, built_in_inst, referenced_from_inst,
3984 std::placeholders::_1));
3985 }
3986
3987 return SPV_SUCCESS;
3988 }
3989
ValidateSingleBuiltInAtDefinition(const Decoration & decoration,const Instruction & inst)3990 spv_result_t BuiltInsValidator::ValidateSingleBuiltInAtDefinition(
3991 const Decoration& decoration, const Instruction& inst) {
3992 const SpvBuiltIn label = SpvBuiltIn(decoration.params()[0]);
3993
3994 // Builtins can only be applied to variables, structures or constants.
3995 auto target_opcode = inst.opcode();
3996 if (target_opcode != SpvOpTypeStruct && target_opcode != SpvOpVariable &&
3997 !spvOpcodeIsConstant(target_opcode)) {
3998 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3999 << "BuiltIns can only target variables, structs or constants";
4000 }
4001
4002 if (!spvIsVulkanEnv(_.context()->target_env)) {
4003 // Early return. All currently implemented rules are based on Vulkan spec.
4004 //
4005 // TODO: If you are adding validation rules for environments other than
4006 // Vulkan (or general rules which are not environment independent), then
4007 // you need to modify or remove this condition. Consider also adding early
4008 // returns into BuiltIn-specific rules, so that the system doesn't spawn new
4009 // rules which don't do anything.
4010 return SPV_SUCCESS;
4011 }
4012
4013 // If you are adding a new BuiltIn enum, please register it here.
4014 // If the newly added enum has validation rules associated with it
4015 // consider leaving a TODO and/or creating an issue.
4016 switch (label) {
4017 case SpvBuiltInClipDistance:
4018 case SpvBuiltInCullDistance: {
4019 return ValidateClipOrCullDistanceAtDefinition(decoration, inst);
4020 }
4021 case SpvBuiltInFragCoord: {
4022 return ValidateFragCoordAtDefinition(decoration, inst);
4023 }
4024 case SpvBuiltInFragDepth: {
4025 return ValidateFragDepthAtDefinition(decoration, inst);
4026 }
4027 case SpvBuiltInFrontFacing: {
4028 return ValidateFrontFacingAtDefinition(decoration, inst);
4029 }
4030 case SpvBuiltInGlobalInvocationId:
4031 case SpvBuiltInLocalInvocationId:
4032 case SpvBuiltInNumWorkgroups:
4033 case SpvBuiltInWorkgroupId: {
4034 return ValidateComputeShaderI32Vec3InputAtDefinition(decoration, inst);
4035 }
4036 case SpvBuiltInHelperInvocation: {
4037 return ValidateHelperInvocationAtDefinition(decoration, inst);
4038 }
4039 case SpvBuiltInInvocationId: {
4040 return ValidateInvocationIdAtDefinition(decoration, inst);
4041 }
4042 case SpvBuiltInInstanceIndex: {
4043 return ValidateInstanceIndexAtDefinition(decoration, inst);
4044 }
4045 case SpvBuiltInLayer:
4046 case SpvBuiltInViewportIndex: {
4047 return ValidateLayerOrViewportIndexAtDefinition(decoration, inst);
4048 }
4049 case SpvBuiltInPatchVertices: {
4050 return ValidatePatchVerticesAtDefinition(decoration, inst);
4051 }
4052 case SpvBuiltInPointCoord: {
4053 return ValidatePointCoordAtDefinition(decoration, inst);
4054 }
4055 case SpvBuiltInPointSize: {
4056 return ValidatePointSizeAtDefinition(decoration, inst);
4057 }
4058 case SpvBuiltInPosition: {
4059 return ValidatePositionAtDefinition(decoration, inst);
4060 }
4061 case SpvBuiltInPrimitiveId: {
4062 return ValidatePrimitiveIdAtDefinition(decoration, inst);
4063 }
4064 case SpvBuiltInSampleId: {
4065 return ValidateSampleIdAtDefinition(decoration, inst);
4066 }
4067 case SpvBuiltInSampleMask: {
4068 return ValidateSampleMaskAtDefinition(decoration, inst);
4069 }
4070 case SpvBuiltInSamplePosition: {
4071 return ValidateSamplePositionAtDefinition(decoration, inst);
4072 }
4073 case SpvBuiltInSubgroupId:
4074 case SpvBuiltInNumSubgroups: {
4075 return ValidateComputeI32InputAtDefinition(decoration, inst);
4076 }
4077 case SpvBuiltInSubgroupLocalInvocationId:
4078 case SpvBuiltInSubgroupSize: {
4079 return ValidateI32InputAtDefinition(decoration, inst);
4080 }
4081 case SpvBuiltInSubgroupEqMask:
4082 case SpvBuiltInSubgroupGeMask:
4083 case SpvBuiltInSubgroupGtMask:
4084 case SpvBuiltInSubgroupLeMask:
4085 case SpvBuiltInSubgroupLtMask: {
4086 return ValidateI32Vec4InputAtDefinition(decoration, inst);
4087 }
4088 case SpvBuiltInTessCoord: {
4089 return ValidateTessCoordAtDefinition(decoration, inst);
4090 }
4091 case SpvBuiltInTessLevelOuter: {
4092 return ValidateTessLevelOuterAtDefinition(decoration, inst);
4093 }
4094 case SpvBuiltInTessLevelInner: {
4095 return ValidateTessLevelInnerAtDefinition(decoration, inst);
4096 }
4097 case SpvBuiltInVertexIndex: {
4098 return ValidateVertexIndexAtDefinition(decoration, inst);
4099 }
4100 case SpvBuiltInWorkgroupSize: {
4101 return ValidateWorkgroupSizeAtDefinition(decoration, inst);
4102 }
4103 case SpvBuiltInVertexId: {
4104 return ValidateVertexIdAtDefinition(decoration, inst);
4105 }
4106 case SpvBuiltInLocalInvocationIndex: {
4107 return ValidateLocalInvocationIndexAtDefinition(decoration, inst);
4108 }
4109 case SpvBuiltInWarpsPerSMNV:
4110 case SpvBuiltInSMCountNV:
4111 case SpvBuiltInWarpIDNV:
4112 case SpvBuiltInSMIDNV: {
4113 return ValidateSMBuiltinsAtDefinition(decoration, inst);
4114 }
4115 case SpvBuiltInBaseInstance:
4116 case SpvBuiltInBaseVertex: {
4117 return ValidateBaseInstanceOrVertexAtDefinition(decoration, inst);
4118 }
4119 case SpvBuiltInDrawIndex: {
4120 return ValidateDrawIndexAtDefinition(decoration, inst);
4121 }
4122 case SpvBuiltInViewIndex: {
4123 return ValidateViewIndexAtDefinition(decoration, inst);
4124 }
4125 case SpvBuiltInDeviceIndex: {
4126 return ValidateDeviceIndexAtDefinition(decoration, inst);
4127 }
4128 case SpvBuiltInFragInvocationCountEXT: {
4129 // alias SpvBuiltInInvocationsPerPixelNV
4130 return ValidateFragInvocationCountAtDefinition(decoration, inst);
4131 }
4132 case SpvBuiltInFragSizeEXT: {
4133 // alias SpvBuiltInFragmentSizeNV
4134 return ValidateFragSizeAtDefinition(decoration, inst);
4135 }
4136 case SpvBuiltInFragStencilRefEXT: {
4137 return ValidateFragStencilRefAtDefinition(decoration, inst);
4138 }
4139 case SpvBuiltInFullyCoveredEXT:{
4140 return ValidateFullyCoveredAtDefinition(decoration, inst);
4141 }
4142 // Ray tracing builtins
4143 case SpvBuiltInHitKindKHR: // alias SpvBuiltInHitKindNV
4144 case SpvBuiltInHitTNV: // NOT present in KHR
4145 case SpvBuiltInInstanceId:
4146 case SpvBuiltInLaunchIdKHR: // alias SpvBuiltInLaunchIdNV
4147 case SpvBuiltInLaunchSizeKHR: // alias SpvBuiltInLaunchSizeNV
4148 case SpvBuiltInWorldRayOriginKHR: // alias SpvBuiltInWorldRayOriginNV
4149 case SpvBuiltInWorldRayDirectionKHR: // alias SpvBuiltInWorldRayDirectionNV
4150 case SpvBuiltInObjectRayOriginKHR: // alias SpvBuiltInObjectRayOriginNV
4151 case SpvBuiltInObjectRayDirectionKHR: // alias
4152 // SpvBuiltInObjectRayDirectionNV
4153 case SpvBuiltInRayTminKHR: // alias SpvBuiltInRayTminNV
4154 case SpvBuiltInRayTmaxKHR: // alias SpvBuiltInRayTmaxNV
4155 case SpvBuiltInInstanceCustomIndexKHR: // alias
4156 // SpvBuiltInInstanceCustomIndexNV
4157 case SpvBuiltInObjectToWorldKHR: // alias SpvBuiltInObjectToWorldNV
4158 case SpvBuiltInWorldToObjectKHR: // alias SpvBuiltInWorldToObjectNV
4159 case SpvBuiltInIncomingRayFlagsKHR: // alias SpvBuiltInIncomingRayFlagsNV
4160 case SpvBuiltInRayGeometryIndexKHR: { // NOT present in NV
4161 return ValidateRayTracingBuiltinsAtDefinition(decoration, inst);
4162 }
4163 case SpvBuiltInWorkDim:
4164 case SpvBuiltInGlobalSize:
4165 case SpvBuiltInEnqueuedWorkgroupSize:
4166 case SpvBuiltInGlobalOffset:
4167 case SpvBuiltInGlobalLinearId:
4168 case SpvBuiltInSubgroupMaxSize:
4169 case SpvBuiltInNumEnqueuedSubgroups:
4170 case SpvBuiltInBaryCoordNoPerspAMD:
4171 case SpvBuiltInBaryCoordNoPerspCentroidAMD:
4172 case SpvBuiltInBaryCoordNoPerspSampleAMD:
4173 case SpvBuiltInBaryCoordSmoothAMD:
4174 case SpvBuiltInBaryCoordSmoothCentroidAMD:
4175 case SpvBuiltInBaryCoordSmoothSampleAMD:
4176 case SpvBuiltInBaryCoordPullModelAMD:
4177 case SpvBuiltInViewportMaskNV:
4178 case SpvBuiltInSecondaryPositionNV:
4179 case SpvBuiltInSecondaryViewportMaskNV:
4180 case SpvBuiltInPositionPerViewNV:
4181 case SpvBuiltInViewportMaskPerViewNV:
4182 case SpvBuiltInMax:
4183 case SpvBuiltInTaskCountNV:
4184 case SpvBuiltInPrimitiveCountNV:
4185 case SpvBuiltInPrimitiveIndicesNV:
4186 case SpvBuiltInClipDistancePerViewNV:
4187 case SpvBuiltInCullDistancePerViewNV:
4188 case SpvBuiltInLayerPerViewNV:
4189 case SpvBuiltInMeshViewCountNV:
4190 case SpvBuiltInMeshViewIndicesNV:
4191 case SpvBuiltInBaryCoordNV:
4192 case SpvBuiltInBaryCoordNoPerspNV:
4193 // No validation rules (for the moment).
4194 break;
4195
4196 case SpvBuiltInPrimitiveShadingRateKHR: {
4197 return ValidatePrimitiveShadingRateAtDefinition(decoration, inst);
4198 }
4199 case SpvBuiltInShadingRateKHR: {
4200 return ValidateShadingRateAtDefinition(decoration, inst);
4201 }
4202 }
4203 return SPV_SUCCESS;
4204 }
4205
ValidateBuiltInsAtDefinition()4206 spv_result_t BuiltInsValidator::ValidateBuiltInsAtDefinition() {
4207 for (const auto& kv : _.id_decorations()) {
4208 const uint32_t id = kv.first;
4209 const auto& decorations = kv.second;
4210 if (decorations.empty()) {
4211 continue;
4212 }
4213
4214 const Instruction* inst = _.FindDef(id);
4215 assert(inst);
4216
4217 for (const auto& decoration : kv.second) {
4218 if (decoration.dec_type() != SpvDecorationBuiltIn) {
4219 continue;
4220 }
4221
4222 if (spv_result_t error =
4223 ValidateSingleBuiltInAtDefinition(decoration, *inst)) {
4224 return error;
4225 }
4226 }
4227 }
4228
4229 return SPV_SUCCESS;
4230 }
4231
Run()4232 spv_result_t BuiltInsValidator::Run() {
4233 // First pass: validate all built-ins at definition and seed
4234 // id_to_at_reference_checks_ with built-ins.
4235 if (auto error = ValidateBuiltInsAtDefinition()) {
4236 return error;
4237 }
4238
4239 if (id_to_at_reference_checks_.empty()) {
4240 // No validation tasks were seeded. Nothing else to do.
4241 return SPV_SUCCESS;
4242 }
4243
4244 // Second pass: validate every id reference in the module using
4245 // rules in id_to_at_reference_checks_.
4246 for (const Instruction& inst : _.ordered_instructions()) {
4247 Update(inst);
4248
4249 std::set<uint32_t> already_checked;
4250
4251 for (const auto& operand : inst.operands()) {
4252 if (!spvIsIdType(operand.type)) {
4253 // Not id.
4254 continue;
4255 }
4256
4257 const uint32_t id = inst.word(operand.offset);
4258 if (id == inst.id()) {
4259 // No need to check result id.
4260 continue;
4261 }
4262
4263 if (!already_checked.insert(id).second) {
4264 // The instruction has already referenced this id.
4265 continue;
4266 }
4267
4268 // Instruction references the id. Run all checks associated with the id
4269 // on the instruction. id_to_at_reference_checks_ can be modified in the
4270 // process, iterators are safe because it's a tree-based map.
4271 const auto it = id_to_at_reference_checks_.find(id);
4272 if (it != id_to_at_reference_checks_.end()) {
4273 for (const auto& check : it->second) {
4274 if (spv_result_t error = check(inst)) {
4275 return error;
4276 }
4277 }
4278 }
4279 }
4280 }
4281
4282 return SPV_SUCCESS;
4283 }
4284
4285 } // namespace
4286
4287 // Validates correctness of built-in variables.
ValidateBuiltIns(ValidationState_t & _)4288 spv_result_t ValidateBuiltIns(ValidationState_t& _) {
4289 BuiltInsValidator validator(_);
4290 return validator.Run();
4291 }
4292
4293 } // namespace val
4294 } // namespace spvtools
4295