1// Copyright (c) 2018 Google LLC.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// Validates correctness of built-in variables.
16
17#include "source/val/validate.h"
18
19#include <functional>
20#include <list>
21#include <map>
22#include <set>
23#include <sstream>
24#include <stack>
25#include <string>
26#include <unordered_map>
27#include <vector>
28
29#include "source/diagnostic.h"
30#include "source/opcode.h"
31#include "source/spirv_target_env.h"
32#include "source/util/bitutils.h"
33#include "source/val/instruction.h"
34#include "source/val/validation_state.h"
35
36namespace spvtools {
37namespace val {
38namespace {
39
40// Returns a short textual description of the id defined by the given
41// instruction.
42std::string GetIdDesc(const Instruction& inst) {
43 std::ostringstream ss;
44 ss << "ID <" << inst.id() << "> (Op" << spvOpcodeString(inst.opcode()) << ")";
45 return ss.str();
46}
47
48// Gets underlying data type which is
49// - member type if instruction is OpTypeStruct
50// (member index is taken from decoration).
51// - data type if id creates a pointer.
52// - type of the constant if instruction is OpConst or OpSpecConst.
53//
54// Fails in any other case. The function is based on built-ins allowed by
55// the Vulkan spec.
56// TODO: If non-Vulkan validation rules are added then it might need
57// to be refactored.
58spv_result_t GetUnderlyingType(ValidationState_t& _,
59 const Decoration& decoration,
60 const Instruction& inst,
61 uint32_t* underlying_type) {
62 if (decoration.struct_member_index() != Decoration::kInvalidMember) {
63 if (inst.opcode() != SpvOpTypeStruct) {
64 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
65 << GetIdDesc(inst)
66 << "Attempted to get underlying data type via member index for "
67 "non-struct type.";
68 }
69 *underlying_type = inst.word(decoration.struct_member_index() + 2);
70 return SPV_SUCCESS;
71 }
72
73 if (inst.opcode() == SpvOpTypeStruct) {
74 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
75 << GetIdDesc(inst)
76 << " did not find an member index to get underlying data type for "
77 "struct type.";
78 }
79
80 if (spvOpcodeIsConstant(inst.opcode())) {
81 *underlying_type = inst.type_id();
82 return SPV_SUCCESS;
83 }
84
85 uint32_t storage_class = 0;
86 if (!_.GetPointerTypeInfo(inst.type_id(), underlying_type, &storage_class)) {
87 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
88 << GetIdDesc(inst)
89 << " is decorated with BuiltIn. BuiltIn decoration should only be "
90 "applied to struct types, variables and constants.";
91 }
92 return SPV_SUCCESS;
93}
94
95// Returns Storage Class used by the instruction if applicable.
96// Returns SpvStorageClassMax if not.
97SpvStorageClass GetStorageClass(const Instruction& inst) {
98 switch (inst.opcode()) {
99 case SpvOpTypePointer:
100 case SpvOpTypeForwardPointer: {
101 return SpvStorageClass(inst.word(2));
102 }
103 case SpvOpVariable: {
104 return SpvStorageClass(inst.word(3));
105 }
106 case SpvOpGenericCastToPtrExplicit: {
107 return SpvStorageClass(inst.word(4));
108 }
109 default: { break; }
110 }
111 return SpvStorageClassMax;
112}
113
114bool IsBuiltInValidForWebGPU(SpvBuiltIn label) {
115 switch (label) {
116 case SpvBuiltInPosition:
117 case SpvBuiltInVertexIndex:
118 case SpvBuiltInInstanceIndex:
119 case SpvBuiltInFrontFacing:
120 case SpvBuiltInFragCoord:
121 case SpvBuiltInFragDepth:
122 case SpvBuiltInNumWorkgroups:
123 case SpvBuiltInWorkgroupSize:
124 case SpvBuiltInLocalInvocationId:
125 case SpvBuiltInGlobalInvocationId:
126 case SpvBuiltInLocalInvocationIndex: {
127 return true;
128 }
129 default:
130 break;
131 }
132
133 return false;
134}
135
136// Helper class managing validation of built-ins.
137// TODO: Generic functionality of this class can be moved into
138// ValidationState_t to be made available to other users.
139class BuiltInsValidator {
140 public:
141 BuiltInsValidator(ValidationState_t& vstate) : _(vstate) {}
142
143 // Run validation.
144 spv_result_t Run();
145
146 private:
147 // Goes through all decorations in the module, if decoration is BuiltIn
148 // calls ValidateSingleBuiltInAtDefinition().
149 spv_result_t ValidateBuiltInsAtDefinition();
150
151 // Validates the instruction defining an id with built-in decoration.
152 // Can be called multiple times for the same id, if multiple built-ins are
153 // specified. Seeds id_to_at_reference_checks_ with decorated ids if needed.
154 spv_result_t ValidateSingleBuiltInAtDefinition(const Decoration& decoration,
155 const Instruction& inst);
156
157 // The following section contains functions which are called when id defined
158 // by |inst| is decorated with BuiltIn |decoration|.
159 // Most functions are specific to a single built-in and have naming scheme:
160 // ValidateXYZAtDefinition. Some functions are common to multiple kinds of
161 // BuiltIn.
162 spv_result_t ValidateClipOrCullDistanceAtDefinition(
163 const Decoration& decoration, const Instruction& inst);
164 spv_result_t ValidateFragCoordAtDefinition(const Decoration& decoration,
165 const Instruction& inst);
166 spv_result_t ValidateFragDepthAtDefinition(const Decoration& decoration,
167 const Instruction& inst);
168 spv_result_t ValidateFrontFacingAtDefinition(const Decoration& decoration,
169 const Instruction& inst);
170 spv_result_t ValidateHelperInvocationAtDefinition(
171 const Decoration& decoration, const Instruction& inst);
172 spv_result_t ValidateInvocationIdAtDefinition(const Decoration& decoration,
173 const Instruction& inst);
174 spv_result_t ValidateInstanceIndexAtDefinition(const Decoration& decoration,
175 const Instruction& inst);
176 spv_result_t ValidateLayerOrViewportIndexAtDefinition(
177 const Decoration& decoration, const Instruction& inst);
178 spv_result_t ValidatePatchVerticesAtDefinition(const Decoration& decoration,
179 const Instruction& inst);
180 spv_result_t ValidatePointCoordAtDefinition(const Decoration& decoration,
181 const Instruction& inst);
182 spv_result_t ValidatePointSizeAtDefinition(const Decoration& decoration,
183 const Instruction& inst);
184 spv_result_t ValidatePositionAtDefinition(const Decoration& decoration,
185 const Instruction& inst);
186 spv_result_t ValidatePrimitiveIdAtDefinition(const Decoration& decoration,
187 const Instruction& inst);
188 spv_result_t ValidateSampleIdAtDefinition(const Decoration& decoration,
189 const Instruction& inst);
190 spv_result_t ValidateSampleMaskAtDefinition(const Decoration& decoration,
191 const Instruction& inst);
192 spv_result_t ValidateSamplePositionAtDefinition(const Decoration& decoration,
193 const Instruction& inst);
194 spv_result_t ValidateTessCoordAtDefinition(const Decoration& decoration,
195 const Instruction& inst);
196 spv_result_t ValidateTessLevelOuterAtDefinition(const Decoration& decoration,
197 const Instruction& inst);
198 spv_result_t ValidateTessLevelInnerAtDefinition(const Decoration& decoration,
199 const Instruction& inst);
200 spv_result_t ValidateVertexIndexAtDefinition(const Decoration& decoration,
201 const Instruction& inst);
202 spv_result_t ValidateVertexIdOrInstanceIdAtDefinition(
203 const Decoration& decoration, const Instruction& inst);
204 spv_result_t ValidateLocalInvocationIndexAtDefinition(
205 const Decoration& decoration, const Instruction& inst);
206 spv_result_t ValidateWorkgroupSizeAtDefinition(const Decoration& decoration,
207 const Instruction& inst);
208 // Used for GlobalInvocationId, LocalInvocationId, NumWorkgroups, WorkgroupId.
209 spv_result_t ValidateComputeShaderI32Vec3InputAtDefinition(
210 const Decoration& decoration, const Instruction& inst);
211 spv_result_t ValidateSMBuiltinsAtDefinition(const Decoration& decoration,
212 const Instruction& inst);
213
214 // Used for SubgroupEqMask, SubgroupGeMask, SubgroupGtMask, SubgroupLtMask,
215 // SubgroupLeMask.
216 spv_result_t ValidateI32Vec4InputAtDefinition(const Decoration& decoration,
217 const Instruction& inst);
218 // Used for SubgroupLocalInvocationId, SubgroupSize.
219 spv_result_t ValidateI32InputAtDefinition(const Decoration& decoration,
220 const Instruction& inst);
221 // Used for SubgroupId, NumSubgroups.
222 spv_result_t ValidateComputeI32InputAtDefinition(const Decoration& decoration,
223 const Instruction& inst);
224
225 // The following section contains functions which are called when id defined
226 // by |referenced_inst| is
227 // 1. referenced by |referenced_from_inst|
228 // 2. dependent on |built_in_inst| which is decorated with BuiltIn
229 // |decoration|. Most functions are specific to a single built-in and have
230 // naming scheme: ValidateXYZAtReference. Some functions are common to
231 // multiple kinds of BuiltIn.
232 spv_result_t ValidateFragCoordAtReference(
233 const Decoration& decoration, const Instruction& built_in_inst,
234 const Instruction& referenced_inst,
235 const Instruction& referenced_from_inst);
236
237 spv_result_t ValidateFragDepthAtReference(
238 const Decoration& decoration, const Instruction& built_in_inst,
239 const Instruction& referenced_inst,
240 const Instruction& referenced_from_inst);
241
242 spv_result_t ValidateFrontFacingAtReference(
243 const Decoration& decoration, const Instruction& built_in_inst,
244 const Instruction& referenced_inst,
245 const Instruction& referenced_from_inst);
246
247 spv_result_t ValidateHelperInvocationAtReference(
248 const Decoration& decoration, const Instruction& built_in_inst,
249 const Instruction& referenced_inst,
250 const Instruction& referenced_from_inst);
251
252 spv_result_t ValidateInvocationIdAtReference(
253 const Decoration& decoration, const Instruction& built_in_inst,
254 const Instruction& referenced_inst,
255 const Instruction& referenced_from_inst);
256
257 spv_result_t ValidateInstanceIdAtReference(
258 const Decoration& decoration, const Instruction& built_in_inst,
259 const Instruction& referenced_inst,
260 const Instruction& referenced_from_inst);
261
262 spv_result_t ValidateInstanceIndexAtReference(
263 const Decoration& decoration, const Instruction& built_in_inst,
264 const Instruction& referenced_inst,
265 const Instruction& referenced_from_inst);
266
267 spv_result_t ValidatePatchVerticesAtReference(
268 const Decoration& decoration, const Instruction& built_in_inst,
269 const Instruction& referenced_inst,
270 const Instruction& referenced_from_inst);
271
272 spv_result_t ValidatePointCoordAtReference(
273 const Decoration& decoration, const Instruction& built_in_inst,
274 const Instruction& referenced_inst,
275 const Instruction& referenced_from_inst);
276
277 spv_result_t ValidatePointSizeAtReference(
278 const Decoration& decoration, const Instruction& built_in_inst,
279 const Instruction& referenced_inst,
280 const Instruction& referenced_from_inst);
281
282 spv_result_t ValidatePositionAtReference(
283 const Decoration& decoration, const Instruction& built_in_inst,
284 const Instruction& referenced_inst,
285 const Instruction& referenced_from_inst);
286
287 spv_result_t ValidatePrimitiveIdAtReference(
288 const Decoration& decoration, const Instruction& built_in_inst,
289 const Instruction& referenced_inst,
290 const Instruction& referenced_from_inst);
291
292 spv_result_t ValidateSampleIdAtReference(
293 const Decoration& decoration, const Instruction& built_in_inst,
294 const Instruction& referenced_inst,
295 const Instruction& referenced_from_inst);
296
297 spv_result_t ValidateSampleMaskAtReference(
298 const Decoration& decoration, const Instruction& built_in_inst,
299 const Instruction& referenced_inst,
300 const Instruction& referenced_from_inst);
301
302 spv_result_t ValidateSamplePositionAtReference(
303 const Decoration& decoration, const Instruction& built_in_inst,
304 const Instruction& referenced_inst,
305 const Instruction& referenced_from_inst);
306
307 spv_result_t ValidateTessCoordAtReference(
308 const Decoration& decoration, const Instruction& built_in_inst,
309 const Instruction& referenced_inst,
310 const Instruction& referenced_from_inst);
311
312 spv_result_t ValidateTessLevelAtReference(
313 const Decoration& decoration, const Instruction& built_in_inst,
314 const Instruction& referenced_inst,
315 const Instruction& referenced_from_inst);
316
317 spv_result_t ValidateLocalInvocationIndexAtReference(
318 const Decoration& decoration, const Instruction& built_in_inst,
319 const Instruction& referenced_inst,
320 const Instruction& referenced_from_inst);
321
322 spv_result_t ValidateVertexIndexAtReference(
323 const Decoration& decoration, const Instruction& built_in_inst,
324 const Instruction& referenced_inst,
325 const Instruction& referenced_from_inst);
326
327 spv_result_t ValidateLayerOrViewportIndexAtReference(
328 const Decoration& decoration, const Instruction& built_in_inst,
329 const Instruction& referenced_inst,
330 const Instruction& referenced_from_inst);
331
332 spv_result_t ValidateWorkgroupSizeAtReference(
333 const Decoration& decoration, const Instruction& built_in_inst,
334 const Instruction& referenced_inst,
335 const Instruction& referenced_from_inst);
336
337 spv_result_t ValidateClipOrCullDistanceAtReference(
338 const Decoration& decoration, const Instruction& built_in_inst,
339 const Instruction& referenced_inst,
340 const Instruction& referenced_from_inst);
341
342 // Used for GlobalInvocationId, LocalInvocationId, NumWorkgroups, WorkgroupId.
343 spv_result_t ValidateComputeShaderI32Vec3InputAtReference(
344 const Decoration& decoration, const Instruction& built_in_inst,
345 const Instruction& referenced_inst,
346 const Instruction& referenced_from_inst);
347 // Used for SubgroupId and NumSubgroups.
348 spv_result_t ValidateComputeI32InputAtReference(
349 const Decoration& decoration, const Instruction& built_in_inst,
350 const Instruction& referenced_inst,
351 const Instruction& referenced_from_inst);
352
353 spv_result_t ValidateSMBuiltinsAtReference(
354 const Decoration& decoration, const Instruction& built_in_inst,
355 const Instruction& referenced_inst,
356 const Instruction& referenced_from_inst);
357
358 // Validates that |built_in_inst| is not (even indirectly) referenced from
359 // within a function which can be called with |execution_model|.
360 //
361 // |comment| - text explaining why the restriction was imposed.
362 // |decoration| - BuiltIn decoration which causes the restriction.
363 // |referenced_inst| - instruction which is dependent on |built_in_inst| and
364 // defines the id which was referenced.
365 // |referenced_from_inst| - instruction which references id defined by
366 // |referenced_inst| from within a function.
367 spv_result_t ValidateNotCalledWithExecutionModel(
368 const char* comment, SpvExecutionModel execution_model,
369 const Decoration& decoration, const Instruction& built_in_inst,
370 const Instruction& referenced_inst,
371 const Instruction& referenced_from_inst);
372
373 // The following section contains functions which check that the decorated
374 // variable has the type specified in the function name. |diag| would be
375 // called with a corresponding error message, if validation is not successful.
376 spv_result_t ValidateBool(
377 const Decoration& decoration, const Instruction& inst,
378 const std::function<spv_result_t(const std::string& message)>& diag);
379 spv_result_t ValidateI32(
380 const Decoration& decoration, const Instruction& inst,
381 const std::function<spv_result_t(const std::string& message)>& diag);
382 spv_result_t ValidateI32Vec(
383 const Decoration& decoration, const Instruction& inst,
384 uint32_t num_components,
385 const std::function<spv_result_t(const std::string& message)>& diag);
386 spv_result_t ValidateI32Arr(
387 const Decoration& decoration, const Instruction& inst,
388 const std::function<spv_result_t(const std::string& message)>& diag);
389 spv_result_t ValidateOptionalArrayedI32(
390 const Decoration& decoration, const Instruction& inst,
391 const std::function<spv_result_t(const std::string& message)>& diag);
392 spv_result_t ValidateI32Helper(
393 const Decoration& decoration, const Instruction& inst,
394 const std::function<spv_result_t(const std::string& message)>& diag,
395 uint32_t underlying_type);
396 spv_result_t ValidateF32(
397 const Decoration& decoration, const Instruction& inst,
398 const std::function<spv_result_t(const std::string& message)>& diag);
399 spv_result_t ValidateOptionalArrayedF32(
400 const Decoration& decoration, const Instruction& inst,
401 const std::function<spv_result_t(const std::string& message)>& diag);
402 spv_result_t ValidateF32Helper(
403 const Decoration& decoration, const Instruction& inst,
404 const std::function<spv_result_t(const std::string& message)>& diag,
405 uint32_t underlying_type);
406 spv_result_t ValidateF32Vec(
407 const Decoration& decoration, const Instruction& inst,
408 uint32_t num_components,
409 const std::function<spv_result_t(const std::string& message)>& diag);
410 spv_result_t ValidateOptionalArrayedF32Vec(
411 const Decoration& decoration, const Instruction& inst,
412 uint32_t num_components,
413 const std::function<spv_result_t(const std::string& message)>& diag);
414 spv_result_t ValidateF32VecHelper(
415 const Decoration& decoration, const Instruction& inst,
416 uint32_t num_components,
417 const std::function<spv_result_t(const std::string& message)>& diag,
418 uint32_t underlying_type);
419 // If |num_components| is zero, the number of components is not checked.
420 spv_result_t ValidateF32Arr(
421 const Decoration& decoration, const Instruction& inst,
422 uint32_t num_components,
423 const std::function<spv_result_t(const std::string& message)>& diag);
424 spv_result_t ValidateOptionalArrayedF32Arr(
425 const Decoration& decoration, const Instruction& inst,
426 uint32_t num_components,
427 const std::function<spv_result_t(const std::string& message)>& diag);
428 spv_result_t ValidateF32ArrHelper(
429 const Decoration& decoration, const Instruction& inst,
430 uint32_t num_components,
431 const std::function<spv_result_t(const std::string& message)>& diag,
432 uint32_t underlying_type);
433
434 // Generates strings like "Member #0 of struct ID <2>".
435 std::string GetDefinitionDesc(const Decoration& decoration,
436 const Instruction& inst) const;
437
438 // Generates strings like "ID <51> (OpTypePointer) is referencing ID <2>
439 // (OpTypeStruct) which is decorated with BuiltIn Position".
440 std::string GetReferenceDesc(
441 const Decoration& decoration, const Instruction& built_in_inst,
442 const Instruction& referenced_inst,
443 const Instruction& referenced_from_inst,
444 SpvExecutionModel execution_model = SpvExecutionModelMax) const;
445
446 // Generates strings like "ID <51> (OpTypePointer) uses storage class
447 // UniformConstant".
448 std::string GetStorageClassDesc(const Instruction& inst) const;
449
450 // Updates inner working of the class. Is called sequentially for every
451 // instruction.
452 void Update(const Instruction& inst);
453
454 ValidationState_t& _;
455
456 // Mapping id -> list of rules which validate instruction referencing the
457 // id. Rules can create new rules and add them to this container.
458 // Using std::map, and not std::unordered_map to avoid iterator invalidation
459 // during rehashing.
460 std::map<uint32_t, std::list<std::function<spv_result_t(const Instruction&)>>>
461 id_to_at_reference_checks_;
462
463 // Id of the function we are currently inside. 0 if not inside a function.
464 uint32_t function_id_ = 0;
465
466 // Entry points which can (indirectly) call the current function.
467 // The pointer either points to a vector inside to function_to_entry_points_
468 // or to no_entry_points_. The pointer is guaranteed to never be null.
469 const std::vector<uint32_t> no_entry_points;
470 const std::vector<uint32_t>* entry_points_ = &no_entry_points;
471
472 // Execution models with which the current function can be called.
473 std::set<SpvExecutionModel> execution_models_;
474};
475
476void BuiltInsValidator::Update(const Instruction& inst) {
477 const SpvOp opcode = inst.opcode();
478 if (opcode == SpvOpFunction) {
479 // Entering a function.
480 assert(function_id_ == 0);
481 function_id_ = inst.id();
482 execution_models_.clear();
483 entry_points_ = &_.FunctionEntryPoints(function_id_);
484 // Collect execution models from all entry points from which the current
485 // function can be called.
486 for (const uint32_t entry_point : *entry_points_) {
487 if (const auto* models = _.GetExecutionModels(entry_point)) {
488 execution_models_.insert(models->begin(), models->end());
489 }
490 }
491 }
492
493 if (opcode == SpvOpFunctionEnd) {
494 // Exiting a function.
495 assert(function_id_ != 0);
496 function_id_ = 0;
497 entry_points_ = &no_entry_points;
498 execution_models_.clear();
499 }
500}
501
502std::string BuiltInsValidator::GetDefinitionDesc(
503 const Decoration& decoration, const Instruction& inst) const {
504 std::ostringstream ss;
505 if (decoration.struct_member_index() != Decoration::kInvalidMember) {
506 assert(inst.opcode() == SpvOpTypeStruct);
507 ss << "Member #" << decoration.struct_member_index();
508 ss << " of struct ID <" << inst.id() << ">";
509 } else {
510 ss << GetIdDesc(inst);
511 }
512 return ss.str();
513}
514
515std::string BuiltInsValidator::GetReferenceDesc(
516 const Decoration& decoration, const Instruction& built_in_inst,
517 const Instruction& referenced_inst, const Instruction& referenced_from_inst,
518 SpvExecutionModel execution_model) const {
519 std::ostringstream ss;
520 ss << GetIdDesc(referenced_from_inst) << " is referencing "
521 << GetIdDesc(referenced_inst);
522 if (built_in_inst.id() != referenced_inst.id()) {
523 ss << " which is dependent on " << GetIdDesc(built_in_inst);
524 }
525
526 ss << " which is decorated with BuiltIn ";
527 ss << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
528 decoration.params()[0]);
529 if (function_id_) {
530 ss << " in function <" << function_id_ << ">";
531 if (execution_model != SpvExecutionModelMax) {
532 ss << " called with execution model ";
533 ss << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_EXECUTION_MODEL,
534 execution_model);
535 }
536 }
537 ss << ".";
538 return ss.str();
539}
540
541std::string BuiltInsValidator::GetStorageClassDesc(
542 const Instruction& inst) const {
543 std::ostringstream ss;
544 ss << GetIdDesc(inst) << " uses storage class ";
545 ss << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_STORAGE_CLASS,
546 GetStorageClass(inst));
547 ss << ".";
548 return ss.str();
549}
550
551spv_result_t BuiltInsValidator::ValidateBool(
552 const Decoration& decoration, const Instruction& inst,
553 const std::function<spv_result_t(const std::string& message)>& diag) {
554 uint32_t underlying_type = 0;
555 if (spv_result_t error =
556 GetUnderlyingType(_, decoration, inst, &underlying_type)) {
557 return error;
558 }
559
560 if (!_.IsBoolScalarType(underlying_type)) {
561 return diag(GetDefinitionDesc(decoration, inst) + " is not a bool scalar.");
562 }
563
564 return SPV_SUCCESS;
565}
566
567spv_result_t BuiltInsValidator::ValidateI32(
568 const Decoration& decoration, const Instruction& inst,
569 const std::function<spv_result_t(const std::string& message)>& diag) {
570 uint32_t underlying_type = 0;
571 if (spv_result_t error =
572 GetUnderlyingType(_, decoration, inst, &underlying_type)) {
573 return error;
574 }
575
576 return ValidateI32Helper(decoration, inst, diag, underlying_type);
577}
578
579spv_result_t BuiltInsValidator::ValidateOptionalArrayedI32(
580 const Decoration& decoration, const Instruction& inst,
581 const std::function<spv_result_t(const std::string& message)>& diag) {
582 uint32_t underlying_type = 0;
583 if (spv_result_t error =
584 GetUnderlyingType(_, decoration, inst, &underlying_type)) {
585 return error;
586 }
587
588 // Strip the array, if present.
589 if (_.GetIdOpcode(underlying_type) == SpvOpTypeArray) {
590 underlying_type = _.FindDef(underlying_type)->word(2u);
591 }
592
593 return ValidateI32Helper(decoration, inst, diag, underlying_type);
594}
595
596spv_result_t BuiltInsValidator::ValidateI32Helper(
597 const Decoration& decoration, const Instruction& inst,
598 const std::function<spv_result_t(const std::string& message)>& diag,
599 uint32_t underlying_type) {
600 if (!_.IsIntScalarType(underlying_type)) {
601 return diag(GetDefinitionDesc(decoration, inst) + " is not an int scalar.");
602 }
603
604 const uint32_t bit_width = _.GetBitWidth(underlying_type);
605 if (bit_width != 32) {
606 std::ostringstream ss;
607 ss << GetDefinitionDesc(decoration, inst) << " has bit width " << bit_width
608 << ".";
609 return diag(ss.str());
610 }
611
612 return SPV_SUCCESS;
613}
614
615spv_result_t BuiltInsValidator::ValidateOptionalArrayedF32(
616 const Decoration& decoration, const Instruction& inst,
617 const std::function<spv_result_t(const std::string& message)>& diag) {
618 uint32_t underlying_type = 0;
619 if (spv_result_t error =
620 GetUnderlyingType(_, decoration, inst, &underlying_type)) {
621 return error;
622 }
623
624 // Strip the array, if present.
625 if (_.GetIdOpcode(underlying_type) == SpvOpTypeArray) {
626 underlying_type = _.FindDef(underlying_type)->word(2u);
627 }
628
629 return ValidateF32Helper(decoration, inst, diag, underlying_type);
630}
631
632spv_result_t BuiltInsValidator::ValidateF32(
633 const Decoration& decoration, const Instruction& inst,
634 const std::function<spv_result_t(const std::string& message)>& diag) {
635 uint32_t underlying_type = 0;
636 if (spv_result_t error =
637 GetUnderlyingType(_, decoration, inst, &underlying_type)) {
638 return error;
639 }
640
641 return ValidateF32Helper(decoration, inst, diag, underlying_type);
642}
643
644spv_result_t BuiltInsValidator::ValidateF32Helper(
645 const Decoration& decoration, const Instruction& inst,
646 const std::function<spv_result_t(const std::string& message)>& diag,
647 uint32_t underlying_type) {
648 if (!_.IsFloatScalarType(underlying_type)) {
649 return diag(GetDefinitionDesc(decoration, inst) +
650 " is not a float scalar.");
651 }
652
653 const uint32_t bit_width = _.GetBitWidth(underlying_type);
654 if (bit_width != 32) {
655 std::ostringstream ss;
656 ss << GetDefinitionDesc(decoration, inst) << " has bit width " << bit_width
657 << ".";
658 return diag(ss.str());
659 }
660
661 return SPV_SUCCESS;
662}
663
664spv_result_t BuiltInsValidator::ValidateI32Vec(
665 const Decoration& decoration, const Instruction& inst,
666 uint32_t num_components,
667 const std::function<spv_result_t(const std::string& message)>& diag) {
668 uint32_t underlying_type = 0;
669 if (spv_result_t error =
670 GetUnderlyingType(_, decoration, inst, &underlying_type)) {
671 return error;
672 }
673
674 if (!_.IsIntVectorType(underlying_type)) {
675 return diag(GetDefinitionDesc(decoration, inst) + " is not an int vector.");
676 }
677
678 const uint32_t actual_num_components = _.GetDimension(underlying_type);
679 if (_.GetDimension(underlying_type) != num_components) {
680 std::ostringstream ss;
681 ss << GetDefinitionDesc(decoration, inst) << " has "
682 << actual_num_components << " components.";
683 return diag(ss.str());
684 }
685
686 const uint32_t bit_width = _.GetBitWidth(underlying_type);
687 if (bit_width != 32) {
688 std::ostringstream ss;
689 ss << GetDefinitionDesc(decoration, inst)
690 << " has components with bit width " << bit_width << ".";
691 return diag(ss.str());
692 }
693
694 return SPV_SUCCESS;
695}
696
697spv_result_t BuiltInsValidator::ValidateOptionalArrayedF32Vec(
698 const Decoration& decoration, const Instruction& inst,
699 uint32_t num_components,
700 const std::function<spv_result_t(const std::string& message)>& diag) {
701 uint32_t underlying_type = 0;
702 if (spv_result_t error =
703 GetUnderlyingType(_, decoration, inst, &underlying_type)) {
704 return error;
705 }
706
707 // Strip the array, if present.
708 if (_.GetIdOpcode(underlying_type) == SpvOpTypeArray) {
709 underlying_type = _.FindDef(underlying_type)->word(2u);
710 }
711
712 return ValidateF32VecHelper(decoration, inst, num_components, diag,
713 underlying_type);
714}
715
716spv_result_t BuiltInsValidator::ValidateF32Vec(
717 const Decoration& decoration, const Instruction& inst,
718 uint32_t num_components,
719 const std::function<spv_result_t(const std::string& message)>& diag) {
720 uint32_t underlying_type = 0;
721 if (spv_result_t error =
722 GetUnderlyingType(_, decoration, inst, &underlying_type)) {
723 return error;
724 }
725
726 return ValidateF32VecHelper(decoration, inst, num_components, diag,
727 underlying_type);
728}
729
730spv_result_t BuiltInsValidator::ValidateF32VecHelper(
731 const Decoration& decoration, const Instruction& inst,
732 uint32_t num_components,
733 const std::function<spv_result_t(const std::string& message)>& diag,
734 uint32_t underlying_type) {
735 if (!_.IsFloatVectorType(underlying_type)) {
736 return diag(GetDefinitionDesc(decoration, inst) +
737 " is not a float vector.");
738 }
739
740 const uint32_t actual_num_components = _.GetDimension(underlying_type);
741 if (_.GetDimension(underlying_type) != num_components) {
742 std::ostringstream ss;
743 ss << GetDefinitionDesc(decoration, inst) << " has "
744 << actual_num_components << " components.";
745 return diag(ss.str());
746 }
747
748 const uint32_t bit_width = _.GetBitWidth(underlying_type);
749 if (bit_width != 32) {
750 std::ostringstream ss;
751 ss << GetDefinitionDesc(decoration, inst)
752 << " has components with bit width " << bit_width << ".";
753 return diag(ss.str());
754 }
755
756 return SPV_SUCCESS;
757}
758
759spv_result_t BuiltInsValidator::ValidateI32Arr(
760 const Decoration& decoration, const Instruction& inst,
761 const std::function<spv_result_t(const std::string& message)>& diag) {
762 uint32_t underlying_type = 0;
763 if (spv_result_t error =
764 GetUnderlyingType(_, decoration, inst, &underlying_type)) {
765 return error;
766 }
767
768 const Instruction* const type_inst = _.FindDef(underlying_type);
769 if (type_inst->opcode() != SpvOpTypeArray) {
770 return diag(GetDefinitionDesc(decoration, inst) + " is not an array.");
771 }
772
773 const uint32_t component_type = type_inst->word(2);
774 if (!_.IsIntScalarType(component_type)) {
775 return diag(GetDefinitionDesc(decoration, inst) +
776 " components are not int scalar.");
777 }
778
779 const uint32_t bit_width = _.GetBitWidth(component_type);
780 if (bit_width != 32) {
781 std::ostringstream ss;
782 ss << GetDefinitionDesc(decoration, inst)
783 << " has components with bit width " << bit_width << ".";
784 return diag(ss.str());
785 }
786
787 return SPV_SUCCESS;
788}
789
790spv_result_t BuiltInsValidator::ValidateF32Arr(
791 const Decoration& decoration, const Instruction& inst,
792 uint32_t num_components,
793 const std::function<spv_result_t(const std::string& message)>& diag) {
794 uint32_t underlying_type = 0;
795 if (spv_result_t error =
796 GetUnderlyingType(_, decoration, inst, &underlying_type)) {
797 return error;
798 }
799
800 return ValidateF32ArrHelper(decoration, inst, num_components, diag,
801 underlying_type);
802}
803
804spv_result_t BuiltInsValidator::ValidateOptionalArrayedF32Arr(
805 const Decoration& decoration, const Instruction& inst,
806 uint32_t num_components,
807 const std::function<spv_result_t(const std::string& message)>& diag) {
808 uint32_t underlying_type = 0;
809 if (spv_result_t error =
810 GetUnderlyingType(_, decoration, inst, &underlying_type)) {
811 return error;
812 }
813
814 // Strip an extra layer of arraying if present.
815 if (_.GetIdOpcode(underlying_type) == SpvOpTypeArray) {
816 uint32_t subtype = _.FindDef(underlying_type)->word(2u);
817 if (_.GetIdOpcode(subtype) == SpvOpTypeArray) {
818 underlying_type = subtype;
819 }
820 }
821
822 return ValidateF32ArrHelper(decoration, inst, num_components, diag,
823 underlying_type);
824}
825
826spv_result_t BuiltInsValidator::ValidateF32ArrHelper(
827 const Decoration& decoration, const Instruction& inst,
828 uint32_t num_components,
829 const std::function<spv_result_t(const std::string& message)>& diag,
830 uint32_t underlying_type) {
831 const Instruction* const type_inst = _.FindDef(underlying_type);
832 if (type_inst->opcode() != SpvOpTypeArray) {
833 return diag(GetDefinitionDesc(decoration, inst) + " is not an array.");
834 }
835
836 const uint32_t component_type = type_inst->word(2);
837 if (!_.IsFloatScalarType(component_type)) {
838 return diag(GetDefinitionDesc(decoration, inst) +
839 " components are not float scalar.");
840 }
841
842 const uint32_t bit_width = _.GetBitWidth(component_type);
843 if (bit_width != 32) {
844 std::ostringstream ss;
845 ss << GetDefinitionDesc(decoration, inst)
846 << " has components with bit width " << bit_width << ".";
847 return diag(ss.str());
848 }
849
850 if (num_components != 0) {
851 uint64_t actual_num_components = 0;
852 if (!_.GetConstantValUint64(type_inst->word(3), &actual_num_components)) {
853 assert(0 && "Array type definition is corrupt");
854 }
855 if (actual_num_components != num_components) {
856 std::ostringstream ss;
857 ss << GetDefinitionDesc(decoration, inst) << " has "
858 << actual_num_components << " components.";
859 return diag(ss.str());
860 }
861 }
862
863 return SPV_SUCCESS;
864}
865
866spv_result_t BuiltInsValidator::ValidateNotCalledWithExecutionModel(
867 const char* comment, SpvExecutionModel execution_model,
868 const Decoration& decoration, const Instruction& built_in_inst,
869 const Instruction& referenced_inst,
870 const Instruction& referenced_from_inst) {
871 if (function_id_) {
872 if (execution_models_.count(execution_model)) {
873 const char* execution_model_str = _.grammar().lookupOperandName(
874 SPV_OPERAND_TYPE_EXECUTION_MODEL, execution_model);
875 const char* built_in_str = _.grammar().lookupOperandName(
876 SPV_OPERAND_TYPE_BUILT_IN, decoration.params()[0]);
877 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
878 << comment << " " << GetIdDesc(referenced_inst) << " depends on "
879 << GetIdDesc(built_in_inst) << " which is decorated with BuiltIn "
880 << built_in_str << "."
881 << " Id <" << referenced_inst.id() << "> is later referenced by "
882 << GetIdDesc(referenced_from_inst) << " in function <"
883 << function_id_ << "> which is called with execution model "
884 << execution_model_str << ".";
885 }
886 } else {
887 // Propagate this rule to all dependant ids in the global scope.
888 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
889 std::bind(&BuiltInsValidator::ValidateNotCalledWithExecutionModel, this,
890 comment, execution_model, decoration, built_in_inst,
891 referenced_from_inst, std::placeholders::_1));
892 }
893 return SPV_SUCCESS;
894}
895
896spv_result_t BuiltInsValidator::ValidateClipOrCullDistanceAtDefinition(
897 const Decoration& decoration, const Instruction& inst) {
898 // Seed at reference checks with this built-in.
899 return ValidateClipOrCullDistanceAtReference(decoration, inst, inst, inst);
900}
901
902spv_result_t BuiltInsValidator::ValidateClipOrCullDistanceAtReference(
903 const Decoration& decoration, const Instruction& built_in_inst,
904 const Instruction& referenced_inst,
905 const Instruction& referenced_from_inst) {
906 if (spvIsVulkanEnv(_.context()->target_env)) {
907 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
908 if (storage_class != SpvStorageClassMax &&
909 storage_class != SpvStorageClassInput &&
910 storage_class != SpvStorageClassOutput) {
911 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
912 << "Vulkan spec allows BuiltIn "
913 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
914 decoration.params()[0])
915 << " to be only used for variables with Input or Output storage "
916 "class. "
917 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
918 referenced_from_inst)
919 << " " << GetStorageClassDesc(referenced_from_inst);
920 }
921
922 if (storage_class == SpvStorageClassInput) {
923 assert(function_id_ == 0);
924 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
925 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this,
926 "Vulkan spec doesn't allow BuiltIn ClipDistance/CullDistance to be "
927 "used for variables with Input storage class if execution model is "
928 "Vertex.",
929 SpvExecutionModelVertex, decoration, built_in_inst,
930 referenced_from_inst, std::placeholders::_1));
931 }
932
933 if (storage_class == SpvStorageClassOutput) {
934 assert(function_id_ == 0);
935 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
936 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this,
937 "Vulkan spec doesn't allow BuiltIn ClipDistance/CullDistance to be "
938 "used for variables with Output storage class if execution model is "
939 "Fragment.",
940 SpvExecutionModelFragment, decoration, built_in_inst,
941 referenced_from_inst, std::placeholders::_1));
942 }
943
944 for (const SpvExecutionModel execution_model : execution_models_) {
945 switch (execution_model) {
946 case SpvExecutionModelFragment:
947 case SpvExecutionModelVertex: {
948 if (spv_result_t error = ValidateF32Arr(
949 decoration, built_in_inst, /* Any number of components */ 0,
950 [this, &decoration, &referenced_from_inst](
951 const std::string& message) -> spv_result_t {
952 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
953 << "According to the Vulkan spec BuiltIn "
954 << _.grammar().lookupOperandName(
955 SPV_OPERAND_TYPE_BUILT_IN,
956 decoration.params()[0])
957 << " variable needs to be a 32-bit float array. "
958 << message;
959 })) {
960 return error;
961 }
962 break;
963 }
964 case SpvExecutionModelTessellationControl:
965 case SpvExecutionModelTessellationEvaluation:
966 case SpvExecutionModelGeometry:
967 case SpvExecutionModelMeshNV: {
968 if (decoration.struct_member_index() != Decoration::kInvalidMember) {
969 // The outer level of array is applied on the variable.
970 if (spv_result_t error = ValidateF32Arr(
971 decoration, built_in_inst, /* Any number of components */ 0,
972 [this, &decoration, &referenced_from_inst](
973 const std::string& message) -> spv_result_t {
974 return _.diag(SPV_ERROR_INVALID_DATA,
975 &referenced_from_inst)
976 << "According to the Vulkan spec BuiltIn "
977 << _.grammar().lookupOperandName(
978 SPV_OPERAND_TYPE_BUILT_IN,
979 decoration.params()[0])
980 << " variable needs to be a 32-bit float array. "
981 << message;
982 })) {
983 return error;
984 }
985 } else {
986 if (spv_result_t error = ValidateOptionalArrayedF32Arr(
987 decoration, built_in_inst, /* Any number of components */ 0,
988 [this, &decoration, &referenced_from_inst](
989 const std::string& message) -> spv_result_t {
990 return _.diag(SPV_ERROR_INVALID_DATA,
991 &referenced_from_inst)
992 << "According to the Vulkan spec BuiltIn "
993 << _.grammar().lookupOperandName(
994 SPV_OPERAND_TYPE_BUILT_IN,
995 decoration.params()[0])
996 << " variable needs to be a 32-bit float array. "
997 << message;
998 })) {
999 return error;
1000 }
1001 }
1002 break;
1003 }
1004
1005 default: {
1006 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1007 << "Vulkan spec allows BuiltIn "
1008 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
1009 decoration.params()[0])
1010 << " to be used only with Fragment, Vertex, "
1011 "TessellationControl, TessellationEvaluation or Geometry "
1012 "execution models. "
1013 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1014 referenced_from_inst, execution_model);
1015 }
1016 }
1017 }
1018 }
1019
1020 if (function_id_ == 0) {
1021 // Propagate this rule to all dependant ids in the global scope.
1022 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
1023 std::bind(&BuiltInsValidator::ValidateClipOrCullDistanceAtReference,
1024 this, decoration, built_in_inst, referenced_from_inst,
1025 std::placeholders::_1));
1026 }
1027
1028 return SPV_SUCCESS;
1029}
1030
1031spv_result_t BuiltInsValidator::ValidateFragCoordAtDefinition(
1032 const Decoration& decoration, const Instruction& inst) {
1033 if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
1034 if (spv_result_t error = ValidateF32Vec(
1035 decoration, inst, 4,
1036 [this, &inst](const std::string& message) -> spv_result_t {
1037 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
1038 << "According to the "
1039 << spvLogStringForEnv(_.context()->target_env)
1040 << " spec BuiltIn FragCoord "
1041 "variable needs to be a 4-component 32-bit float "
1042 "vector. "
1043 << message;
1044 })) {
1045 return error;
1046 }
1047 }
1048
1049 // Seed at reference checks with this built-in.
1050 return ValidateFragCoordAtReference(decoration, inst, inst, inst);
1051}
1052
1053spv_result_t BuiltInsValidator::ValidateFragCoordAtReference(
1054 const Decoration& decoration, const Instruction& built_in_inst,
1055 const Instruction& referenced_inst,
1056 const Instruction& referenced_from_inst) {
1057 if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
1058 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
1059 if (storage_class != SpvStorageClassMax &&
1060 storage_class != SpvStorageClassInput) {
1061 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1062 << spvLogStringForEnv(_.context()->target_env)
1063 << " spec allows BuiltIn FragCoord to be only used for "
1064 "variables with Input storage class. "
1065 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1066 referenced_from_inst)
1067 << " " << GetStorageClassDesc(referenced_from_inst);
1068 }
1069
1070 for (const SpvExecutionModel execution_model : execution_models_) {
1071 if (execution_model != SpvExecutionModelFragment) {
1072 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1073 << spvLogStringForEnv(_.context()->target_env)
1074 << " spec allows BuiltIn FragCoord to be used only with "
1075 "Fragment execution model. "
1076 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1077 referenced_from_inst, execution_model);
1078 }
1079 }
1080 }
1081
1082 if (function_id_ == 0) {
1083 // Propagate this rule to all dependant ids in the global scope.
1084 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1085 &BuiltInsValidator::ValidateFragCoordAtReference, this, decoration,
1086 built_in_inst, referenced_from_inst, std::placeholders::_1));
1087 }
1088
1089 return SPV_SUCCESS;
1090}
1091
1092spv_result_t BuiltInsValidator::ValidateFragDepthAtDefinition(
1093 const Decoration& decoration, const Instruction& inst) {
1094 if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
1095 if (spv_result_t error = ValidateF32(
1096 decoration, inst,
1097 [this, &inst](const std::string& message) -> spv_result_t {
1098 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
1099 << "According to the "
1100 << spvLogStringForEnv(_.context()->target_env)
1101 << " spec BuiltIn FragDepth "
1102 "variable needs to be a 32-bit float scalar. "
1103 << message;
1104 })) {
1105 return error;
1106 }
1107 }
1108
1109 // Seed at reference checks with this built-in.
1110 return ValidateFragDepthAtReference(decoration, inst, inst, inst);
1111}
1112
1113spv_result_t BuiltInsValidator::ValidateFragDepthAtReference(
1114 const Decoration& decoration, const Instruction& built_in_inst,
1115 const Instruction& referenced_inst,
1116 const Instruction& referenced_from_inst) {
1117 if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
1118 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
1119 if (storage_class != SpvStorageClassMax &&
1120 storage_class != SpvStorageClassOutput) {
1121 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1122 << spvLogStringForEnv(_.context()->target_env)
1123 << " spec allows BuiltIn FragDepth to be only used for "
1124 "variables with Output storage class. "
1125 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1126 referenced_from_inst)
1127 << " " << GetStorageClassDesc(referenced_from_inst);
1128 }
1129
1130 for (const SpvExecutionModel execution_model : execution_models_) {
1131 if (execution_model != SpvExecutionModelFragment) {
1132 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1133 << spvLogStringForEnv(_.context()->target_env)
1134 << " spec allows BuiltIn FragDepth to be used only with "
1135 "Fragment execution model. "
1136 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1137 referenced_from_inst, execution_model);
1138 }
1139 }
1140
1141 for (const uint32_t entry_point : *entry_points_) {
1142 // Every entry point from which this function is called needs to have
1143 // Execution Mode DepthReplacing.
1144 const auto* modes = _.GetExecutionModes(entry_point);
1145 if (!modes || !modes->count(SpvExecutionModeDepthReplacing)) {
1146 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1147 << spvLogStringForEnv(_.context()->target_env)
1148 << " spec requires DepthReplacing execution mode to be "
1149 "declared when using BuiltIn FragDepth. "
1150 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1151 referenced_from_inst);
1152 }
1153 }
1154 }
1155
1156 if (function_id_ == 0) {
1157 // Propagate this rule to all dependant ids in the global scope.
1158 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1159 &BuiltInsValidator::ValidateFragDepthAtReference, this, decoration,
1160 built_in_inst, referenced_from_inst, std::placeholders::_1));
1161 }
1162
1163 return SPV_SUCCESS;
1164}
1165
1166spv_result_t BuiltInsValidator::ValidateFrontFacingAtDefinition(
1167 const Decoration& decoration, const Instruction& inst) {
1168 if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
1169 if (spv_result_t error = ValidateBool(
1170 decoration, inst,
1171 [this, &inst](const std::string& message) -> spv_result_t {
1172 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
1173 << "According to the "
1174 << spvLogStringForEnv(_.context()->target_env)
1175 << " spec BuiltIn FrontFacing "
1176 "variable needs to be a bool scalar. "
1177 << message;
1178 })) {
1179 return error;
1180 }
1181 }
1182
1183 // Seed at reference checks with this built-in.
1184 return ValidateFrontFacingAtReference(decoration, inst, inst, inst);
1185}
1186
1187spv_result_t BuiltInsValidator::ValidateFrontFacingAtReference(
1188 const Decoration& decoration, const Instruction& built_in_inst,
1189 const Instruction& referenced_inst,
1190 const Instruction& referenced_from_inst) {
1191 if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
1192 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
1193 if (storage_class != SpvStorageClassMax &&
1194 storage_class != SpvStorageClassInput) {
1195 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1196 << spvLogStringForEnv(_.context()->target_env)
1197 << " spec allows BuiltIn FrontFacing to be only used for "
1198 "variables with Input storage class. "
1199 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1200 referenced_from_inst)
1201 << " " << GetStorageClassDesc(referenced_from_inst);
1202 }
1203
1204 for (const SpvExecutionModel execution_model : execution_models_) {
1205 if (execution_model != SpvExecutionModelFragment) {
1206 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1207 << spvLogStringForEnv(_.context()->target_env)
1208 << " spec allows BuiltIn FrontFacing to be used only with "
1209 "Fragment execution model. "
1210 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1211 referenced_from_inst, execution_model);
1212 }
1213 }
1214 }
1215
1216 if (function_id_ == 0) {
1217 // Propagate this rule to all dependant ids in the global scope.
1218 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1219 &BuiltInsValidator::ValidateFrontFacingAtReference, this, decoration,
1220 built_in_inst, referenced_from_inst, std::placeholders::_1));
1221 }
1222
1223 return SPV_SUCCESS;
1224}
1225
1226spv_result_t BuiltInsValidator::ValidateHelperInvocationAtDefinition(
1227 const Decoration& decoration, const Instruction& inst) {
1228 if (spvIsVulkanEnv(_.context()->target_env)) {
1229 if (spv_result_t error = ValidateBool(
1230 decoration, inst,
1231 [this, &inst](const std::string& message) -> spv_result_t {
1232 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
1233 << "According to the Vulkan spec BuiltIn HelperInvocation "
1234 "variable needs to be a bool scalar. "
1235 << message;
1236 })) {
1237 return error;
1238 }
1239 }
1240
1241 // Seed at reference checks with this built-in.
1242 return ValidateHelperInvocationAtReference(decoration, inst, inst, inst);
1243}
1244
1245spv_result_t BuiltInsValidator::ValidateHelperInvocationAtReference(
1246 const Decoration& decoration, const Instruction& built_in_inst,
1247 const Instruction& referenced_inst,
1248 const Instruction& referenced_from_inst) {
1249 if (spvIsVulkanEnv(_.context()->target_env)) {
1250 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
1251 if (storage_class != SpvStorageClassMax &&
1252 storage_class != SpvStorageClassInput) {
1253 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1254 << "Vulkan spec allows BuiltIn HelperInvocation to be only used "
1255 "for variables with Input storage class. "
1256 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1257 referenced_from_inst)
1258 << " " << GetStorageClassDesc(referenced_from_inst);
1259 }
1260
1261 for (const SpvExecutionModel execution_model : execution_models_) {
1262 if (execution_model != SpvExecutionModelFragment) {
1263 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1264 << "Vulkan spec allows BuiltIn HelperInvocation to be used only "
1265 "with Fragment execution model. "
1266 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1267 referenced_from_inst, execution_model);
1268 }
1269 }
1270 }
1271
1272 if (function_id_ == 0) {
1273 // Propagate this rule to all dependant ids in the global scope.
1274 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
1275 std::bind(&BuiltInsValidator::ValidateHelperInvocationAtReference, this,
1276 decoration, built_in_inst, referenced_from_inst,
1277 std::placeholders::_1));
1278 }
1279
1280 return SPV_SUCCESS;
1281}
1282
1283spv_result_t BuiltInsValidator::ValidateInvocationIdAtDefinition(
1284 const Decoration& decoration, const Instruction& inst) {
1285 if (spvIsVulkanEnv(_.context()->target_env)) {
1286 if (spv_result_t error = ValidateI32(
1287 decoration, inst,
1288 [this, &inst](const std::string& message) -> spv_result_t {
1289 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
1290 << "According to the Vulkan spec BuiltIn InvocationId "
1291 "variable needs to be a 32-bit int scalar. "
1292 << message;
1293 })) {
1294 return error;
1295 }
1296 }
1297
1298 // Seed at reference checks with this built-in.
1299 return ValidateInvocationIdAtReference(decoration, inst, inst, inst);
1300}
1301
1302spv_result_t BuiltInsValidator::ValidateInvocationIdAtReference(
1303 const Decoration& decoration, const Instruction& built_in_inst,
1304 const Instruction& referenced_inst,
1305 const Instruction& referenced_from_inst) {
1306 if (spvIsVulkanEnv(_.context()->target_env)) {
1307 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
1308 if (storage_class != SpvStorageClassMax &&
1309 storage_class != SpvStorageClassInput) {
1310 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1311 << "Vulkan spec allows BuiltIn InvocationId to be only used for "
1312 "variables with Input storage class. "
1313 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1314 referenced_from_inst)
1315 << " " << GetStorageClassDesc(referenced_from_inst);
1316 }
1317
1318 for (const SpvExecutionModel execution_model : execution_models_) {
1319 if (execution_model != SpvExecutionModelTessellationControl &&
1320 execution_model != SpvExecutionModelGeometry) {
1321 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1322 << "Vulkan spec allows BuiltIn InvocationId to be used only "
1323 "with TessellationControl or Geometry execution models. "
1324 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1325 referenced_from_inst, execution_model);
1326 }
1327 }
1328 }
1329
1330 if (function_id_ == 0) {
1331 // Propagate this rule to all dependant ids in the global scope.
1332 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1333 &BuiltInsValidator::ValidateInvocationIdAtReference, this, decoration,
1334 built_in_inst, referenced_from_inst, std::placeholders::_1));
1335 }
1336
1337 return SPV_SUCCESS;
1338}
1339
1340spv_result_t BuiltInsValidator::ValidateInstanceIndexAtDefinition(
1341 const Decoration& decoration, const Instruction& inst) {
1342 if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
1343 if (spv_result_t error = ValidateI32(
1344 decoration, inst,
1345 [this, &inst](const std::string& message) -> spv_result_t {
1346 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
1347 << "According to the "
1348 << spvLogStringForEnv(_.context()->target_env)
1349 << " spec BuiltIn InstanceIndex "
1350 "variable needs to be a 32-bit int scalar. "
1351 << message;
1352 })) {
1353 return error;
1354 }
1355 }
1356
1357 // Seed at reference checks with this built-in.
1358 return ValidateInstanceIndexAtReference(decoration, inst, inst, inst);
1359}
1360
1361spv_result_t BuiltInsValidator::ValidateInstanceIndexAtReference(
1362 const Decoration& decoration, const Instruction& built_in_inst,
1363 const Instruction& referenced_inst,
1364 const Instruction& referenced_from_inst) {
1365 if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
1366 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
1367 if (storage_class != SpvStorageClassMax &&
1368 storage_class != SpvStorageClassInput) {
1369 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1370 << spvLogStringForEnv(_.context()->target_env)
1371 << " spec allows BuiltIn InstanceIndex to be only used for "
1372 "variables with Input storage class. "
1373 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1374 referenced_from_inst)
1375 << " " << GetStorageClassDesc(referenced_from_inst);
1376 }
1377
1378 for (const SpvExecutionModel execution_model : execution_models_) {
1379 if (execution_model != SpvExecutionModelVertex) {
1380 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1381 << spvLogStringForEnv(_.context()->target_env)
1382 << " spec allows BuiltIn InstanceIndex to be used only "
1383 "with Vertex execution model. "
1384 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1385 referenced_from_inst, execution_model);
1386 }
1387 }
1388 }
1389
1390 if (function_id_ == 0) {
1391 // Propagate this rule to all dependant ids in the global scope.
1392 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1393 &BuiltInsValidator::ValidateInstanceIndexAtReference, this, decoration,
1394 built_in_inst, referenced_from_inst, std::placeholders::_1));
1395 }
1396
1397 return SPV_SUCCESS;
1398}
1399
1400spv_result_t BuiltInsValidator::ValidatePatchVerticesAtDefinition(
1401 const Decoration& decoration, const Instruction& inst) {
1402 if (spvIsVulkanEnv(_.context()->target_env)) {
1403 if (spv_result_t error = ValidateI32(
1404 decoration, inst,
1405 [this, &inst](const std::string& message) -> spv_result_t {
1406 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
1407 << "According to the Vulkan spec BuiltIn PatchVertices "
1408 "variable needs to be a 32-bit int scalar. "
1409 << message;
1410 })) {
1411 return error;
1412 }
1413 }
1414
1415 // Seed at reference checks with this built-in.
1416 return ValidatePatchVerticesAtReference(decoration, inst, inst, inst);
1417}
1418
1419spv_result_t BuiltInsValidator::ValidatePatchVerticesAtReference(
1420 const Decoration& decoration, const Instruction& built_in_inst,
1421 const Instruction& referenced_inst,
1422 const Instruction& referenced_from_inst) {
1423 if (spvIsVulkanEnv(_.context()->target_env)) {
1424 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
1425 if (storage_class != SpvStorageClassMax &&
1426 storage_class != SpvStorageClassInput) {
1427 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1428 << "Vulkan spec allows BuiltIn PatchVertices to be only used for "
1429 "variables with Input storage class. "
1430 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1431 referenced_from_inst)
1432 << " " << GetStorageClassDesc(referenced_from_inst);
1433 }
1434
1435 for (const SpvExecutionModel execution_model : execution_models_) {
1436 if (execution_model != SpvExecutionModelTessellationControl &&
1437 execution_model != SpvExecutionModelTessellationEvaluation) {
1438 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1439 << "Vulkan spec allows BuiltIn PatchVertices to be used only "
1440 "with TessellationControl or TessellationEvaluation "
1441 "execution models. "
1442 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1443 referenced_from_inst, execution_model);
1444 }
1445 }
1446 }
1447
1448 if (function_id_ == 0) {
1449 // Propagate this rule to all dependant ids in the global scope.
1450 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1451 &BuiltInsValidator::ValidatePatchVerticesAtReference, this, decoration,
1452 built_in_inst, referenced_from_inst, std::placeholders::_1));
1453 }
1454
1455 return SPV_SUCCESS;
1456}
1457
1458spv_result_t BuiltInsValidator::ValidatePointCoordAtDefinition(
1459 const Decoration& decoration, const Instruction& inst) {
1460 if (spvIsVulkanEnv(_.context()->target_env)) {
1461 if (spv_result_t error = ValidateF32Vec(
1462 decoration, inst, 2,
1463 [this, &inst](const std::string& message) -> spv_result_t {
1464 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
1465 << "According to the Vulkan spec BuiltIn PointCoord "
1466 "variable needs to be a 2-component 32-bit float "
1467 "vector. "
1468 << message;
1469 })) {
1470 return error;
1471 }
1472 }
1473
1474 // Seed at reference checks with this built-in.
1475 return ValidatePointCoordAtReference(decoration, inst, inst, inst);
1476}
1477
1478spv_result_t BuiltInsValidator::ValidatePointCoordAtReference(
1479 const Decoration& decoration, const Instruction& built_in_inst,
1480 const Instruction& referenced_inst,
1481 const Instruction& referenced_from_inst) {
1482 if (spvIsVulkanEnv(_.context()->target_env)) {
1483 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
1484 if (storage_class != SpvStorageClassMax &&
1485 storage_class != SpvStorageClassInput) {
1486 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1487 << "Vulkan spec allows BuiltIn PointCoord to be only used for "
1488 "variables with Input storage class. "
1489 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1490 referenced_from_inst)
1491 << " " << GetStorageClassDesc(referenced_from_inst);
1492 }
1493
1494 for (const SpvExecutionModel execution_model : execution_models_) {
1495 if (execution_model != SpvExecutionModelFragment) {
1496 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1497 << "Vulkan spec allows BuiltIn PointCoord to be used only with "
1498 "Fragment execution model. "
1499 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1500 referenced_from_inst, execution_model);
1501 }
1502 }
1503 }
1504
1505 if (function_id_ == 0) {
1506 // Propagate this rule to all dependant ids in the global scope.
1507 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1508 &BuiltInsValidator::ValidatePointCoordAtReference, this, decoration,
1509 built_in_inst, referenced_from_inst, std::placeholders::_1));
1510 }
1511
1512 return SPV_SUCCESS;
1513}
1514
1515spv_result_t BuiltInsValidator::ValidatePointSizeAtDefinition(
1516 const Decoration& decoration, const Instruction& inst) {
1517 // Seed at reference checks with this built-in.
1518 return ValidatePointSizeAtReference(decoration, inst, inst, inst);
1519}
1520
1521spv_result_t BuiltInsValidator::ValidatePointSizeAtReference(
1522 const Decoration& decoration, const Instruction& built_in_inst,
1523 const Instruction& referenced_inst,
1524 const Instruction& referenced_from_inst) {
1525 if (spvIsVulkanEnv(_.context()->target_env)) {
1526 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
1527 if (storage_class != SpvStorageClassMax &&
1528 storage_class != SpvStorageClassInput &&
1529 storage_class != SpvStorageClassOutput) {
1530 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1531 << "Vulkan spec allows BuiltIn PointSize to be only used for "
1532 "variables with Input or Output storage class. "
1533 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1534 referenced_from_inst)
1535 << " " << GetStorageClassDesc(referenced_from_inst);
1536 }
1537
1538 if (storage_class == SpvStorageClassInput) {
1539 assert(function_id_ == 0);
1540 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1541 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this,
1542 "Vulkan spec doesn't allow BuiltIn PointSize to be used for "
1543 "variables with Input storage class if execution model is Vertex.",
1544 SpvExecutionModelVertex, decoration, built_in_inst,
1545 referenced_from_inst, std::placeholders::_1));
1546 }
1547
1548 for (const SpvExecutionModel execution_model : execution_models_) {
1549 switch (execution_model) {
1550 case SpvExecutionModelVertex: {
1551 if (spv_result_t error = ValidateF32(
1552 decoration, built_in_inst,
1553 [this, &referenced_from_inst](
1554 const std::string& message) -> spv_result_t {
1555 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1556 << "According to the Vulkan spec BuiltIn PointSize "
1557 "variable needs to be a 32-bit float scalar. "
1558 << message;
1559 })) {
1560 return error;
1561 }
1562 break;
1563 }
1564 case SpvExecutionModelTessellationControl:
1565 case SpvExecutionModelTessellationEvaluation:
1566 case SpvExecutionModelGeometry:
1567 case SpvExecutionModelMeshNV: {
1568 // PointSize can be a per-vertex variable for tessellation control,
1569 // tessellation evaluation and geometry shader stages. In such cases
1570 // variables will have an array of 32-bit floats.
1571 if (decoration.struct_member_index() != Decoration::kInvalidMember) {
1572 // The array is on the variable, so this must be a 32-bit float.
1573 if (spv_result_t error = ValidateF32(
1574 decoration, built_in_inst,
1575 [this, &referenced_from_inst](
1576 const std::string& message) -> spv_result_t {
1577 return _.diag(SPV_ERROR_INVALID_DATA,
1578 &referenced_from_inst)
1579 << "According to the Vulkan spec BuiltIn "
1580 "PointSize variable needs to be a 32-bit "
1581 "float scalar. "
1582 << message;
1583 })) {
1584 return error;
1585 }
1586 } else {
1587 if (spv_result_t error = ValidateOptionalArrayedF32(
1588 decoration, built_in_inst,
1589 [this, &referenced_from_inst](
1590 const std::string& message) -> spv_result_t {
1591 return _.diag(SPV_ERROR_INVALID_DATA,
1592 &referenced_from_inst)
1593 << "According to the Vulkan spec BuiltIn "
1594 "PointSize variable needs to be a 32-bit "
1595 "float scalar. "
1596 << message;
1597 })) {
1598 return error;
1599 }
1600 }
1601 break;
1602 }
1603
1604 default: {
1605 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1606 << "Vulkan spec allows BuiltIn PointSize to be used only with "
1607 "Vertex, TessellationControl, TessellationEvaluation or "
1608 "Geometry execution models. "
1609 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1610 referenced_from_inst, execution_model);
1611 }
1612 }
1613 }
1614 }
1615
1616 if (function_id_ == 0) {
1617 // Propagate this rule to all dependant ids in the global scope.
1618 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1619 &BuiltInsValidator::ValidatePointSizeAtReference, this, decoration,
1620 built_in_inst, referenced_from_inst, std::placeholders::_1));
1621 }
1622
1623 return SPV_SUCCESS;
1624}
1625
1626spv_result_t BuiltInsValidator::ValidatePositionAtDefinition(
1627 const Decoration& decoration, const Instruction& inst) {
1628 // Seed at reference checks with this built-in.
1629 return ValidatePositionAtReference(decoration, inst, inst, inst);
1630}
1631
1632spv_result_t BuiltInsValidator::ValidatePositionAtReference(
1633 const Decoration& decoration, const Instruction& built_in_inst,
1634 const Instruction& referenced_inst,
1635 const Instruction& referenced_from_inst) {
1636 if (spvIsVulkanEnv(_.context()->target_env)) {
1637 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
1638 if (storage_class != SpvStorageClassMax &&
1639 storage_class != SpvStorageClassInput &&
1640 storage_class != SpvStorageClassOutput) {
1641 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1642 << "Vulkan spec allows BuiltIn Position to be only used for "
1643 "variables with Input or Output storage class. "
1644 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1645 referenced_from_inst)
1646 << " " << GetStorageClassDesc(referenced_from_inst);
1647 }
1648
1649 if (storage_class == SpvStorageClassInput) {
1650 assert(function_id_ == 0);
1651 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1652 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this,
1653 "Vulkan spec doesn't allow BuiltIn Position to be used for variables "
1654 "with Input storage class if execution model is Vertex.",
1655 SpvExecutionModelVertex, decoration, built_in_inst,
1656 referenced_from_inst, std::placeholders::_1));
1657 }
1658
1659 for (const SpvExecutionModel execution_model : execution_models_) {
1660 switch (execution_model) {
1661 case SpvExecutionModelVertex: {
1662 if (spv_result_t error = ValidateF32Vec(
1663 decoration, built_in_inst, 4,
1664 [this, &referenced_from_inst](
1665 const std::string& message) -> spv_result_t {
1666 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1667 << "According to the Vulkan spec BuiltIn Position "
1668 "variable needs to be a 4-component 32-bit float "
1669 "vector. "
1670 << message;
1671 })) {
1672 return error;
1673 }
1674 break;
1675 }
1676 case SpvExecutionModelGeometry:
1677 case SpvExecutionModelTessellationControl:
1678 case SpvExecutionModelTessellationEvaluation:
1679 case SpvExecutionModelMeshNV: {
1680 // Position can be a per-vertex variable for tessellation control,
1681 // tessellation evaluation, geometry and mesh shader stages. In such
1682 // cases variables will have an array of 4-component 32-bit float
1683 // vectors.
1684 if (decoration.struct_member_index() != Decoration::kInvalidMember) {
1685 // The array is on the variable, so this must be a 4-component
1686 // 32-bit float vector.
1687 if (spv_result_t error = ValidateF32Vec(
1688 decoration, built_in_inst, 4,
1689 [this, &referenced_from_inst](
1690 const std::string& message) -> spv_result_t {
1691 return _.diag(SPV_ERROR_INVALID_DATA,
1692 &referenced_from_inst)
1693 << "According to the Vulkan spec BuiltIn Position "
1694 "variable needs to be a 4-component 32-bit "
1695 "float vector. "
1696 << message;
1697 })) {
1698 return error;
1699 }
1700 } else {
1701 if (spv_result_t error = ValidateOptionalArrayedF32Vec(
1702 decoration, built_in_inst, 4,
1703 [this, &referenced_from_inst](
1704 const std::string& message) -> spv_result_t {
1705 return _.diag(SPV_ERROR_INVALID_DATA,
1706 &referenced_from_inst)
1707 << "According to the Vulkan spec BuiltIn Position "
1708 "variable needs to be a 4-component 32-bit "
1709 "float vector. "
1710 << message;
1711 })) {
1712 return error;
1713 }
1714 }
1715 break;
1716 }
1717
1718 default: {
1719 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1720 << "Vulkan spec allows BuiltIn Position to be used only "
1721 "with Vertex, TessellationControl, TessellationEvaluation"
1722 " or Geometry execution models. "
1723 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1724 referenced_from_inst, execution_model);
1725 }
1726 }
1727 }
1728 }
1729
1730 if (spvIsWebGPUEnv(_.context()->target_env)) {
1731 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
1732 if (storage_class != SpvStorageClassMax &&
1733 storage_class != SpvStorageClassOutput) {
1734 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1735 << "WebGPU spec allows BuiltIn Position to be only used for "
1736 "variables with Output storage class. "
1737 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1738 referenced_from_inst)
1739 << " " << GetStorageClassDesc(referenced_from_inst);
1740 }
1741
1742 for (const SpvExecutionModel execution_model : execution_models_) {
1743 switch (execution_model) {
1744 case SpvExecutionModelVertex: {
1745 if (spv_result_t error = ValidateF32Vec(
1746 decoration, built_in_inst, 4,
1747 [this, &referenced_from_inst](
1748 const std::string& message) -> spv_result_t {
1749 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1750 << "According to the WebGPU spec BuiltIn Position "
1751 "variable needs to be a 4-component 32-bit float "
1752 "vector. "
1753 << message;
1754 })) {
1755 return error;
1756 }
1757 break;
1758 }
1759 default: {
1760 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1761 << "WebGPU spec allows BuiltIn Position to be used only "
1762 "with the Vertex execution model. "
1763 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1764 referenced_from_inst, execution_model);
1765 }
1766 }
1767 }
1768 }
1769
1770 if (function_id_ == 0) {
1771 // Propagate this rule to all dependant ids in the global scope.
1772 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1773 &BuiltInsValidator::ValidatePositionAtReference, this, decoration,
1774 built_in_inst, referenced_from_inst, std::placeholders::_1));
1775 }
1776
1777 return SPV_SUCCESS;
1778}
1779
1780spv_result_t BuiltInsValidator::ValidatePrimitiveIdAtDefinition(
1781 const Decoration& decoration, const Instruction& inst) {
1782 if (spvIsVulkanEnv(_.context()->target_env)) {
1783 // PrimitiveId can be a per-primitive variable for mesh shader stage.
1784 // In such cases variable will have an array of 32-bit integers.
1785 if (decoration.struct_member_index() != Decoration::kInvalidMember) {
1786 // This must be a 32-bit int scalar.
1787 if (spv_result_t error = ValidateI32(
1788 decoration, inst,
1789 [this, &inst](const std::string& message) -> spv_result_t {
1790 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
1791 << "According to the Vulkan spec BuiltIn PrimitiveId "
1792 "variable needs to be a 32-bit int scalar. "
1793 << message;
1794 })) {
1795 return error;
1796 }
1797 } else {
1798 if (spv_result_t error = ValidateOptionalArrayedI32(
1799 decoration, inst,
1800 [this, &inst](const std::string& message) -> spv_result_t {
1801 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
1802 << "According to the Vulkan spec BuiltIn PrimitiveId "
1803 "variable needs to be a 32-bit int scalar. "
1804 << message;
1805 })) {
1806 return error;
1807 }
1808 }
1809 }
1810
1811 // Seed at reference checks with this built-in.
1812 return ValidatePrimitiveIdAtReference(decoration, inst, inst, inst);
1813}
1814
1815spv_result_t BuiltInsValidator::ValidatePrimitiveIdAtReference(
1816 const Decoration& decoration, const Instruction& built_in_inst,
1817 const Instruction& referenced_inst,
1818 const Instruction& referenced_from_inst) {
1819 if (spvIsVulkanEnv(_.context()->target_env)) {
1820 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
1821 if (storage_class != SpvStorageClassMax &&
1822 storage_class != SpvStorageClassInput &&
1823 storage_class != SpvStorageClassOutput) {
1824 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1825 << "Vulkan spec allows BuiltIn PrimitiveId to be only used for "
1826 "variables with Input or Output storage class. "
1827 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1828 referenced_from_inst)
1829 << " " << GetStorageClassDesc(referenced_from_inst);
1830 }
1831
1832 if (storage_class == SpvStorageClassOutput) {
1833 assert(function_id_ == 0);
1834 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1835 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this,
1836 "Vulkan spec doesn't allow BuiltIn PrimitiveId to be used for "
1837 "variables with Output storage class if execution model is "
1838 "TessellationControl.",
1839 SpvExecutionModelTessellationControl, decoration, built_in_inst,
1840 referenced_from_inst, std::placeholders::_1));
1841 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1842 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this,
1843 "Vulkan spec doesn't allow BuiltIn PrimitiveId to be used for "
1844 "variables with Output storage class if execution model is "
1845 "TessellationEvaluation.",
1846 SpvExecutionModelTessellationEvaluation, decoration, built_in_inst,
1847 referenced_from_inst, std::placeholders::_1));
1848 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1849 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this,
1850 "Vulkan spec doesn't allow BuiltIn PrimitiveId to be used for "
1851 "variables with Output storage class if execution model is "
1852 "Fragment.",
1853 SpvExecutionModelFragment, decoration, built_in_inst,
1854 referenced_from_inst, std::placeholders::_1));
1855 }
1856
1857 for (const SpvExecutionModel execution_model : execution_models_) {
1858 switch (execution_model) {
1859 case SpvExecutionModelFragment:
1860 case SpvExecutionModelTessellationControl:
1861 case SpvExecutionModelTessellationEvaluation:
1862 case SpvExecutionModelGeometry:
1863 case SpvExecutionModelMeshNV:
1864 case SpvExecutionModelRayGenerationNV:
1865 case SpvExecutionModelIntersectionNV:
1866 case SpvExecutionModelAnyHitNV:
1867 case SpvExecutionModelClosestHitNV:
1868 case SpvExecutionModelMissNV:
1869 case SpvExecutionModelCallableNV: {
1870 // Ok.
1871 break;
1872 }
1873
1874 default: {
1875 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1876 << "Vulkan spec allows BuiltIn PrimitiveId to be used only "
1877 "with Fragment, TessellationControl, "
1878 "TessellationEvaluation or Geometry execution models. "
1879 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1880 referenced_from_inst, execution_model);
1881 }
1882 }
1883 }
1884 }
1885
1886 if (function_id_ == 0) {
1887 // Propagate this rule to all dependant ids in the global scope.
1888 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1889 &BuiltInsValidator::ValidatePrimitiveIdAtReference, this, decoration,
1890 built_in_inst, referenced_from_inst, std::placeholders::_1));
1891 }
1892
1893 return SPV_SUCCESS;
1894}
1895
1896spv_result_t BuiltInsValidator::ValidateSampleIdAtDefinition(
1897 const Decoration& decoration, const Instruction& inst) {
1898 if (spvIsVulkanEnv(_.context()->target_env)) {
1899 if (spv_result_t error = ValidateI32(
1900 decoration, inst,
1901 [this, &inst](const std::string& message) -> spv_result_t {
1902 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
1903 << "According to the Vulkan spec BuiltIn SampleId "
1904 "variable needs to be a 32-bit int scalar. "
1905 << message;
1906 })) {
1907 return error;
1908 }
1909 }
1910
1911 // Seed at reference checks with this built-in.
1912 return ValidateSampleIdAtReference(decoration, inst, inst, inst);
1913}
1914
1915spv_result_t BuiltInsValidator::ValidateSampleIdAtReference(
1916 const Decoration& decoration, const Instruction& built_in_inst,
1917 const Instruction& referenced_inst,
1918 const Instruction& referenced_from_inst) {
1919 if (spvIsVulkanEnv(_.context()->target_env)) {
1920 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
1921 if (storage_class != SpvStorageClassMax &&
1922 storage_class != SpvStorageClassInput) {
1923 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1924 << "Vulkan spec allows BuiltIn SampleId to be only used for "
1925 "variables with Input storage class. "
1926 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1927 referenced_from_inst)
1928 << " " << GetStorageClassDesc(referenced_from_inst);
1929 }
1930
1931 for (const SpvExecutionModel execution_model : execution_models_) {
1932 if (execution_model != SpvExecutionModelFragment) {
1933 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1934 << "Vulkan spec allows BuiltIn SampleId to be used only with "
1935 "Fragment execution model. "
1936 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1937 referenced_from_inst, execution_model);
1938 }
1939 }
1940 }
1941
1942 if (function_id_ == 0) {
1943 // Propagate this rule to all dependant ids in the global scope.
1944 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1945 &BuiltInsValidator::ValidateSampleIdAtReference, this, decoration,
1946 built_in_inst, referenced_from_inst, std::placeholders::_1));
1947 }
1948
1949 return SPV_SUCCESS;
1950}
1951
1952spv_result_t BuiltInsValidator::ValidateSampleMaskAtDefinition(
1953 const Decoration& decoration, const Instruction& inst) {
1954 if (spvIsVulkanEnv(_.context()->target_env)) {
1955 if (spv_result_t error = ValidateI32Arr(
1956 decoration, inst,
1957 [this, &inst](const std::string& message) -> spv_result_t {
1958 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
1959 << "According to the Vulkan spec BuiltIn SampleMask "
1960 "variable needs to be a 32-bit int array. "
1961 << message;
1962 })) {
1963 return error;
1964 }
1965 }
1966
1967 // Seed at reference checks with this built-in.
1968 return ValidateSampleMaskAtReference(decoration, inst, inst, inst);
1969}
1970
1971spv_result_t BuiltInsValidator::ValidateSampleMaskAtReference(
1972 const Decoration& decoration, const Instruction& built_in_inst,
1973 const Instruction& referenced_inst,
1974 const Instruction& referenced_from_inst) {
1975 if (spvIsVulkanEnv(_.context()->target_env)) {
1976 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
1977 if (storage_class != SpvStorageClassMax &&
1978 storage_class != SpvStorageClassInput &&
1979 storage_class != SpvStorageClassOutput) {
1980 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1981 << "Vulkan spec allows BuiltIn SampleMask to be only used for "
1982 "variables with Input or Output storage class. "
1983 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1984 referenced_from_inst)
1985 << " " << GetStorageClassDesc(referenced_from_inst);
1986 }
1987
1988 for (const SpvExecutionModel execution_model : execution_models_) {
1989 if (execution_model != SpvExecutionModelFragment) {
1990 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1991 << "Vulkan spec allows BuiltIn SampleMask to be used only "
1992 "with "
1993 "Fragment execution model. "
1994 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1995 referenced_from_inst, execution_model);
1996 }
1997 }
1998 }
1999
2000 if (function_id_ == 0) {
2001 // Propagate this rule to all dependant ids in the global scope.
2002 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2003 &BuiltInsValidator::ValidateSampleMaskAtReference, this, decoration,
2004 built_in_inst, referenced_from_inst, std::placeholders::_1));
2005 }
2006
2007 return SPV_SUCCESS;
2008}
2009
2010spv_result_t BuiltInsValidator::ValidateSamplePositionAtDefinition(
2011 const Decoration& decoration, const Instruction& inst) {
2012 if (spvIsVulkanEnv(_.context()->target_env)) {
2013 if (spv_result_t error = ValidateF32Vec(
2014 decoration, inst, 2,
2015 [this, &inst](const std::string& message) -> spv_result_t {
2016 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2017 << "According to the Vulkan spec BuiltIn SamplePosition "
2018 "variable needs to be a 2-component 32-bit float "
2019 "vector. "
2020 << message;
2021 })) {
2022 return error;
2023 }
2024 }
2025
2026 // Seed at reference checks with this built-in.
2027 return ValidateSamplePositionAtReference(decoration, inst, inst, inst);
2028}
2029
2030spv_result_t BuiltInsValidator::ValidateSamplePositionAtReference(
2031 const Decoration& decoration, const Instruction& built_in_inst,
2032 const Instruction& referenced_inst,
2033 const Instruction& referenced_from_inst) {
2034 if (spvIsVulkanEnv(_.context()->target_env)) {
2035 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
2036 if (storage_class != SpvStorageClassMax &&
2037 storage_class != SpvStorageClassInput) {
2038 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2039 << "Vulkan spec allows BuiltIn SamplePosition to be only used "
2040 "for "
2041 "variables with Input storage class. "
2042 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2043 referenced_from_inst)
2044 << " " << GetStorageClassDesc(referenced_from_inst);
2045 }
2046
2047 for (const SpvExecutionModel execution_model : execution_models_) {
2048 if (execution_model != SpvExecutionModelFragment) {
2049 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2050 << "Vulkan spec allows BuiltIn SamplePosition to be used only "
2051 "with "
2052 "Fragment execution model. "
2053 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2054 referenced_from_inst, execution_model);
2055 }
2056 }
2057 }
2058
2059 if (function_id_ == 0) {
2060 // Propagate this rule to all dependant ids in the global scope.
2061 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2062 &BuiltInsValidator::ValidateSamplePositionAtReference, this, decoration,
2063 built_in_inst, referenced_from_inst, std::placeholders::_1));
2064 }
2065
2066 return SPV_SUCCESS;
2067}
2068
2069spv_result_t BuiltInsValidator::ValidateTessCoordAtDefinition(
2070 const Decoration& decoration, const Instruction& inst) {
2071 if (spvIsVulkanEnv(_.context()->target_env)) {
2072 if (spv_result_t error = ValidateF32Vec(
2073 decoration, inst, 3,
2074 [this, &inst](const std::string& message) -> spv_result_t {
2075 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2076 << "According to the Vulkan spec BuiltIn TessCoord "
2077 "variable needs to be a 3-component 32-bit float "
2078 "vector. "
2079 << message;
2080 })) {
2081 return error;
2082 }
2083 }
2084
2085 // Seed at reference checks with this built-in.
2086 return ValidateTessCoordAtReference(decoration, inst, inst, inst);
2087}
2088
2089spv_result_t BuiltInsValidator::ValidateTessCoordAtReference(
2090 const Decoration& decoration, const Instruction& built_in_inst,
2091 const Instruction& referenced_inst,
2092 const Instruction& referenced_from_inst) {
2093 if (spvIsVulkanEnv(_.context()->target_env)) {
2094 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
2095 if (storage_class != SpvStorageClassMax &&
2096 storage_class != SpvStorageClassInput) {
2097 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2098 << "Vulkan spec allows BuiltIn TessCoord to be only used for "
2099 "variables with Input storage class. "
2100 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2101 referenced_from_inst)
2102 << " " << GetStorageClassDesc(referenced_from_inst);
2103 }
2104
2105 for (const SpvExecutionModel execution_model : execution_models_) {
2106 if (execution_model != SpvExecutionModelTessellationEvaluation) {
2107 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2108 << "Vulkan spec allows BuiltIn TessCoord to be used only with "
2109 "TessellationEvaluation execution model. "
2110 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2111 referenced_from_inst, execution_model);
2112 }
2113 }
2114 }
2115
2116 if (function_id_ == 0) {
2117 // Propagate this rule to all dependant ids in the global scope.
2118 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2119 &BuiltInsValidator::ValidateTessCoordAtReference, this, decoration,
2120 built_in_inst, referenced_from_inst, std::placeholders::_1));
2121 }
2122
2123 return SPV_SUCCESS;
2124}
2125
2126spv_result_t BuiltInsValidator::ValidateTessLevelOuterAtDefinition(
2127 const Decoration& decoration, const Instruction& inst) {
2128 if (spvIsVulkanEnv(_.context()->target_env)) {
2129 if (spv_result_t error = ValidateF32Arr(
2130 decoration, inst, 4,
2131 [this, &inst](const std::string& message) -> spv_result_t {
2132 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2133 << "According to the Vulkan spec BuiltIn TessLevelOuter "
2134 "variable needs to be a 4-component 32-bit float "
2135 "array. "
2136 << message;
2137 })) {
2138 return error;
2139 }
2140 }
2141
2142 // Seed at reference checks with this built-in.
2143 return ValidateTessLevelAtReference(decoration, inst, inst, inst);
2144}
2145
2146spv_result_t BuiltInsValidator::ValidateTessLevelInnerAtDefinition(
2147 const Decoration& decoration, const Instruction& inst) {
2148 if (spvIsVulkanEnv(_.context()->target_env)) {
2149 if (spv_result_t error = ValidateF32Arr(
2150 decoration, inst, 2,
2151 [this, &inst](const std::string& message) -> spv_result_t {
2152 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2153 << "According to the Vulkan spec BuiltIn TessLevelOuter "
2154 "variable needs to be a 2-component 32-bit float "
2155 "array. "
2156 << message;
2157 })) {
2158 return error;
2159 }
2160 }
2161
2162 // Seed at reference checks with this built-in.
2163 return ValidateTessLevelAtReference(decoration, inst, inst, inst);
2164}
2165
2166spv_result_t BuiltInsValidator::ValidateTessLevelAtReference(
2167 const Decoration& decoration, const Instruction& built_in_inst,
2168 const Instruction& referenced_inst,
2169 const Instruction& referenced_from_inst) {
2170 if (spvIsVulkanEnv(_.context()->target_env)) {
2171 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
2172 if (storage_class != SpvStorageClassMax &&
2173 storage_class != SpvStorageClassInput &&
2174 storage_class != SpvStorageClassOutput) {
2175 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2176 << "Vulkan spec allows BuiltIn "
2177 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2178 decoration.params()[0])
2179 << " to be only used for variables with Input or Output storage "
2180 "class. "
2181 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2182 referenced_from_inst)
2183 << " " << GetStorageClassDesc(referenced_from_inst);
2184 }
2185
2186 if (storage_class == SpvStorageClassInput) {
2187 assert(function_id_ == 0);
2188 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2189 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this,
2190 "Vulkan spec doesn't allow TessLevelOuter/TessLevelInner to be "
2191 "used "
2192 "for variables with Input storage class if execution model is "
2193 "TessellationControl.",
2194 SpvExecutionModelTessellationControl, decoration, built_in_inst,
2195 referenced_from_inst, std::placeholders::_1));
2196 }
2197
2198 if (storage_class == SpvStorageClassOutput) {
2199 assert(function_id_ == 0);
2200 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2201 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this,
2202 "Vulkan spec doesn't allow TessLevelOuter/TessLevelInner to be "
2203 "used "
2204 "for variables with Output storage class if execution model is "
2205 "TessellationEvaluation.",
2206 SpvExecutionModelTessellationEvaluation, decoration, built_in_inst,
2207 referenced_from_inst, std::placeholders::_1));
2208 }
2209
2210 for (const SpvExecutionModel execution_model : execution_models_) {
2211 switch (execution_model) {
2212 case SpvExecutionModelTessellationControl:
2213 case SpvExecutionModelTessellationEvaluation: {
2214 // Ok.
2215 break;
2216 }
2217
2218 default: {
2219 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2220 << "Vulkan spec allows BuiltIn "
2221 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2222 decoration.params()[0])
2223 << " to be used only with TessellationControl or "
2224 "TessellationEvaluation execution models. "
2225 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2226 referenced_from_inst, execution_model);
2227 }
2228 }
2229 }
2230 }
2231
2232 if (function_id_ == 0) {
2233 // Propagate this rule to all dependant ids in the global scope.
2234 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2235 &BuiltInsValidator::ValidateTessLevelAtReference, this, decoration,
2236 built_in_inst, referenced_from_inst, std::placeholders::_1));
2237 }
2238
2239 return SPV_SUCCESS;
2240}
2241
2242spv_result_t BuiltInsValidator::ValidateVertexIndexAtDefinition(
2243 const Decoration& decoration, const Instruction& inst) {
2244 if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
2245 if (spv_result_t error = ValidateI32(
2246 decoration, inst,
2247 [this, &inst](const std::string& message) -> spv_result_t {
2248 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2249 << "According to the "
2250 << spvLogStringForEnv(_.context()->target_env)
2251 << " spec BuiltIn VertexIndex variable needs to be a "
2252 "32-bit int scalar. "
2253 << message;
2254 })) {
2255 return error;
2256 }
2257 }
2258
2259 // Seed at reference checks with this built-in.
2260 return ValidateVertexIndexAtReference(decoration, inst, inst, inst);
2261}
2262
2263spv_result_t BuiltInsValidator::ValidateVertexIdOrInstanceIdAtDefinition(
2264 const Decoration& decoration, const Instruction& inst) {
2265 const SpvBuiltIn label = SpvBuiltIn(decoration.params()[0]);
2266 bool allow_instance_id =
2267 (_.HasCapability(SpvCapabilityRayTracingNV) ||
2268 _.HasCapability(SpvCapabilityRayTracingProvisionalKHR)) &&
2269 label == SpvBuiltInInstanceId;
2270
2271 if (spvIsVulkanEnv(_.context()->target_env) && !allow_instance_id) {
2272 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2273 << "Vulkan spec doesn't allow BuiltIn VertexId/InstanceId "
2274 "to be used.";
2275 }
2276
2277 if (label == SpvBuiltInInstanceId) {
2278 return ValidateInstanceIdAtReference(decoration, inst, inst, inst);
2279 }
2280 return SPV_SUCCESS;
2281}
2282
2283spv_result_t BuiltInsValidator::ValidateInstanceIdAtReference(
2284 const Decoration& decoration, const Instruction& built_in_inst,
2285 const Instruction& referenced_inst,
2286 const Instruction& referenced_from_inst) {
2287 if (spvIsVulkanEnv(_.context()->target_env)) {
2288 for (const SpvExecutionModel execution_model : execution_models_) {
2289 switch (execution_model) {
2290 case SpvExecutionModelIntersectionNV:
2291 case SpvExecutionModelClosestHitNV:
2292 case SpvExecutionModelAnyHitNV:
2293 // Do nothing, valid stages
2294 break;
2295 default:
2296 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2297 << "Vulkan spec allows BuiltIn InstanceId to be used "
2298 "only with IntersectionNV, ClosestHitNV and AnyHitNV "
2299 "execution models. "
2300 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2301 referenced_from_inst);
2302 break;
2303 }
2304 }
2305 }
2306
2307 if (function_id_ == 0) {
2308 // Propagate this rule to all dependant ids in the global scope.
2309 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2310 &BuiltInsValidator::ValidateInstanceIdAtReference, this, decoration,
2311 built_in_inst, referenced_from_inst, std::placeholders::_1));
2312 }
2313
2314 return SPV_SUCCESS;
2315}
2316
2317spv_result_t BuiltInsValidator::ValidateLocalInvocationIndexAtDefinition(
2318 const Decoration& decoration, const Instruction& inst) {
2319 if (spvIsWebGPUEnv(_.context()->target_env)) {
2320 if (spv_result_t error = ValidateI32(
2321 decoration, inst,
2322 [this, &inst](const std::string& message) -> spv_result_t {
2323 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2324 << "According to the WebGPU spec BuiltIn "
2325 "LocalInvocationIndex variable needs to be a 32-bit "
2326 "int."
2327 << message;
2328 })) {
2329 return error;
2330 }
2331 }
2332
2333 // Seed at reference checks with this built-in.
2334 return ValidateLocalInvocationIndexAtReference(decoration, inst, inst, inst);
2335}
2336
2337spv_result_t BuiltInsValidator::ValidateLocalInvocationIndexAtReference(
2338 const Decoration& decoration, const Instruction& built_in_inst,
2339 const Instruction& referenced_inst,
2340 const Instruction& referenced_from_inst) {
2341 if (spvIsWebGPUEnv(_.context()->target_env)) {
2342 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
2343 if (storage_class != SpvStorageClassMax &&
2344 storage_class != SpvStorageClassInput) {
2345 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2346 << "WebGPU spec allows BuiltIn LocalInvocationIndex to be only "
2347 "used for variables with Input storage class. "
2348 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2349 referenced_from_inst)
2350 << " " << GetStorageClassDesc(referenced_from_inst);
2351 }
2352
2353 for (const SpvExecutionModel execution_model : execution_models_) {
2354 if (execution_model != SpvExecutionModelGLCompute) {
2355 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2356 << "WebGPU spec allows BuiltIn VertexIndex to be used only "
2357 "with GLCompute execution model. "
2358 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2359 referenced_from_inst, execution_model);
2360 }
2361 }
2362 }
2363
2364 if (function_id_ == 0) {
2365 // Propagate this rule to all dependant ids in the global scope.
2366 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
2367 std::bind(&BuiltInsValidator::ValidateLocalInvocationIndexAtReference,
2368 this, decoration, built_in_inst, referenced_from_inst,
2369 std::placeholders::_1));
2370 }
2371
2372 return SPV_SUCCESS;
2373}
2374
2375spv_result_t BuiltInsValidator::ValidateVertexIndexAtReference(
2376 const Decoration& decoration, const Instruction& built_in_inst,
2377 const Instruction& referenced_inst,
2378 const Instruction& referenced_from_inst) {
2379 if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
2380 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
2381 if (storage_class != SpvStorageClassMax &&
2382 storage_class != SpvStorageClassInput) {
2383 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2384 << spvLogStringForEnv(_.context()->target_env)
2385 << " spec allows BuiltIn VertexIndex to be only used for "
2386 "variables with Input storage class. "
2387 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2388 referenced_from_inst)
2389 << " " << GetStorageClassDesc(referenced_from_inst);
2390 }
2391
2392 for (const SpvExecutionModel execution_model : execution_models_) {
2393 if (execution_model != SpvExecutionModelVertex) {
2394 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2395 << spvLogStringForEnv(_.context()->target_env)
2396 << " spec allows BuiltIn VertexIndex to be used only with "
2397 "Vertex execution model. "
2398 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2399 referenced_from_inst, execution_model);
2400 }
2401 }
2402 }
2403
2404 if (function_id_ == 0) {
2405 // Propagate this rule to all dependant ids in the global scope.
2406 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2407 &BuiltInsValidator::ValidateVertexIndexAtReference, this, decoration,
2408 built_in_inst, referenced_from_inst, std::placeholders::_1));
2409 }
2410
2411 return SPV_SUCCESS;
2412}
2413
2414spv_result_t BuiltInsValidator::ValidateLayerOrViewportIndexAtDefinition(
2415 const Decoration& decoration, const Instruction& inst) {
2416 if (spvIsVulkanEnv(_.context()->target_env)) {
2417 // This can be a per-primitive variable for mesh shader stage.
2418 // In such cases variable will have an array of 32-bit integers.
2419 if (decoration.struct_member_index() != Decoration::kInvalidMember) {
2420 // This must be a 32-bit int scalar.
2421 if (spv_result_t error = ValidateI32(
2422 decoration, inst,
2423 [this, &decoration,
2424 &inst](const std::string& message) -> spv_result_t {
2425 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2426 << "According to the Vulkan spec BuiltIn "
2427 << _.grammar().lookupOperandName(
2428 SPV_OPERAND_TYPE_BUILT_IN, decoration.params()[0])
2429 << "variable needs to be a 32-bit int scalar. "
2430 << message;
2431 })) {
2432 return error;
2433 }
2434 } else {
2435 if (spv_result_t error = ValidateOptionalArrayedI32(
2436 decoration, inst,
2437 [this, &decoration,
2438 &inst](const std::string& message) -> spv_result_t {
2439 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2440 << "According to the Vulkan spec BuiltIn "
2441 << _.grammar().lookupOperandName(
2442 SPV_OPERAND_TYPE_BUILT_IN, decoration.params()[0])
2443 << "variable needs to be a 32-bit int scalar. "
2444 << message;
2445 })) {
2446 return error;
2447 }
2448 }
2449 }
2450
2451 // Seed at reference checks with this built-in.
2452 return ValidateLayerOrViewportIndexAtReference(decoration, inst, inst, inst);
2453}
2454
2455spv_result_t BuiltInsValidator::ValidateLayerOrViewportIndexAtReference(
2456 const Decoration& decoration, const Instruction& built_in_inst,
2457 const Instruction& referenced_inst,
2458 const Instruction& referenced_from_inst) {
2459 if (spvIsVulkanEnv(_.context()->target_env)) {
2460 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
2461 if (storage_class != SpvStorageClassMax &&
2462 storage_class != SpvStorageClassInput &&
2463 storage_class != SpvStorageClassOutput) {
2464 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2465 << "Vulkan spec allows BuiltIn "
2466 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2467 decoration.params()[0])
2468 << " to be only used for variables with Input or Output storage "
2469 "class. "
2470 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2471 referenced_from_inst)
2472 << " " << GetStorageClassDesc(referenced_from_inst);
2473 }
2474
2475 if (storage_class == SpvStorageClassInput) {
2476 assert(function_id_ == 0);
2477 for (const auto em :
2478 {SpvExecutionModelVertex, SpvExecutionModelTessellationEvaluation,
2479 SpvExecutionModelGeometry}) {
2480 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
2481 std::bind(&BuiltInsValidator::ValidateNotCalledWithExecutionModel,
2482 this,
2483 "Vulkan spec doesn't allow BuiltIn Layer and "
2484 "ViewportIndex to be "
2485 "used for variables with Input storage class if "
2486 "execution model is Vertex, TessellationEvaluation, or "
2487 "Geometry.",
2488 em, decoration, built_in_inst, referenced_from_inst,
2489 std::placeholders::_1));
2490 }
2491 }
2492
2493 if (storage_class == SpvStorageClassOutput) {
2494 assert(function_id_ == 0);
2495 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2496 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this,
2497 "Vulkan spec doesn't allow BuiltIn Layer and "
2498 "ViewportIndex to be "
2499 "used for variables with Output storage class if "
2500 "execution model is "
2501 "Fragment.",
2502 SpvExecutionModelFragment, decoration, built_in_inst,
2503 referenced_from_inst, std::placeholders::_1));
2504 }
2505
2506 for (const SpvExecutionModel execution_model : execution_models_) {
2507 switch (execution_model) {
2508 case SpvExecutionModelGeometry:
2509 case SpvExecutionModelFragment:
2510 case SpvExecutionModelMeshNV:
2511 // Ok.
2512 break;
2513 case SpvExecutionModelVertex:
2514 case SpvExecutionModelTessellationEvaluation: {
2515 if (!_.HasCapability(SpvCapabilityShaderViewportIndexLayerEXT)) {
2516 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2517 << "Using BuiltIn "
2518 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2519 decoration.params()[0])
2520 << " in Vertex or Tessellation execution model requires "
2521 "the ShaderViewportIndexLayerEXT capability.";
2522 }
2523 break;
2524 }
2525 default: {
2526 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2527 << "Vulkan spec allows BuiltIn "
2528 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2529 decoration.params()[0])
2530 << " to be used only with Vertex, TessellationEvaluation, "
2531 "Geometry, or Fragment execution models. "
2532 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2533 referenced_from_inst, execution_model);
2534 }
2535 }
2536 }
2537 }
2538
2539 if (function_id_ == 0) {
2540 // Propagate this rule to all dependant ids in the global scope.
2541 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
2542 std::bind(&BuiltInsValidator::ValidateLayerOrViewportIndexAtReference,
2543 this, decoration, built_in_inst, referenced_from_inst,
2544 std::placeholders::_1));
2545 }
2546
2547 return SPV_SUCCESS;
2548}
2549
2550spv_result_t BuiltInsValidator::ValidateComputeShaderI32Vec3InputAtDefinition(
2551 const Decoration& decoration, const Instruction& inst) {
2552 if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
2553 if (spv_result_t error = ValidateI32Vec(
2554 decoration, inst, 3,
2555 [this, &decoration,
2556 &inst](const std::string& message) -> spv_result_t {
2557 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2558 << "According to the "
2559 << spvLogStringForEnv(_.context()->target_env)
2560 << " spec BuiltIn "
2561 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2562 decoration.params()[0])
2563 << " variable needs to be a 3-component 32-bit int "
2564 "vector. "
2565 << message;
2566 })) {
2567 return error;
2568 }
2569 }
2570
2571 // Seed at reference checks with this built-in.
2572 return ValidateComputeShaderI32Vec3InputAtReference(decoration, inst, inst,
2573 inst);
2574}
2575
2576spv_result_t BuiltInsValidator::ValidateComputeShaderI32Vec3InputAtReference(
2577 const Decoration& decoration, const Instruction& built_in_inst,
2578 const Instruction& referenced_inst,
2579 const Instruction& referenced_from_inst) {
2580 if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
2581 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
2582 if (storage_class != SpvStorageClassMax &&
2583 storage_class != SpvStorageClassInput) {
2584 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2585 << spvLogStringForEnv(_.context()->target_env)
2586 << " spec allows BuiltIn "
2587 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2588 decoration.params()[0])
2589 << " to be only used for variables with Input storage class. "
2590 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2591 referenced_from_inst)
2592 << " " << GetStorageClassDesc(referenced_from_inst);
2593 }
2594
2595 for (const SpvExecutionModel execution_model : execution_models_) {
2596 bool has_vulkan_model = execution_model == SpvExecutionModelGLCompute ||
2597 execution_model == SpvExecutionModelTaskNV ||
2598 execution_model == SpvExecutionModelMeshNV;
2599 bool has_webgpu_model = execution_model == SpvExecutionModelGLCompute;
2600 if ((spvIsVulkanEnv(_.context()->target_env) && !has_vulkan_model) ||
2601 (spvIsWebGPUEnv(_.context()->target_env) && !has_webgpu_model)) {
2602 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2603 << spvLogStringForEnv(_.context()->target_env)
2604 << " spec allows BuiltIn "
2605 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2606 decoration.params()[0])
2607 << " to be used only with GLCompute execution model. "
2608 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2609 referenced_from_inst, execution_model);
2610 }
2611 }
2612 }
2613
2614 if (function_id_ == 0) {
2615 // Propagate this rule to all dependant ids in the global scope.
2616 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2617 &BuiltInsValidator::ValidateComputeShaderI32Vec3InputAtReference, this,
2618 decoration, built_in_inst, referenced_from_inst,
2619 std::placeholders::_1));
2620 }
2621
2622 return SPV_SUCCESS;
2623}
2624
2625spv_result_t BuiltInsValidator::ValidateComputeI32InputAtDefinition(
2626 const Decoration& decoration, const Instruction& inst) {
2627 if (spvIsVulkanEnv(_.context()->target_env)) {
2628 if (decoration.struct_member_index() != Decoration::kInvalidMember) {
2629 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2630 << "BuiltIn "
2631 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2632 decoration.params()[0])
2633 << " cannot be used as a member decoration ";
2634 }
2635 if (spv_result_t error = ValidateI32(
2636 decoration, inst,
2637 [this, &decoration,
2638 &inst](const std::string& message) -> spv_result_t {
2639 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2640 << "According to the "
2641 << spvLogStringForEnv(_.context()->target_env)
2642 << " spec BuiltIn "
2643 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2644 decoration.params()[0])
2645 << " variable needs to be a 32-bit int "
2646 "vector. "
2647 << message;
2648 })) {
2649 return error;
2650 }
2651 }
2652
2653 // Seed at reference checks with this built-in.
2654 return ValidateComputeI32InputAtReference(decoration, inst, inst, inst);
2655}
2656
2657spv_result_t BuiltInsValidator::ValidateComputeI32InputAtReference(
2658 const Decoration& decoration, const Instruction& built_in_inst,
2659 const Instruction& referenced_inst,
2660 const Instruction& referenced_from_inst) {
2661 if (spvIsVulkanEnv(_.context()->target_env)) {
2662 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
2663 if (storage_class != SpvStorageClassMax &&
2664 storage_class != SpvStorageClassInput) {
2665 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2666 << spvLogStringForEnv(_.context()->target_env)
2667 << " spec allows BuiltIn "
2668 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2669 decoration.params()[0])
2670 << " to be only used for variables with Input storage class. "
2671 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2672 referenced_from_inst)
2673 << " " << GetStorageClassDesc(referenced_from_inst);
2674 }
2675
2676 for (const SpvExecutionModel execution_model : execution_models_) {
2677 bool has_vulkan_model = execution_model == SpvExecutionModelGLCompute ||
2678 execution_model == SpvExecutionModelTaskNV ||
2679 execution_model == SpvExecutionModelMeshNV;
2680 if (spvIsVulkanEnv(_.context()->target_env) && !has_vulkan_model) {
2681 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2682 << spvLogStringForEnv(_.context()->target_env)
2683 << " spec allows BuiltIn "
2684 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2685 decoration.params()[0])
2686 << " to be used only with GLCompute execution model. "
2687 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2688 referenced_from_inst, execution_model);
2689 }
2690 }
2691 }
2692
2693 if (function_id_ == 0) {
2694 // Propagate this rule to all dependant ids in the global scope.
2695 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
2696 std::bind(&BuiltInsValidator::ValidateComputeI32InputAtReference, this,
2697 decoration, built_in_inst, referenced_from_inst,
2698 std::placeholders::_1));
2699 }
2700
2701 return SPV_SUCCESS;
2702}
2703
2704spv_result_t BuiltInsValidator::ValidateI32InputAtDefinition(
2705 const Decoration& decoration, const Instruction& inst) {
2706 if (spvIsVulkanEnv(_.context()->target_env)) {
2707 if (decoration.struct_member_index() != Decoration::kInvalidMember) {
2708 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2709 << "BuiltIn "
2710 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2711 decoration.params()[0])
2712 << " cannot be used as a member decoration ";
2713 }
2714 if (spv_result_t error = ValidateI32(
2715 decoration, inst,
2716 [this, &decoration,
2717 &inst](const std::string& message) -> spv_result_t {
2718 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2719 << "According to the "
2720 << spvLogStringForEnv(_.context()->target_env)
2721 << " spec BuiltIn "
2722 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2723 decoration.params()[0])
2724 << " variable needs to be a 32-bit int. " << message;
2725 })) {
2726 return error;
2727 }
2728
2729 const SpvStorageClass storage_class = GetStorageClass(inst);
2730 if (storage_class != SpvStorageClassMax &&
2731 storage_class != SpvStorageClassInput) {
2732 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2733 << spvLogStringForEnv(_.context()->target_env)
2734 << " spec allows BuiltIn "
2735 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2736 decoration.params()[0])
2737 << " to be only used for variables with Input storage class. "
2738 << GetReferenceDesc(decoration, inst, inst, inst) << " "
2739 << GetStorageClassDesc(inst);
2740 }
2741 }
2742
2743 return SPV_SUCCESS;
2744}
2745
2746spv_result_t BuiltInsValidator::ValidateI32Vec4InputAtDefinition(
2747 const Decoration& decoration, const Instruction& inst) {
2748 if (spvIsVulkanEnv(_.context()->target_env)) {
2749 if (decoration.struct_member_index() != Decoration::kInvalidMember) {
2750 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2751 << "BuiltIn "
2752 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2753 decoration.params()[0])
2754 << " cannot be used as a member decoration ";
2755 }
2756 if (spv_result_t error = ValidateI32Vec(
2757 decoration, inst, 4,
2758 [this, &decoration,
2759 &inst](const std::string& message) -> spv_result_t {
2760 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2761 << "According to the "
2762 << spvLogStringForEnv(_.context()->target_env)
2763 << " spec BuiltIn "
2764 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2765 decoration.params()[0])
2766 << " variable needs to be a 4-component 32-bit int "
2767 "vector. "
2768 << message;
2769 })) {
2770 return error;
2771 }
2772
2773 const SpvStorageClass storage_class = GetStorageClass(inst);
2774 if (storage_class != SpvStorageClassMax &&
2775 storage_class != SpvStorageClassInput) {
2776 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2777 << spvLogStringForEnv(_.context()->target_env)
2778 << " spec allows BuiltIn "
2779 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2780 decoration.params()[0])
2781 << " to be only used for variables with Input storage class. "
2782 << GetReferenceDesc(decoration, inst, inst, inst) << " "
2783 << GetStorageClassDesc(inst);
2784 }
2785 }
2786
2787 return SPV_SUCCESS;
2788}
2789
2790spv_result_t BuiltInsValidator::ValidateWorkgroupSizeAtDefinition(
2791 const Decoration& decoration, const Instruction& inst) {
2792 if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
2793 if (spvIsVulkanEnv(_.context()->target_env) &&
2794 !spvOpcodeIsConstant(inst.opcode())) {
2795 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2796 << "Vulkan spec requires BuiltIn WorkgroupSize to be a "
2797 "constant. "
2798 << GetIdDesc(inst) << " is not a constant.";
2799 }
2800
2801 if (spv_result_t error = ValidateI32Vec(
2802 decoration, inst, 3,
2803 [this, &inst](const std::string& message) -> spv_result_t {
2804 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2805 << "According to the "
2806 << spvLogStringForEnv(_.context()->target_env)
2807 << " spec BuiltIn WorkgroupSize variable needs to be a "
2808 "3-component 32-bit int vector. "
2809 << message;
2810 })) {
2811 return error;
2812 }
2813 }
2814
2815 // Seed at reference checks with this built-in.
2816 return ValidateWorkgroupSizeAtReference(decoration, inst, inst, inst);
2817}
2818
2819spv_result_t BuiltInsValidator::ValidateWorkgroupSizeAtReference(
2820 const Decoration& decoration, const Instruction& built_in_inst,
2821 const Instruction& referenced_inst,
2822 const Instruction& referenced_from_inst) {
2823 if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
2824 for (const SpvExecutionModel execution_model : execution_models_) {
2825 if (execution_model != SpvExecutionModelGLCompute) {
2826 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2827 << spvLogStringForEnv(_.context()->target_env)
2828 << " spec allows BuiltIn "
2829 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2830 decoration.params()[0])
2831 << " to be used only with GLCompute execution model. "
2832 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2833 referenced_from_inst, execution_model);
2834 }
2835 }
2836 }
2837
2838 if (function_id_ == 0) {
2839 // Propagate this rule to all dependant ids in the global scope.
2840 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2841 &BuiltInsValidator::ValidateWorkgroupSizeAtReference, this, decoration,
2842 built_in_inst, referenced_from_inst, std::placeholders::_1));
2843 }
2844
2845 return SPV_SUCCESS;
2846}
2847
2848spv_result_t BuiltInsValidator::ValidateSMBuiltinsAtDefinition(
2849 const Decoration& decoration, const Instruction& inst) {
2850 if (spvIsVulkanEnv(_.context()->target_env)) {
2851 if (spv_result_t error = ValidateI32(
2852 decoration, inst,
2853 [this, &inst,
2854 &decoration](const std::string& message) -> spv_result_t {
2855 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2856 << "According to the "
2857 << spvLogStringForEnv(_.context()->target_env)
2858 << " spec BuiltIn "
2859 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2860 decoration.params()[0])
2861 << " variable needs to be a 32-bit int scalar. "
2862 << message;
2863 })) {
2864 return error;
2865 }
2866 }
2867
2868 // Seed at reference checks with this built-in.
2869 return ValidateSMBuiltinsAtReference(decoration, inst, inst, inst);
2870}
2871
2872spv_result_t BuiltInsValidator::ValidateSMBuiltinsAtReference(
2873 const Decoration& decoration, const Instruction& built_in_inst,
2874 const Instruction& referenced_inst,
2875 const Instruction& referenced_from_inst) {
2876 if (spvIsVulkanEnv(_.context()->target_env)) {
2877 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
2878 if (storage_class != SpvStorageClassMax &&
2879 storage_class != SpvStorageClassInput) {
2880 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2881 << spvLogStringForEnv(_.context()->target_env)
2882 << " spec allows BuiltIn "
2883 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2884 decoration.params()[0])
2885 << " to be only used for "
2886 "variables with Input storage class. "
2887 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2888 referenced_from_inst)
2889 << " " << GetStorageClassDesc(referenced_from_inst);
2890 }
2891 }
2892
2893 if (function_id_ == 0) {
2894 // Propagate this rule to all dependant ids in the global scope.
2895 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2896 &BuiltInsValidator::ValidateSMBuiltinsAtReference, this, decoration,
2897 built_in_inst, referenced_from_inst, std::placeholders::_1));
2898 }
2899
2900 return SPV_SUCCESS;
2901}
2902
2903spv_result_t BuiltInsValidator::ValidateSingleBuiltInAtDefinition(
2904 const Decoration& decoration, const Instruction& inst) {
2905 const SpvBuiltIn label = SpvBuiltIn(decoration.params()[0]);
2906
2907 // Builtins can only be applied to variables, structures or constants.
2908 auto target_opcode = inst.opcode();
2909 if (target_opcode != SpvOpTypeStruct && target_opcode != SpvOpVariable &&
2910 !spvOpcodeIsConstant(target_opcode)) {
2911 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2912 << "BuiltIns can only target variables, structs or constants";
2913 }
2914
2915 if (!spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
2916 // Early return. All currently implemented rules are based on Vulkan or
2917 // WebGPU spec.
2918 //
2919 // TODO: If you are adding validation rules for environments other than
2920 // Vulkan or WebGPU (or general rules which are not environment
2921 // independent), then you need to modify or remove this condition. Consider
2922 // also adding early returns into BuiltIn-specific rules, so that the system
2923 // doesn't spawn new rules which don't do anything.
2924 return SPV_SUCCESS;
2925 }
2926
2927 if (spvIsWebGPUEnv(_.context()->target_env) &&
2928 !IsBuiltInValidForWebGPU(label)) {
2929 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2930 << "WebGPU does not allow BuiltIn "
2931 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2932 decoration.params()[0]);
2933 }
2934
2935 // If you are adding a new BuiltIn enum, please register it here.
2936 // If the newly added enum has validation rules associated with it
2937 // consider leaving a TODO and/or creating an issue.
2938 switch (label) {
2939 case SpvBuiltInClipDistance:
2940 case SpvBuiltInCullDistance: {
2941 return ValidateClipOrCullDistanceAtDefinition(decoration, inst);
2942 }
2943 case SpvBuiltInFragCoord: {
2944 return ValidateFragCoordAtDefinition(decoration, inst);
2945 }
2946 case SpvBuiltInFragDepth: {
2947 return ValidateFragDepthAtDefinition(decoration, inst);
2948 }
2949 case SpvBuiltInFrontFacing: {
2950 return ValidateFrontFacingAtDefinition(decoration, inst);
2951 }
2952 case SpvBuiltInGlobalInvocationId:
2953 case SpvBuiltInLocalInvocationId:
2954 case SpvBuiltInNumWorkgroups:
2955 case SpvBuiltInWorkgroupId: {
2956 return ValidateComputeShaderI32Vec3InputAtDefinition(decoration, inst);
2957 }
2958 case SpvBuiltInHelperInvocation: {
2959 return ValidateHelperInvocationAtDefinition(decoration, inst);
2960 }
2961 case SpvBuiltInInvocationId: {
2962 return ValidateInvocationIdAtDefinition(decoration, inst);
2963 }
2964 case SpvBuiltInInstanceIndex: {
2965 return ValidateInstanceIndexAtDefinition(decoration, inst);
2966 }
2967 case SpvBuiltInLayer:
2968 case SpvBuiltInViewportIndex: {
2969 return ValidateLayerOrViewportIndexAtDefinition(decoration, inst);
2970 }
2971 case SpvBuiltInPatchVertices: {
2972 return ValidatePatchVerticesAtDefinition(decoration, inst);
2973 }
2974 case SpvBuiltInPointCoord: {
2975 return ValidatePointCoordAtDefinition(decoration, inst);
2976 }
2977 case SpvBuiltInPointSize: {
2978 return ValidatePointSizeAtDefinition(decoration, inst);
2979 }
2980 case SpvBuiltInPosition: {
2981 return ValidatePositionAtDefinition(decoration, inst);
2982 }
2983 case SpvBuiltInPrimitiveId: {
2984 return ValidatePrimitiveIdAtDefinition(decoration, inst);
2985 }
2986 case SpvBuiltInSampleId: {
2987 return ValidateSampleIdAtDefinition(decoration, inst);
2988 }
2989 case SpvBuiltInSampleMask: {
2990 return ValidateSampleMaskAtDefinition(decoration, inst);
2991 }
2992 case SpvBuiltInSamplePosition: {
2993 return ValidateSamplePositionAtDefinition(decoration, inst);
2994 }
2995 case SpvBuiltInSubgroupId:
2996 case SpvBuiltInNumSubgroups: {
2997 return ValidateComputeI32InputAtDefinition(decoration, inst);
2998 }
2999 case SpvBuiltInSubgroupLocalInvocationId:
3000 case SpvBuiltInSubgroupSize: {
3001 return ValidateI32InputAtDefinition(decoration, inst);
3002 }
3003 case SpvBuiltInSubgroupEqMask:
3004 case SpvBuiltInSubgroupGeMask:
3005 case SpvBuiltInSubgroupGtMask:
3006 case SpvBuiltInSubgroupLeMask:
3007 case SpvBuiltInSubgroupLtMask: {
3008 return ValidateI32Vec4InputAtDefinition(decoration, inst);
3009 }
3010 case SpvBuiltInTessCoord: {
3011 return ValidateTessCoordAtDefinition(decoration, inst);
3012 }
3013 case SpvBuiltInTessLevelOuter: {
3014 return ValidateTessLevelOuterAtDefinition(decoration, inst);
3015 }
3016 case SpvBuiltInTessLevelInner: {
3017 return ValidateTessLevelInnerAtDefinition(decoration, inst);
3018 }
3019 case SpvBuiltInVertexIndex: {
3020 return ValidateVertexIndexAtDefinition(decoration, inst);
3021 }
3022 case SpvBuiltInWorkgroupSize: {
3023 return ValidateWorkgroupSizeAtDefinition(decoration, inst);
3024 }
3025 case SpvBuiltInVertexId:
3026 case SpvBuiltInInstanceId: {
3027 return ValidateVertexIdOrInstanceIdAtDefinition(decoration, inst);
3028 }
3029 case SpvBuiltInLocalInvocationIndex: {
3030 return ValidateLocalInvocationIndexAtDefinition(decoration, inst);
3031 }
3032 case SpvBuiltInWarpsPerSMNV:
3033 case SpvBuiltInSMCountNV:
3034 case SpvBuiltInWarpIDNV:
3035 case SpvBuiltInSMIDNV: {
3036 return ValidateSMBuiltinsAtDefinition(decoration, inst);
3037 }
3038 case SpvBuiltInWorkDim:
3039 case SpvBuiltInGlobalSize:
3040 case SpvBuiltInEnqueuedWorkgroupSize:
3041 case SpvBuiltInGlobalOffset:
3042 case SpvBuiltInGlobalLinearId:
3043 case SpvBuiltInSubgroupMaxSize:
3044 case SpvBuiltInNumEnqueuedSubgroups:
3045 case SpvBuiltInBaseVertex:
3046 case SpvBuiltInBaseInstance:
3047 case SpvBuiltInDrawIndex:
3048 case SpvBuiltInDeviceIndex:
3049 case SpvBuiltInViewIndex:
3050 case SpvBuiltInBaryCoordNoPerspAMD:
3051 case SpvBuiltInBaryCoordNoPerspCentroidAMD:
3052 case SpvBuiltInBaryCoordNoPerspSampleAMD:
3053 case SpvBuiltInBaryCoordSmoothAMD:
3054 case SpvBuiltInBaryCoordSmoothCentroidAMD:
3055 case SpvBuiltInBaryCoordSmoothSampleAMD:
3056 case SpvBuiltInBaryCoordPullModelAMD:
3057 case SpvBuiltInFragStencilRefEXT:
3058 case SpvBuiltInViewportMaskNV:
3059 case SpvBuiltInSecondaryPositionNV:
3060 case SpvBuiltInSecondaryViewportMaskNV:
3061 case SpvBuiltInPositionPerViewNV:
3062 case SpvBuiltInViewportMaskPerViewNV:
3063 case SpvBuiltInFullyCoveredEXT:
3064 case SpvBuiltInMax:
3065 case SpvBuiltInTaskCountNV:
3066 case SpvBuiltInPrimitiveCountNV:
3067 case SpvBuiltInPrimitiveIndicesNV:
3068 case SpvBuiltInClipDistancePerViewNV:
3069 case SpvBuiltInCullDistancePerViewNV:
3070 case SpvBuiltInLayerPerViewNV:
3071 case SpvBuiltInMeshViewCountNV:
3072 case SpvBuiltInMeshViewIndicesNV:
3073 case SpvBuiltInBaryCoordNV:
3074 case SpvBuiltInBaryCoordNoPerspNV:
3075 case SpvBuiltInFragmentSizeNV: // alias SpvBuiltInFragSizeEXT
3076 case SpvBuiltInInvocationsPerPixelNV: // alias
3077 // SpvBuiltInFragInvocationCountEXT
3078 case SpvBuiltInLaunchIdNV:
3079 case SpvBuiltInLaunchSizeNV:
3080 case SpvBuiltInWorldRayOriginNV:
3081 case SpvBuiltInWorldRayDirectionNV:
3082 case SpvBuiltInObjectRayOriginNV:
3083 case SpvBuiltInObjectRayDirectionNV:
3084 case SpvBuiltInRayTminNV:
3085 case SpvBuiltInRayTmaxNV:
3086 case SpvBuiltInInstanceCustomIndexNV:
3087 case SpvBuiltInObjectToWorldNV:
3088 case SpvBuiltInWorldToObjectNV:
3089 case SpvBuiltInHitTNV:
3090 case SpvBuiltInHitKindNV:
3091 case SpvBuiltInIncomingRayFlagsNV:
3092 case SpvBuiltInRayGeometryIndexKHR: {
3093 // No validation rules (for the moment).
3094 break;
3095 }
3096 }
3097 return SPV_SUCCESS;
3098}
3099
3100spv_result_t BuiltInsValidator::ValidateBuiltInsAtDefinition() {
3101 for (const auto& kv : _.id_decorations()) {
3102 const uint32_t id = kv.first;
3103 const auto& decorations = kv.second;
3104 if (decorations.empty()) {
3105 continue;
3106 }
3107
3108 const Instruction* inst = _.FindDef(id);
3109 assert(inst);
3110
3111 for (const auto& decoration : kv.second) {
3112 if (decoration.dec_type() != SpvDecorationBuiltIn) {
3113 continue;
3114 }
3115
3116 if (spv_result_t error =
3117 ValidateSingleBuiltInAtDefinition(decoration, *inst)) {
3118 return error;
3119 }
3120 }
3121 }
3122
3123 return SPV_SUCCESS;
3124}
3125
3126spv_result_t BuiltInsValidator::Run() {
3127 // First pass: validate all built-ins at definition and seed
3128 // id_to_at_reference_checks_ with built-ins.
3129 if (auto error = ValidateBuiltInsAtDefinition()) {
3130 return error;
3131 }
3132
3133 if (id_to_at_reference_checks_.empty()) {
3134 // No validation tasks were seeded. Nothing else to do.
3135 return SPV_SUCCESS;
3136 }
3137
3138 // Second pass: validate every id reference in the module using
3139 // rules in id_to_at_reference_checks_.
3140 for (const Instruction& inst : _.ordered_instructions()) {
3141 Update(inst);
3142
3143 std::set<uint32_t> already_checked;
3144
3145 for (const auto& operand : inst.operands()) {
3146 if (!spvIsIdType(operand.type)) {
3147 // Not id.
3148 continue;
3149 }
3150
3151 const uint32_t id = inst.word(operand.offset);
3152 if (id == inst.id()) {
3153 // No need to check result id.
3154 continue;
3155 }
3156
3157 if (!already_checked.insert(id).second) {
3158 // The instruction has already referenced this id.
3159 continue;
3160 }
3161
3162 // Instruction references the id. Run all checks associated with the id
3163 // on the instruction. id_to_at_reference_checks_ can be modified in the
3164 // process, iterators are safe because it's a tree-based map.
3165 const auto it = id_to_at_reference_checks_.find(id);
3166 if (it != id_to_at_reference_checks_.end()) {
3167 for (const auto& check : it->second) {
3168 if (spv_result_t error = check(inst)) {
3169 return error;
3170 }
3171 }
3172 }
3173 }
3174 }
3175
3176 return SPV_SUCCESS;
3177}
3178
3179} // namespace
3180
3181// Validates correctness of built-in variables.
3182spv_result_t ValidateBuiltIns(ValidationState_t& _) {
3183 BuiltInsValidator validator(_);
3184 return validator.Run();
3185}
3186
3187} // namespace val
3188} // namespace spvtools
3189