1 // Copyright (c) 2018 Google LLC.
2 // Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
9 // http://www.apache.org/licenses/LICENSE-2.0
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
21 #include "source/opcode.h"
22 #include "source/spirv_target_env.h"
23 #include "source/val/instruction.h"
24 #include "source/val/validate.h"
25 #include "source/val/validate_scopes.h"
26 #include "source/val/validation_state.h"
32 bool AreLayoutCompatibleStructs(ValidationState_t
&, const Instruction
*,
34 bool HaveLayoutCompatibleMembers(ValidationState_t
&, const Instruction
*,
36 bool HaveSameLayoutDecorations(ValidationState_t
&, const Instruction
*,
38 bool HasConflictingMemberOffsets(const std::set
<Decoration
>&,
39 const std::set
<Decoration
>&);
41 bool IsAllowedTypeOrArrayOfSame(ValidationState_t
& _
, const Instruction
* type
,
42 std::initializer_list
<spv::Op
> allowed
) {
43 if (std::find(allowed
.begin(), allowed
.end(), type
->opcode()) !=
47 if (type
->opcode() == spv::Op::OpTypeArray
||
48 type
->opcode() == spv::Op::OpTypeRuntimeArray
) {
49 auto elem_type
= _
.FindDef(type
->word(2));
50 return std::find(allowed
.begin(), allowed
.end(), elem_type
->opcode()) !=
56 // Returns true if the two instructions represent structs that, as far as the
57 // validator can tell, have the exact same data layout.
58 bool AreLayoutCompatibleStructs(ValidationState_t
& _
, const Instruction
* type1
,
59 const Instruction
* type2
) {
60 if (type1
->opcode() != spv::Op::OpTypeStruct
) {
63 if (type2
->opcode() != spv::Op::OpTypeStruct
) {
67 if (!HaveLayoutCompatibleMembers(_
, type1
, type2
)) return false;
69 return HaveSameLayoutDecorations(_
, type1
, type2
);
72 // Returns true if the operands to the OpTypeStruct instruction defining the
73 // types are the same or are layout compatible types. |type1| and |type2| must
74 // be OpTypeStruct instructions.
75 bool HaveLayoutCompatibleMembers(ValidationState_t
& _
, const Instruction
* type1
,
76 const Instruction
* type2
) {
77 assert(type1
->opcode() == spv::Op::OpTypeStruct
&&
78 "type1 must be an OpTypeStruct instruction.");
79 assert(type2
->opcode() == spv::Op::OpTypeStruct
&&
80 "type2 must be an OpTypeStruct instruction.");
81 const auto& type1_operands
= type1
->operands();
82 const auto& type2_operands
= type2
->operands();
83 if (type1_operands
.size() != type2_operands
.size()) {
87 for (size_t operand
= 2; operand
< type1_operands
.size(); ++operand
) {
88 if (type1
->word(operand
) != type2
->word(operand
)) {
89 auto def1
= _
.FindDef(type1
->word(operand
));
90 auto def2
= _
.FindDef(type2
->word(operand
));
91 if (!AreLayoutCompatibleStructs(_
, def1
, def2
)) {
99 // Returns true if all decorations that affect the data layout of the struct
100 // (like Offset), are the same for the two types. |type1| and |type2| must be
101 // OpTypeStruct instructions.
102 bool HaveSameLayoutDecorations(ValidationState_t
& _
, const Instruction
* type1
,
103 const Instruction
* type2
) {
104 assert(type1
->opcode() == spv::Op::OpTypeStruct
&&
105 "type1 must be an OpTypeStruct instruction.");
106 assert(type2
->opcode() == spv::Op::OpTypeStruct
&&
107 "type2 must be an OpTypeStruct instruction.");
108 const std::set
<Decoration
>& type1_decorations
= _
.id_decorations(type1
->id());
109 const std::set
<Decoration
>& type2_decorations
= _
.id_decorations(type2
->id());
111 // TODO: Will have to add other check for arrays an matricies if we want to
113 if (HasConflictingMemberOffsets(type1_decorations
, type2_decorations
)) {
120 bool HasConflictingMemberOffsets(
121 const std::set
<Decoration
>& type1_decorations
,
122 const std::set
<Decoration
>& type2_decorations
) {
124 // We are interested in conflicting decoration. If a decoration is in one
125 // list but not the other, then we will assume the code is correct. We are
126 // looking for things we know to be wrong.
128 // We do not have to traverse type2_decoration because, after traversing
129 // type1_decorations, anything new will not be found in
130 // type1_decoration. Therefore, it cannot lead to a conflict.
131 for (const Decoration
& decoration
: type1_decorations
) {
132 switch (decoration
.dec_type()) {
133 case spv::Decoration::Offset
: {
134 // Since these affect the layout of the struct, they must be present
136 auto compare
= [&decoration
](const Decoration
& rhs
) {
137 if (rhs
.dec_type() != spv::Decoration::Offset
) return false;
138 return decoration
.struct_member_index() ==
139 rhs
.struct_member_index();
141 auto i
= std::find_if(type2_decorations
.begin(),
142 type2_decorations
.end(), compare
);
143 if (i
!= type2_decorations
.end() &&
144 decoration
.params().front() != i
->params().front()) {
149 // This decoration does not affect the layout of the structure, so
158 // If |skip_builtin| is true, returns true if |storage| contains bool within
159 // it and no storage that contains the bool is builtin.
160 // If |skip_builtin| is false, returns true if |storage| contains bool within
162 bool ContainsInvalidBool(ValidationState_t
& _
, const Instruction
* storage
,
165 for (const Decoration
& decoration
: _
.id_decorations(storage
->id())) {
166 if (decoration
.dec_type() == spv::Decoration::BuiltIn
) return false;
170 const size_t elem_type_index
= 1;
171 uint32_t elem_type_id
;
172 Instruction
* elem_type
;
174 switch (storage
->opcode()) {
175 case spv::Op::OpTypeBool
:
177 case spv::Op::OpTypeVector
:
178 case spv::Op::OpTypeMatrix
:
179 case spv::Op::OpTypeArray
:
180 case spv::Op::OpTypeRuntimeArray
:
181 elem_type_id
= storage
->GetOperandAs
<uint32_t>(elem_type_index
);
182 elem_type
= _
.FindDef(elem_type_id
);
183 return ContainsInvalidBool(_
, elem_type
, skip_builtin
);
184 case spv::Op::OpTypeStruct
:
185 for (size_t member_type_index
= 1;
186 member_type_index
< storage
->operands().size();
187 ++member_type_index
) {
188 auto member_type_id
=
189 storage
->GetOperandAs
<uint32_t>(member_type_index
);
190 auto member_type
= _
.FindDef(member_type_id
);
191 if (ContainsInvalidBool(_
, member_type
, skip_builtin
)) return true;
199 bool ContainsCooperativeMatrix(ValidationState_t
& _
,
200 const Instruction
* storage
) {
201 const size_t elem_type_index
= 1;
202 uint32_t elem_type_id
;
203 Instruction
* elem_type
;
205 switch (storage
->opcode()) {
206 case spv::Op::OpTypeCooperativeMatrixNV
:
207 case spv::Op::OpTypeCooperativeMatrixKHR
:
209 case spv::Op::OpTypeArray
:
210 case spv::Op::OpTypeRuntimeArray
:
211 elem_type_id
= storage
->GetOperandAs
<uint32_t>(elem_type_index
);
212 elem_type
= _
.FindDef(elem_type_id
);
213 return ContainsCooperativeMatrix(_
, elem_type
);
214 case spv::Op::OpTypeStruct
:
215 for (size_t member_type_index
= 1;
216 member_type_index
< storage
->operands().size();
217 ++member_type_index
) {
218 auto member_type_id
=
219 storage
->GetOperandAs
<uint32_t>(member_type_index
);
220 auto member_type
= _
.FindDef(member_type_id
);
221 if (ContainsCooperativeMatrix(_
, member_type
)) return true;
230 std::pair
<spv::StorageClass
, spv::StorageClass
> GetStorageClass(
231 ValidationState_t
& _
, const Instruction
* inst
) {
232 spv::StorageClass dst_sc
= spv::StorageClass::Max
;
233 spv::StorageClass src_sc
= spv::StorageClass::Max
;
234 switch (inst
->opcode()) {
235 case spv::Op::OpCooperativeMatrixLoadNV
:
236 case spv::Op::OpCooperativeMatrixLoadTensorNV
:
237 case spv::Op::OpCooperativeMatrixLoadKHR
:
238 case spv::Op::OpLoad
: {
239 auto load_pointer
= _
.FindDef(inst
->GetOperandAs
<uint32_t>(2));
240 auto load_pointer_type
= _
.FindDef(load_pointer
->type_id());
241 dst_sc
= load_pointer_type
->GetOperandAs
<spv::StorageClass
>(1);
244 case spv::Op::OpCooperativeMatrixStoreNV
:
245 case spv::Op::OpCooperativeMatrixStoreTensorNV
:
246 case spv::Op::OpCooperativeMatrixStoreKHR
:
247 case spv::Op::OpStore
: {
248 auto store_pointer
= _
.FindDef(inst
->GetOperandAs
<uint32_t>(0));
249 auto store_pointer_type
= _
.FindDef(store_pointer
->type_id());
250 dst_sc
= store_pointer_type
->GetOperandAs
<spv::StorageClass
>(1);
253 case spv::Op::OpCopyMemory
:
254 case spv::Op::OpCopyMemorySized
: {
255 auto dst
= _
.FindDef(inst
->GetOperandAs
<uint32_t>(0));
256 auto dst_type
= _
.FindDef(dst
->type_id());
257 dst_sc
= dst_type
->GetOperandAs
<spv::StorageClass
>(1);
258 auto src
= _
.FindDef(inst
->GetOperandAs
<uint32_t>(1));
259 auto src_type
= _
.FindDef(src
->type_id());
260 src_sc
= src_type
->GetOperandAs
<spv::StorageClass
>(1);
267 return std::make_pair(dst_sc
, src_sc
);
270 // Returns the number of instruction words taken up by a memory access
271 // argument and its implied operands.
272 int MemoryAccessNumWords(uint32_t mask
) {
273 int result
= 1; // Count the mask
274 if (mask
& uint32_t(spv::MemoryAccessMask::Aligned
)) ++result
;
275 if (mask
& uint32_t(spv::MemoryAccessMask::MakePointerAvailableKHR
)) ++result
;
276 if (mask
& uint32_t(spv::MemoryAccessMask::MakePointerVisibleKHR
)) ++result
;
280 // Returns the scope ID operand for MakeAvailable memory access with mask
281 // at the given operand index.
282 // This function is only called for OpLoad, OpStore, OpCopyMemory and
283 // OpCopyMemorySized, OpCooperativeMatrixLoadNV, and
284 // OpCooperativeMatrixStoreNV.
285 uint32_t GetMakeAvailableScope(const Instruction
* inst
, uint32_t mask
,
286 uint32_t mask_index
) {
287 assert(mask
& uint32_t(spv::MemoryAccessMask::MakePointerAvailableKHR
));
288 uint32_t this_bit
= uint32_t(spv::MemoryAccessMask::MakePointerAvailableKHR
);
290 mask_index
- 1 + MemoryAccessNumWords(mask
& (this_bit
| (this_bit
- 1)));
291 return inst
->GetOperandAs
<uint32_t>(index
);
294 // This function is only called for OpLoad, OpStore, OpCopyMemory,
295 // OpCopyMemorySized, OpCooperativeMatrixLoadNV, and
296 // OpCooperativeMatrixStoreNV.
297 uint32_t GetMakeVisibleScope(const Instruction
* inst
, uint32_t mask
,
298 uint32_t mask_index
) {
299 assert(mask
& uint32_t(spv::MemoryAccessMask::MakePointerVisibleKHR
));
300 uint32_t this_bit
= uint32_t(spv::MemoryAccessMask::MakePointerVisibleKHR
);
302 mask_index
- 1 + MemoryAccessNumWords(mask
& (this_bit
| (this_bit
- 1)));
303 return inst
->GetOperandAs
<uint32_t>(index
);
306 bool DoesStructContainRTA(const ValidationState_t
& _
, const Instruction
* inst
) {
307 for (size_t member_index
= 1; member_index
< inst
->operands().size();
309 const auto member_id
= inst
->GetOperandAs
<uint32_t>(member_index
);
310 const auto member_type
= _
.FindDef(member_id
);
311 if (member_type
->opcode() == spv::Op::OpTypeRuntimeArray
) return true;
316 spv_result_t
CheckMemoryAccess(ValidationState_t
& _
, const Instruction
* inst
,
318 spv::StorageClass dst_sc
, src_sc
;
319 std::tie(dst_sc
, src_sc
) = GetStorageClass(_
, inst
);
320 if (inst
->operands().size() <= index
) {
321 // Cases where lack of some operand is invalid
322 if (src_sc
== spv::StorageClass::PhysicalStorageBuffer
||
323 dst_sc
== spv::StorageClass::PhysicalStorageBuffer
) {
324 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
326 << "Memory accesses with PhysicalStorageBuffer must use Aligned.";
331 const uint32_t mask
= inst
->GetOperandAs
<uint32_t>(index
);
332 if (mask
& uint32_t(spv::MemoryAccessMask::MakePointerAvailableKHR
)) {
333 if (inst
->opcode() == spv::Op::OpLoad
||
334 inst
->opcode() == spv::Op::OpCooperativeMatrixLoadNV
||
335 inst
->opcode() == spv::Op::OpCooperativeMatrixLoadTensorNV
||
336 inst
->opcode() == spv::Op::OpCooperativeMatrixLoadKHR
) {
337 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
338 << "MakePointerAvailableKHR cannot be used with OpLoad.";
341 if (!(mask
& uint32_t(spv::MemoryAccessMask::NonPrivatePointerKHR
))) {
342 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
343 << "NonPrivatePointerKHR must be specified if "
344 "MakePointerAvailableKHR is specified.";
347 // Check the associated scope for MakeAvailableKHR.
348 const auto available_scope
= GetMakeAvailableScope(inst
, mask
, index
);
349 if (auto error
= ValidateMemoryScope(_
, inst
, available_scope
))
353 if (mask
& uint32_t(spv::MemoryAccessMask::MakePointerVisibleKHR
)) {
354 if (inst
->opcode() == spv::Op::OpStore
||
355 inst
->opcode() == spv::Op::OpCooperativeMatrixStoreNV
||
356 inst
->opcode() == spv::Op::OpCooperativeMatrixStoreKHR
||
357 inst
->opcode() == spv::Op::OpCooperativeMatrixStoreTensorNV
) {
358 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
359 << "MakePointerVisibleKHR cannot be used with OpStore.";
362 if (!(mask
& uint32_t(spv::MemoryAccessMask::NonPrivatePointerKHR
))) {
363 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
364 << "NonPrivatePointerKHR must be specified if "
365 << "MakePointerVisibleKHR is specified.";
368 // Check the associated scope for MakeVisibleKHR.
369 const auto visible_scope
= GetMakeVisibleScope(inst
, mask
, index
);
370 if (auto error
= ValidateMemoryScope(_
, inst
, visible_scope
)) return error
;
373 if (mask
& uint32_t(spv::MemoryAccessMask::NonPrivatePointerKHR
)) {
374 if (dst_sc
!= spv::StorageClass::Uniform
&&
375 dst_sc
!= spv::StorageClass::Workgroup
&&
376 dst_sc
!= spv::StorageClass::CrossWorkgroup
&&
377 dst_sc
!= spv::StorageClass::Generic
&&
378 dst_sc
!= spv::StorageClass::Image
&&
379 dst_sc
!= spv::StorageClass::StorageBuffer
&&
380 dst_sc
!= spv::StorageClass::PhysicalStorageBuffer
) {
381 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
382 << "NonPrivatePointerKHR requires a pointer in Uniform, "
383 << "Workgroup, CrossWorkgroup, Generic, Image or StorageBuffer "
384 << "storage classes.";
386 if (src_sc
!= spv::StorageClass::Max
&&
387 src_sc
!= spv::StorageClass::Uniform
&&
388 src_sc
!= spv::StorageClass::Workgroup
&&
389 src_sc
!= spv::StorageClass::CrossWorkgroup
&&
390 src_sc
!= spv::StorageClass::Generic
&&
391 src_sc
!= spv::StorageClass::Image
&&
392 src_sc
!= spv::StorageClass::StorageBuffer
&&
393 src_sc
!= spv::StorageClass::PhysicalStorageBuffer
) {
394 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
395 << "NonPrivatePointerKHR requires a pointer in Uniform, "
396 << "Workgroup, CrossWorkgroup, Generic, Image or StorageBuffer "
397 << "storage classes.";
401 if (!(mask
& uint32_t(spv::MemoryAccessMask::Aligned
))) {
402 if (src_sc
== spv::StorageClass::PhysicalStorageBuffer
||
403 dst_sc
== spv::StorageClass::PhysicalStorageBuffer
) {
404 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
406 << "Memory accesses with PhysicalStorageBuffer must use Aligned.";
413 spv_result_t
ValidateVariable(ValidationState_t
& _
, const Instruction
* inst
) {
414 const bool untyped_pointer
= inst
->opcode() == spv::Op::OpUntypedVariableKHR
;
416 auto result_type
= _
.FindDef(inst
->type_id());
417 if (untyped_pointer
) {
419 result_type
->opcode() != spv::Op::OpTypeUntypedPointerKHR
)
420 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
421 << "Result type must be an untyped pointer";
423 if (!result_type
|| result_type
->opcode() != spv::Op::OpTypePointer
) {
424 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
425 << "OpVariable Result Type <id> " << _
.getIdName(inst
->type_id())
426 << " is not a pointer type.";
430 const auto storage_class_index
= 2u;
432 inst
->GetOperandAs
<spv::StorageClass
>(storage_class_index
);
433 uint32_t value_id
= 0;
434 if (untyped_pointer
) {
435 const auto has_data_type
= 3u < inst
->operands().size();
437 value_id
= inst
->GetOperandAs
<uint32_t>(3u);
438 auto data_type
= _
.FindDef(value_id
);
439 if (!data_type
|| !spvOpcodeGeneratesType(data_type
->opcode())) {
440 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
441 << "Data type must be a type instruction";
444 if (storage_class
== spv::StorageClass::Function
||
445 storage_class
== spv::StorageClass::Private
||
446 storage_class
== spv::StorageClass::Workgroup
) {
447 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
448 << "Data type must be specified for Function, Private, and "
449 "Workgroup storage classes";
451 if (spvIsVulkanEnv(_
.context()->target_env
)) {
452 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
453 << "Vulkan requires that data type be specified";
458 // For OpVariable the data type comes from pointee type of the result type,
459 // while for OpUntypedVariableKHR the data type comes from the operand.
460 if (!untyped_pointer
) {
461 value_id
= result_type
->GetOperandAs
<uint32_t>(2);
463 auto value_type
= value_id
== 0 ? nullptr : _
.FindDef(value_id
);
465 const auto initializer_index
= untyped_pointer
? 4u : 3u;
466 if (initializer_index
< inst
->operands().size()) {
467 const auto initializer_id
= inst
->GetOperandAs
<uint32_t>(initializer_index
);
468 const auto initializer
= _
.FindDef(initializer_id
);
469 const auto is_module_scope_var
=
471 (initializer
->opcode() == spv::Op::OpVariable
||
472 initializer
->opcode() == spv::Op::OpUntypedVariableKHR
) &&
473 (initializer
->GetOperandAs
<spv::StorageClass
>(storage_class_index
) !=
474 spv::StorageClass::Function
);
475 const auto is_constant
=
476 initializer
&& spvOpcodeIsConstant(initializer
->opcode());
477 if (!initializer
|| !(is_constant
|| is_module_scope_var
)) {
478 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
479 << "Variable Initializer <id> " << _
.getIdName(initializer_id
)
480 << " is not a constant or module-scope variable.";
482 if (initializer
->type_id() != value_id
) {
483 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
484 << "Initializer type must match the data type";
488 if (storage_class
!= spv::StorageClass::Workgroup
&&
489 storage_class
!= spv::StorageClass::CrossWorkgroup
&&
490 storage_class
!= spv::StorageClass::Private
&&
491 storage_class
!= spv::StorageClass::Function
&&
492 storage_class
!= spv::StorageClass::UniformConstant
&&
493 storage_class
!= spv::StorageClass::RayPayloadKHR
&&
494 storage_class
!= spv::StorageClass::IncomingRayPayloadKHR
&&
495 storage_class
!= spv::StorageClass::HitAttributeKHR
&&
496 storage_class
!= spv::StorageClass::CallableDataKHR
&&
497 storage_class
!= spv::StorageClass::IncomingCallableDataKHR
&&
498 storage_class
!= spv::StorageClass::TaskPayloadWorkgroupEXT
&&
499 storage_class
!= spv::StorageClass::HitObjectAttributeNV
) {
500 bool storage_input_or_output
= storage_class
== spv::StorageClass::Input
||
501 storage_class
== spv::StorageClass::Output
;
502 bool builtin
= false;
503 if (storage_input_or_output
) {
504 for (const Decoration
& decoration
: _
.id_decorations(inst
->id())) {
505 if (decoration
.dec_type() == spv::Decoration::BuiltIn
) {
511 if (!builtin
&& value_type
&&
512 ContainsInvalidBool(_
, value_type
, storage_input_or_output
)) {
513 if (storage_input_or_output
) {
514 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
516 << "If OpTypeBool is stored in conjunction with OpVariable "
517 "using Input or Output Storage Classes it requires a BuiltIn "
521 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
522 << "If OpTypeBool is stored in conjunction with OpVariable, it "
523 "can only be used with non-externally visible shader Storage "
524 "Classes: Workgroup, CrossWorkgroup, Private, Function, "
525 "Input, Output, RayPayloadKHR, IncomingRayPayloadKHR, "
526 "HitAttributeKHR, CallableDataKHR, "
527 "IncomingCallableDataKHR, or UniformConstant";
532 if (!_
.IsValidStorageClass(storage_class
)) {
533 return _
.diag(SPV_ERROR_INVALID_BINARY
, inst
)
535 << "Invalid storage class for target environment";
538 if (storage_class
== spv::StorageClass::Generic
) {
539 return _
.diag(SPV_ERROR_INVALID_BINARY
, inst
)
540 << "Variable storage class cannot be Generic";
543 if (inst
->function() && storage_class
!= spv::StorageClass::Function
) {
544 return _
.diag(SPV_ERROR_INVALID_LAYOUT
, inst
)
545 << "Variables must have a function[7] storage class inside"
549 if (!inst
->function() && storage_class
== spv::StorageClass::Function
) {
550 return _
.diag(SPV_ERROR_INVALID_LAYOUT
, inst
)
551 << "Variables can not have a function[7] storage class "
552 "outside of a function";
555 // SPIR-V 3.32.8: Check that pointer type and variable type have the same
557 const auto result_storage_class_index
= 1;
558 const auto result_storage_class
=
559 result_type
->GetOperandAs
<spv::StorageClass
>(result_storage_class_index
);
560 if (storage_class
!= result_storage_class
) {
561 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
562 << "Storage class must match result type storage class";
565 // Variable pointer related restrictions.
566 const auto pointee
= untyped_pointer
567 ? value_id
== 0 ? nullptr : _
.FindDef(value_id
)
568 : _
.FindDef(result_type
->word(3));
569 if (_
.addressing_model() == spv::AddressingModel::Logical
&&
570 !_
.options()->relax_logical_pointer
) {
571 // VariablePointersStorageBuffer is implied by VariablePointers.
572 if (pointee
&& pointee
->opcode() == spv::Op::OpTypePointer
) {
573 if (!_
.HasCapability(spv::Capability::VariablePointersStorageBuffer
)) {
574 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
575 << "In Logical addressing, variables may not allocate a pointer "
577 } else if (storage_class
!= spv::StorageClass::Function
&&
578 storage_class
!= spv::StorageClass::Private
) {
579 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
580 << "In Logical addressing with variable pointers, variables "
581 << "that allocate pointers must be in Function or Private "
582 << "storage classes";
587 if (spvIsVulkanEnv(_
.context()->target_env
)) {
588 // Vulkan Push Constant Interface section: Check type of PushConstant
590 if (storage_class
== spv::StorageClass::PushConstant
) {
591 if (pointee
&& pointee
->opcode() != spv::Op::OpTypeStruct
) {
592 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
593 << _
.VkErrorID(6808) << "PushConstant OpVariable <id> "
594 << _
.getIdName(inst
->id()) << " has illegal type.\n"
595 << "From Vulkan spec, Push Constant Interface section:\n"
596 << "Such variables must be typed as OpTypeStruct";
600 // Vulkan Descriptor Set Interface: Check type of UniformConstant and
601 // Uniform variables.
602 if (storage_class
== spv::StorageClass::UniformConstant
) {
603 if (pointee
&& !IsAllowedTypeOrArrayOfSame(
605 {spv::Op::OpTypeImage
, spv::Op::OpTypeSampler
,
606 spv::Op::OpTypeSampledImage
,
607 spv::Op::OpTypeAccelerationStructureKHR
})) {
608 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
609 << _
.VkErrorID(4655) << "UniformConstant OpVariable <id> "
610 << _
.getIdName(inst
->id()) << " has illegal type.\n"
611 << "Variables identified with the UniformConstant storage class "
612 << "are used only as handles to refer to opaque resources. Such "
613 << "variables must be typed as OpTypeImage, OpTypeSampler, "
614 << "OpTypeSampledImage, OpTypeAccelerationStructureKHR, "
615 << "or an array of one of these types.";
619 if (storage_class
== spv::StorageClass::Uniform
) {
621 !IsAllowedTypeOrArrayOfSame(_
, pointee
, {spv::Op::OpTypeStruct
})) {
622 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
623 << _
.VkErrorID(6807) << "Uniform OpVariable <id> "
624 << _
.getIdName(inst
->id()) << " has illegal type.\n"
625 << "From Vulkan spec:\n"
626 << "Variables identified with the Uniform storage class are "
627 << "used to access transparent buffer backed resources. Such "
628 << "variables must be typed as OpTypeStruct, or an array of "
633 if (storage_class
== spv::StorageClass::StorageBuffer
) {
635 !IsAllowedTypeOrArrayOfSame(_
, pointee
, {spv::Op::OpTypeStruct
})) {
636 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
637 << _
.VkErrorID(6807) << "StorageBuffer OpVariable <id> "
638 << _
.getIdName(inst
->id()) << " has illegal type.\n"
639 << "From Vulkan spec:\n"
640 << "Variables identified with the StorageBuffer storage class "
641 "are used to access transparent buffer backed resources. "
642 "Such variables must be typed as OpTypeStruct, or an array "
647 // Check for invalid use of Invariant
648 if (storage_class
!= spv::StorageClass::Input
&&
649 storage_class
!= spv::StorageClass::Output
) {
650 if (_
.HasDecoration(inst
->id(), spv::Decoration::Invariant
)) {
651 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
653 << "Variable decorated with Invariant must only be identified "
654 "with the Input or Output storage class in Vulkan "
657 // Need to check if only the members in a struct are decorated
658 if (value_type
&& value_type
->opcode() == spv::Op::OpTypeStruct
) {
659 if (_
.HasDecoration(value_id
, spv::Decoration::Invariant
)) {
660 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
662 << "Variable struct member decorated with Invariant must only "
663 "be identified with the Input or Output storage class in "
664 "Vulkan environment.";
670 // Vulkan Appendix A: Check that if contains initializer, then
671 // storage class is Output, Private, or Function.
672 if (inst
->operands().size() > initializer_index
&&
673 storage_class
!= spv::StorageClass::Output
&&
674 storage_class
!= spv::StorageClass::Private
&&
675 storage_class
!= spv::StorageClass::Function
) {
676 if (spvIsVulkanEnv(_
.context()->target_env
)) {
677 if (storage_class
== spv::StorageClass::Workgroup
) {
678 auto init_id
= inst
->GetOperandAs
<uint32_t>(initializer_index
);
679 auto init
= _
.FindDef(init_id
);
680 if (init
->opcode() != spv::Op::OpConstantNull
) {
681 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
682 << _
.VkErrorID(4734) << "OpVariable, <id> "
683 << _
.getIdName(inst
->id())
684 << ", initializers are limited to OpConstantNull in "
688 } else if (storage_class
!= spv::StorageClass::Output
&&
689 storage_class
!= spv::StorageClass::Private
&&
690 storage_class
!= spv::StorageClass::Function
) {
691 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
692 << _
.VkErrorID(4651) << "OpVariable, <id> "
693 << _
.getIdName(inst
->id())
694 << ", has a disallowed initializer & storage class "
696 << "From " << spvLogStringForEnv(_
.context()->target_env
)
698 << "Variable declarations that include initializers must have "
699 << "one of the following storage classes: Output, Private, "
700 << "Function or Workgroup";
705 if (initializer_index
< inst
->operands().size()) {
706 if (storage_class
== spv::StorageClass::TaskPayloadWorkgroupEXT
) {
707 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
708 << "OpVariable, <id> " << _
.getIdName(inst
->id())
709 << ", initializer are not allowed for TaskPayloadWorkgroupEXT";
711 if (storage_class
== spv::StorageClass::Input
) {
712 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
713 << "OpVariable, <id> " << _
.getIdName(inst
->id())
714 << ", initializer are not allowed for Input";
716 if (storage_class
== spv::StorageClass::HitObjectAttributeNV
) {
717 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
718 << "OpVariable, <id> " << _
.getIdName(inst
->id())
719 << ", initializer are not allowed for HitObjectAttributeNV";
723 if (storage_class
== spv::StorageClass::PhysicalStorageBuffer
) {
724 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
725 << "PhysicalStorageBuffer must not be used with OpVariable.";
728 auto pointee_base
= pointee
;
729 while (pointee_base
&& pointee_base
->opcode() == spv::Op::OpTypeArray
) {
730 pointee_base
= _
.FindDef(pointee_base
->GetOperandAs
<uint32_t>(1u));
732 if (pointee_base
&& pointee_base
->opcode() == spv::Op::OpTypePointer
) {
733 if (pointee_base
->GetOperandAs
<spv::StorageClass
>(1u) ==
734 spv::StorageClass::PhysicalStorageBuffer
) {
735 // check for AliasedPointer/RestrictPointer
737 _
.HasDecoration(inst
->id(), spv::Decoration::AliasedPointer
);
739 _
.HasDecoration(inst
->id(), spv::Decoration::RestrictPointer
);
740 if (!foundAliased
&& !foundRestrict
) {
741 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
742 << "OpVariable " << inst
->id()
743 << ": expected AliasedPointer or RestrictPointer for "
744 << "PhysicalStorageBuffer pointer.";
746 if (foundAliased
&& foundRestrict
) {
747 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
748 << "OpVariable " << inst
->id()
749 << ": can't specify both AliasedPointer and "
750 << "RestrictPointer for PhysicalStorageBuffer pointer.";
755 // Vulkan specific validation rules for OpTypeRuntimeArray
756 if (spvIsVulkanEnv(_
.context()->target_env
)) {
757 // OpTypeRuntimeArray should only ever be in a container like OpTypeStruct,
758 // so should never appear as a bare variable.
759 // Unless the module has the RuntimeDescriptorArrayEXT capability.
760 if (value_type
&& value_type
->opcode() == spv::Op::OpTypeRuntimeArray
) {
761 if (!_
.HasCapability(spv::Capability::RuntimeDescriptorArrayEXT
)) {
762 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
763 << _
.VkErrorID(4680) << "OpVariable, <id> "
764 << _
.getIdName(inst
->id())
765 << ", is attempting to create memory for an illegal type, "
766 << "OpTypeRuntimeArray.\nFor Vulkan OpTypeRuntimeArray can only "
767 << "appear as the final member of an OpTypeStruct, thus cannot "
768 << "be instantiated via OpVariable";
770 // A bare variable OpTypeRuntimeArray is allowed in this context, but
771 // still need to check the storage class.
772 if (storage_class
!= spv::StorageClass::StorageBuffer
&&
773 storage_class
!= spv::StorageClass::Uniform
&&
774 storage_class
!= spv::StorageClass::UniformConstant
) {
775 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
777 << "For Vulkan with RuntimeDescriptorArrayEXT, a variable "
778 << "containing OpTypeRuntimeArray must have storage class of "
779 << "StorageBuffer, Uniform, or UniformConstant.";
784 // If an OpStruct has an OpTypeRuntimeArray somewhere within it, then it
785 // must either have the storage class StorageBuffer and be decorated
786 // with Block, or it must be in the Uniform storage class and be decorated
788 if (value_type
&& value_type
->opcode() == spv::Op::OpTypeStruct
) {
789 if (DoesStructContainRTA(_
, value_type
)) {
790 if (storage_class
== spv::StorageClass::StorageBuffer
||
791 storage_class
== spv::StorageClass::PhysicalStorageBuffer
) {
792 if (!_
.HasDecoration(value_id
, spv::Decoration::Block
)) {
793 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
795 << "For Vulkan, an OpTypeStruct variable containing an "
796 << "OpTypeRuntimeArray must be decorated with Block if it "
797 << "has storage class StorageBuffer or "
798 "PhysicalStorageBuffer.";
800 } else if (storage_class
== spv::StorageClass::Uniform
) {
801 if (!_
.HasDecoration(value_id
, spv::Decoration::BufferBlock
)) {
802 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
804 << "For Vulkan, an OpTypeStruct variable containing an "
805 << "OpTypeRuntimeArray must be decorated with BufferBlock "
806 << "if it has storage class Uniform.";
809 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
811 << "For Vulkan, OpTypeStruct variables containing "
812 << "OpTypeRuntimeArray must have storage class of "
813 << "StorageBuffer, PhysicalStorageBuffer, or Uniform.";
819 // Cooperative matrix types can only be allocated in Function or Private
820 if ((storage_class
!= spv::StorageClass::Function
&&
821 storage_class
!= spv::StorageClass::Private
) &&
822 pointee
&& ContainsCooperativeMatrix(_
, pointee
)) {
823 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
824 << "Cooperative matrix types (or types containing them) can only be "
826 << "in Function or Private storage classes or as function "
830 if (_
.HasCapability(spv::Capability::Shader
)) {
831 // Don't allow variables containing 16-bit elements without the appropriate
833 if ((!_
.HasCapability(spv::Capability::Int16
) &&
834 _
.ContainsSizedIntOrFloatType(value_id
, spv::Op::OpTypeInt
, 16)) ||
835 (!_
.HasCapability(spv::Capability::Float16
) &&
836 _
.ContainsSizedIntOrFloatType(value_id
, spv::Op::OpTypeFloat
, 16))) {
837 auto underlying_type
= value_type
;
838 while (underlying_type
&&
839 underlying_type
->opcode() == spv::Op::OpTypePointer
) {
840 storage_class
= underlying_type
->GetOperandAs
<spv::StorageClass
>(1u);
842 _
.FindDef(underlying_type
->GetOperandAs
<uint32_t>(2u));
844 bool storage_class_ok
= true;
845 std::string sc_name
= _
.grammar().lookupOperandName(
846 SPV_OPERAND_TYPE_STORAGE_CLASS
, uint32_t(storage_class
));
847 switch (storage_class
) {
848 case spv::StorageClass::StorageBuffer
:
849 case spv::StorageClass::PhysicalStorageBuffer
:
850 if (!_
.HasCapability(spv::Capability::StorageBuffer16BitAccess
)) {
851 storage_class_ok
= false;
854 case spv::StorageClass::Uniform
:
855 if (underlying_type
&&
857 spv::Capability::UniformAndStorageBuffer16BitAccess
)) {
858 if (underlying_type
->opcode() == spv::Op::OpTypeArray
||
859 underlying_type
->opcode() == spv::Op::OpTypeRuntimeArray
) {
861 _
.FindDef(underlying_type
->GetOperandAs
<uint32_t>(1u));
863 if (!_
.HasCapability(spv::Capability::StorageBuffer16BitAccess
) ||
864 !_
.HasDecoration(underlying_type
->id(),
865 spv::Decoration::BufferBlock
)) {
866 storage_class_ok
= false;
870 case spv::StorageClass::PushConstant
:
871 if (!_
.HasCapability(spv::Capability::StoragePushConstant16
)) {
872 storage_class_ok
= false;
875 case spv::StorageClass::Input
:
876 case spv::StorageClass::Output
:
877 if (!_
.HasCapability(spv::Capability::StorageInputOutput16
)) {
878 storage_class_ok
= false;
881 case spv::StorageClass::Workgroup
:
882 if (!_
.HasCapability(
884 WorkgroupMemoryExplicitLayout16BitAccessKHR
)) {
885 storage_class_ok
= false;
889 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
890 << "Cannot allocate a variable containing a 16-bit type in "
891 << sc_name
<< " storage class";
893 if (!storage_class_ok
) {
894 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
895 << "Allocating a variable containing a 16-bit element in "
896 << sc_name
<< " storage class requires an additional capability";
899 // Don't allow variables containing 8-bit elements without the appropriate
901 if (!_
.HasCapability(spv::Capability::Int8
) &&
902 _
.ContainsSizedIntOrFloatType(value_id
, spv::Op::OpTypeInt
, 8)) {
903 auto underlying_type
= value_type
;
904 while (underlying_type
&&
905 underlying_type
->opcode() == spv::Op::OpTypePointer
) {
906 storage_class
= underlying_type
->GetOperandAs
<spv::StorageClass
>(1u);
908 _
.FindDef(underlying_type
->GetOperandAs
<uint32_t>(2u));
910 bool storage_class_ok
= true;
911 std::string sc_name
= _
.grammar().lookupOperandName(
912 SPV_OPERAND_TYPE_STORAGE_CLASS
, uint32_t(storage_class
));
913 switch (storage_class
) {
914 case spv::StorageClass::StorageBuffer
:
915 case spv::StorageClass::PhysicalStorageBuffer
:
916 if (!_
.HasCapability(spv::Capability::StorageBuffer8BitAccess
)) {
917 storage_class_ok
= false;
920 case spv::StorageClass::Uniform
:
921 if (underlying_type
&&
923 spv::Capability::UniformAndStorageBuffer8BitAccess
)) {
924 if (underlying_type
->opcode() == spv::Op::OpTypeArray
||
925 underlying_type
->opcode() == spv::Op::OpTypeRuntimeArray
) {
927 _
.FindDef(underlying_type
->GetOperandAs
<uint32_t>(1u));
929 if (!_
.HasCapability(spv::Capability::StorageBuffer8BitAccess
) ||
930 !_
.HasDecoration(underlying_type
->id(),
931 spv::Decoration::BufferBlock
)) {
932 storage_class_ok
= false;
936 case spv::StorageClass::PushConstant
:
937 if (!_
.HasCapability(spv::Capability::StoragePushConstant8
)) {
938 storage_class_ok
= false;
941 case spv::StorageClass::Workgroup
:
942 if (!_
.HasCapability(
944 WorkgroupMemoryExplicitLayout8BitAccessKHR
)) {
945 storage_class_ok
= false;
949 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
950 << "Cannot allocate a variable containing a 8-bit type in "
951 << sc_name
<< " storage class";
953 if (!storage_class_ok
) {
954 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
955 << "Allocating a variable containing a 8-bit element in "
956 << sc_name
<< " storage class requires an additional capability";
964 spv_result_t
ValidateLoad(ValidationState_t
& _
, const Instruction
* inst
) {
965 const auto result_type
= _
.FindDef(inst
->type_id());
967 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
968 << "OpLoad Result Type <id> " << _
.getIdName(inst
->type_id())
969 << " is not defined.";
972 const auto pointer_index
= 2;
973 const auto pointer_id
= inst
->GetOperandAs
<uint32_t>(pointer_index
);
974 const auto pointer
= _
.FindDef(pointer_id
);
976 ((_
.addressing_model() == spv::AddressingModel::Logical
) &&
977 ((!_
.features().variable_pointers
&&
978 !spvOpcodeReturnsLogicalPointer(pointer
->opcode())) ||
979 (_
.features().variable_pointers
&&
980 !spvOpcodeReturnsLogicalVariablePointer(pointer
->opcode()))))) {
981 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
982 << "OpLoad Pointer <id> " << _
.getIdName(pointer_id
)
983 << " is not a logical pointer.";
986 const auto pointer_type
= _
.FindDef(pointer
->type_id());
988 (pointer_type
->opcode() != spv::Op::OpTypePointer
&&
989 pointer_type
->opcode() != spv::Op::OpTypeUntypedPointerKHR
)) {
990 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
991 << "OpLoad type for pointer <id> " << _
.getIdName(pointer_id
)
992 << " is not a pointer type.";
995 if (pointer_type
->opcode() == spv::Op::OpTypePointer
) {
996 const auto pointee_type
=
997 _
.FindDef(pointer_type
->GetOperandAs
<uint32_t>(2));
998 if (!pointee_type
|| result_type
->id() != pointee_type
->id()) {
999 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
1000 << "OpLoad Result Type <id> " << _
.getIdName(inst
->type_id())
1001 << " does not match Pointer <id> " << _
.getIdName(pointer
->id())
1006 if (!_
.options()->before_hlsl_legalization
&&
1007 _
.ContainsRuntimeArray(inst
->type_id())) {
1008 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
1009 << "Cannot load a runtime-sized array";
1012 if (auto error
= CheckMemoryAccess(_
, inst
, 3)) return error
;
1014 if (_
.HasCapability(spv::Capability::Shader
) &&
1015 _
.ContainsLimitedUseIntOrFloatType(inst
->type_id()) &&
1016 result_type
->opcode() != spv::Op::OpTypePointer
) {
1017 if (result_type
->opcode() != spv::Op::OpTypeInt
&&
1018 result_type
->opcode() != spv::Op::OpTypeFloat
&&
1019 result_type
->opcode() != spv::Op::OpTypeVector
&&
1020 result_type
->opcode() != spv::Op::OpTypeMatrix
) {
1021 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
1022 << "8- or 16-bit loads must be a scalar, vector or matrix type";
1026 _
.RegisterQCOMImageProcessingTextureConsumer(pointer_id
, inst
, nullptr);
1031 spv_result_t
ValidateStore(ValidationState_t
& _
, const Instruction
* inst
) {
1032 const auto pointer_index
= 0;
1033 const auto pointer_id
= inst
->GetOperandAs
<uint32_t>(pointer_index
);
1034 const auto pointer
= _
.FindDef(pointer_id
);
1036 (_
.addressing_model() == spv::AddressingModel::Logical
&&
1037 ((!_
.features().variable_pointers
&&
1038 !spvOpcodeReturnsLogicalPointer(pointer
->opcode())) ||
1039 (_
.features().variable_pointers
&&
1040 !spvOpcodeReturnsLogicalVariablePointer(pointer
->opcode()))))) {
1041 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
1042 << "OpStore Pointer <id> " << _
.getIdName(pointer_id
)
1043 << " is not a logical pointer.";
1045 const auto pointer_type
= _
.FindDef(pointer
->type_id());
1046 if (!pointer_type
||
1047 (pointer_type
->opcode() != spv::Op::OpTypePointer
&&
1048 pointer_type
->opcode() != spv::Op::OpTypeUntypedPointerKHR
)) {
1049 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
1050 << "OpStore type for pointer <id> " << _
.getIdName(pointer_id
)
1051 << " is not a pointer type.";
1054 Instruction
* type
= nullptr;
1055 if (pointer_type
->opcode() == spv::Op::OpTypePointer
) {
1056 const auto type_id
= pointer_type
->GetOperandAs
<uint32_t>(2);
1057 type
= _
.FindDef(type_id
);
1058 if (!type
|| spv::Op::OpTypeVoid
== type
->opcode()) {
1059 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
1060 << "OpStore Pointer <id> " << _
.getIdName(pointer_id
)
1061 << "s type is void.";
1065 // validate storage class
1068 spv::StorageClass storage_class
;
1069 if (!_
.GetPointerTypeInfo(pointer_type
->id(), &data_type
, &storage_class
)) {
1070 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
1071 << "OpStore Pointer <id> " << _
.getIdName(pointer_id
)
1072 << " is not pointer type";
1075 if (storage_class
== spv::StorageClass::UniformConstant
||
1076 storage_class
== spv::StorageClass::Input
||
1077 storage_class
== spv::StorageClass::PushConstant
) {
1078 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
1079 << "OpStore Pointer <id> " << _
.getIdName(pointer_id
)
1080 << " storage class is read-only";
1081 } else if (storage_class
== spv::StorageClass::ShaderRecordBufferKHR
) {
1082 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
1083 << "ShaderRecordBufferKHR Storage Class variables are read only";
1084 } else if (storage_class
== spv::StorageClass::HitAttributeKHR
) {
1085 std::string errorVUID
= _
.VkErrorID(4703);
1086 _
.function(inst
->function()->id())
1087 ->RegisterExecutionModelLimitation(
1088 [errorVUID
](spv::ExecutionModel model
, std::string
* message
) {
1089 if (model
== spv::ExecutionModel::AnyHitKHR
||
1090 model
== spv::ExecutionModel::ClosestHitKHR
) {
1094 "HitAttributeKHR Storage Class variables are read only "
1095 "with AnyHitKHR and ClosestHitKHR";
1103 if (spvIsVulkanEnv(_
.context()->target_env
) &&
1104 storage_class
== spv::StorageClass::Uniform
) {
1105 auto base_ptr
= _
.TracePointer(pointer
);
1106 if (base_ptr
->opcode() == spv::Op::OpVariable
) {
1107 // If it's not a variable a different check should catch the problem.
1108 auto base_type
= _
.FindDef(base_ptr
->GetOperandAs
<uint32_t>(0));
1109 // Get the pointed-to type.
1110 base_type
= _
.FindDef(base_type
->GetOperandAs
<uint32_t>(2u));
1111 if (base_type
->opcode() == spv::Op::OpTypeArray
||
1112 base_type
->opcode() == spv::Op::OpTypeRuntimeArray
) {
1113 base_type
= _
.FindDef(base_type
->GetOperandAs
<uint32_t>(1u));
1115 if (_
.HasDecoration(base_type
->id(), spv::Decoration::Block
)) {
1116 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
1117 << _
.VkErrorID(6925)
1118 << "In the Vulkan environment, cannot store to Uniform Blocks";
1124 const auto object_index
= 1;
1125 const auto object_id
= inst
->GetOperandAs
<uint32_t>(object_index
);
1126 const auto object
= _
.FindDef(object_id
);
1127 if (!object
|| !object
->type_id()) {
1128 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
1129 << "OpStore Object <id> " << _
.getIdName(object_id
)
1130 << " is not an object.";
1132 const auto object_type
= _
.FindDef(object
->type_id());
1133 if (!object_type
|| spv::Op::OpTypeVoid
== object_type
->opcode()) {
1134 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
1135 << "OpStore Object <id> " << _
.getIdName(object_id
)
1136 << "s type is void.";
1139 if (type
&& (type
->id() != object_type
->id())) {
1140 if (!_
.options()->relax_struct_store
||
1141 type
->opcode() != spv::Op::OpTypeStruct
||
1142 object_type
->opcode() != spv::Op::OpTypeStruct
) {
1143 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
1144 << "OpStore Pointer <id> " << _
.getIdName(pointer_id
)
1145 << "s type does not match Object <id> "
1146 << _
.getIdName(object
->id()) << "s type.";
1149 // TODO: Check for layout compatible matricies and arrays as well.
1150 if (!AreLayoutCompatibleStructs(_
, type
, object_type
)) {
1151 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
1152 << "OpStore Pointer <id> " << _
.getIdName(pointer_id
)
1153 << "s layout does not match Object <id> "
1154 << _
.getIdName(object
->id()) << "s layout.";
1158 if (auto error
= CheckMemoryAccess(_
, inst
, 2)) return error
;
1160 if (_
.HasCapability(spv::Capability::Shader
) &&
1161 _
.ContainsLimitedUseIntOrFloatType(inst
->type_id()) &&
1162 object_type
->opcode() != spv::Op::OpTypePointer
) {
1163 if (object_type
->opcode() != spv::Op::OpTypeInt
&&
1164 object_type
->opcode() != spv::Op::OpTypeFloat
&&
1165 object_type
->opcode() != spv::Op::OpTypeVector
&&
1166 object_type
->opcode() != spv::Op::OpTypeMatrix
) {
1167 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
1168 << "8- or 16-bit stores must be a scalar, vector or matrix type";
1172 if (spvIsVulkanEnv(_
.context()->target_env
) &&
1173 !_
.options()->before_hlsl_legalization
) {
1174 const auto isForbiddenType
= [](const Instruction
* type_inst
) {
1175 auto opcode
= type_inst
->opcode();
1176 return opcode
== spv::Op::OpTypeImage
||
1177 opcode
== spv::Op::OpTypeSampler
||
1178 opcode
== spv::Op::OpTypeSampledImage
||
1179 opcode
== spv::Op::OpTypeAccelerationStructureKHR
;
1181 if (_
.ContainsType(object_type
->id(), isForbiddenType
)) {
1182 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
1183 << _
.VkErrorID(6924)
1184 << "Cannot store to OpTypeImage, OpTypeSampler, "
1185 "OpTypeSampledImage, or OpTypeAccelerationStructureKHR objects";
1192 spv_result_t
ValidateCopyMemoryMemoryAccess(ValidationState_t
& _
,
1193 const Instruction
* inst
) {
1194 assert(inst
->opcode() == spv::Op::OpCopyMemory
||
1195 inst
->opcode() == spv::Op::OpCopyMemorySized
);
1196 const uint32_t first_access_index
=
1197 inst
->opcode() == spv::Op::OpCopyMemory
? 2 : 3;
1198 if (inst
->operands().size() > first_access_index
) {
1199 if (auto error
= CheckMemoryAccess(_
, inst
, first_access_index
))
1202 const auto first_access
= inst
->GetOperandAs
<uint32_t>(first_access_index
);
1203 const uint32_t second_access_index
=
1204 first_access_index
+ MemoryAccessNumWords(first_access
);
1205 if (inst
->operands().size() > second_access_index
) {
1206 if (_
.features().copy_memory_permits_two_memory_accesses
) {
1207 if (auto error
= CheckMemoryAccess(_
, inst
, second_access_index
))
1210 // In the two-access form in SPIR-V 1.4 and later:
1211 // - the first is the target (write) access and it can't have
1213 // - the second is the source (read) access and it can't have
1216 uint32_t(spv::MemoryAccessMask::MakePointerVisibleKHR
)) {
1217 return _
.diag(SPV_ERROR_INVALID_DATA
, inst
)
1218 << "Target memory access must not include "
1219 "MakePointerVisibleKHR";
1221 const auto second_access
=
1222 inst
->GetOperandAs
<uint32_t>(second_access_index
);
1224 uint32_t(spv::MemoryAccessMask::MakePointerAvailableKHR
)) {
1225 return _
.diag(SPV_ERROR_INVALID_DATA
, inst
)
1226 << "Source memory access must not include "
1227 "MakePointerAvailableKHR";
1230 return _
.diag(SPV_ERROR_INVALID_DATA
, inst
)
1231 << spvOpcodeString(static_cast<spv::Op
>(inst
->opcode()))
1232 << " with two memory access operands requires SPIR-V 1.4 or "
1240 spv_result_t
ValidateCopyMemory(ValidationState_t
& _
, const Instruction
* inst
) {
1241 const auto target_index
= 0;
1242 const auto target_id
= inst
->GetOperandAs
<uint32_t>(target_index
);
1243 const auto target
= _
.FindDef(target_id
);
1245 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
1246 << "Target operand <id> " << _
.getIdName(target_id
)
1247 << " is not defined.";
1250 const auto source_index
= 1;
1251 const auto source_id
= inst
->GetOperandAs
<uint32_t>(source_index
);
1252 const auto source
= _
.FindDef(source_id
);
1254 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
1255 << "Source operand <id> " << _
.getIdName(source_id
)
1256 << " is not defined.";
1259 const auto target_pointer_type
= _
.FindDef(target
->type_id());
1260 if (!target_pointer_type
||
1261 (target_pointer_type
->opcode() != spv::Op::OpTypePointer
&&
1262 target_pointer_type
->opcode() != spv::Op::OpTypeUntypedPointerKHR
)) {
1263 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
1264 << "Target operand <id> " << _
.getIdName(target_id
)
1265 << " is not a pointer.";
1268 const auto source_pointer_type
= _
.FindDef(source
->type_id());
1269 if (!source_pointer_type
||
1270 (source_pointer_type
->opcode() != spv::Op::OpTypePointer
&&
1271 source_pointer_type
->opcode() != spv::Op::OpTypeUntypedPointerKHR
)) {
1272 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
1273 << "Source operand <id> " << _
.getIdName(source_id
)
1274 << " is not a pointer.";
1277 if (inst
->opcode() == spv::Op::OpCopyMemory
) {
1278 const bool target_typed
=
1279 target_pointer_type
->opcode() == spv::Op::OpTypePointer
;
1280 const bool source_typed
=
1281 source_pointer_type
->opcode() == spv::Op::OpTypePointer
;
1282 Instruction
* target_type
= nullptr;
1283 Instruction
* source_type
= nullptr;
1285 target_type
= _
.FindDef(target_pointer_type
->GetOperandAs
<uint32_t>(2));
1287 if (!target_type
|| target_type
->opcode() == spv::Op::OpTypeVoid
) {
1288 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
1289 << "Target operand <id> " << _
.getIdName(target_id
)
1290 << " cannot be a void pointer.";
1295 source_type
= _
.FindDef(source_pointer_type
->GetOperandAs
<uint32_t>(2));
1296 if (!source_type
|| source_type
->opcode() == spv::Op::OpTypeVoid
) {
1297 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
1298 << "Source operand <id> " << _
.getIdName(source_id
)
1299 << " cannot be a void pointer.";
1303 if (target_type
&& source_type
&& target_type
->id() != source_type
->id()) {
1304 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
1305 << "Target <id> " << _
.getIdName(source_id
)
1306 << "s type does not match Source <id> "
1307 << _
.getIdName(source_type
->id()) << "s type.";
1310 if (!target_type
&& !source_type
) {
1311 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
1312 << "One of Source or Target must be a typed pointer";
1315 if (auto error
= CheckMemoryAccess(_
, inst
, 2)) return error
;
1317 const auto size_id
= inst
->GetOperandAs
<uint32_t>(2);
1318 const auto size
= _
.FindDef(size_id
);
1320 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
1321 << "Size operand <id> " << _
.getIdName(size_id
)
1322 << " is not defined.";
1325 const auto size_type
= _
.FindDef(size
->type_id());
1326 if (!_
.IsIntScalarType(size_type
->id())) {
1327 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
1328 << "Size operand <id> " << _
.getIdName(size_id
)
1329 << " must be a scalar integer type.";
1331 bool is_zero
= true;
1332 switch (size
->opcode()) {
1333 case spv::Op::OpConstantNull
:
1334 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
1335 << "Size operand <id> " << _
.getIdName(size_id
)
1336 << " cannot be a constant zero.";
1337 case spv::Op::OpConstant
:
1338 if (size_type
->word(3) == 1 &&
1339 size
->word(size
->words().size() - 1) & 0x80000000) {
1340 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
1341 << "Size operand <id> " << _
.getIdName(size_id
)
1342 << " cannot have the sign bit set to 1.";
1344 for (size_t i
= 3; is_zero
&& i
< size
->words().size(); ++i
) {
1345 is_zero
&= (size
->word(i
) == 0);
1348 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
1349 << "Size operand <id> " << _
.getIdName(size_id
)
1350 << " cannot be a constant zero.";
1354 // Cannot infer any other opcodes.
1358 if (_
.HasCapability(spv::Capability::Shader
)) {
1359 bool is_int
= false;
1360 bool is_const
= false;
1362 std::tie(is_int
, is_const
, value
) = _
.EvalInt32IfConst(size_id
);
1364 if (value
% 4 != 0) {
1365 const auto source_sc
=
1366 source_pointer_type
->GetOperandAs
<spv::StorageClass
>(1);
1367 const auto target_sc
=
1368 target_pointer_type
->GetOperandAs
<spv::StorageClass
>(1);
1369 const bool int8
= _
.HasCapability(spv::Capability::Int8
);
1370 const bool ubo_int8
= _
.HasCapability(
1371 spv::Capability::UniformAndStorageBuffer8BitAccess
);
1372 const bool ssbo_int8
=
1373 _
.HasCapability(spv::Capability::StorageBuffer8BitAccess
) ||
1375 const bool pc_int8
=
1376 _
.HasCapability(spv::Capability::StoragePushConstant8
);
1377 const bool wg_int8
= _
.HasCapability(
1378 spv::Capability::WorkgroupMemoryExplicitLayout8BitAccessKHR
);
1379 const bool int16
= _
.HasCapability(spv::Capability::Int16
) || int8
;
1380 const bool ubo_int16
=
1382 spv::Capability::UniformAndStorageBuffer16BitAccess
) ||
1384 const bool ssbo_int16
=
1385 _
.HasCapability(spv::Capability::StorageBuffer16BitAccess
) ||
1386 ubo_int16
|| ssbo_int8
;
1387 const bool pc_int16
=
1388 _
.HasCapability(spv::Capability::StoragePushConstant16
) ||
1390 const bool io_int16
=
1391 _
.HasCapability(spv::Capability::StorageInputOutput16
);
1392 const bool wg_int16
= _
.HasCapability(
1393 spv::Capability::WorkgroupMemoryExplicitLayout16BitAccessKHR
);
1395 bool source_int16_match
= false;
1396 bool target_int16_match
= false;
1397 bool source_int8_match
= false;
1398 bool target_int8_match
= false;
1399 switch (source_sc
) {
1400 case spv::StorageClass::StorageBuffer
:
1401 source_int16_match
= ssbo_int16
;
1402 source_int8_match
= ssbo_int8
;
1404 case spv::StorageClass::Uniform
:
1405 source_int16_match
= ubo_int16
;
1406 source_int8_match
= ubo_int8
;
1408 case spv::StorageClass::PushConstant
:
1409 source_int16_match
= pc_int16
;
1410 source_int8_match
= pc_int8
;
1412 case spv::StorageClass::Input
:
1413 case spv::StorageClass::Output
:
1414 source_int16_match
= io_int16
;
1416 case spv::StorageClass::Workgroup
:
1417 source_int16_match
= wg_int16
;
1418 source_int8_match
= wg_int8
;
1423 switch (target_sc
) {
1424 case spv::StorageClass::StorageBuffer
:
1425 target_int16_match
= ssbo_int16
;
1426 target_int8_match
= ssbo_int8
;
1428 case spv::StorageClass::Uniform
:
1429 target_int16_match
= ubo_int16
;
1430 target_int8_match
= ubo_int8
;
1432 case spv::StorageClass::PushConstant
:
1433 target_int16_match
= pc_int16
;
1434 target_int8_match
= pc_int8
;
1436 // Input is read-only so it cannot be the target pointer.
1437 case spv::StorageClass::Output
:
1438 target_int16_match
= io_int16
;
1440 case spv::StorageClass::Workgroup
:
1441 target_int16_match
= wg_int16
;
1442 target_int8_match
= wg_int8
;
1447 if (!int8
&& !int16
&& !(source_int16_match
&& target_int16_match
)) {
1448 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
1449 << "Size must be a multiple of 4";
1451 if (value
% 2 != 0) {
1452 if (!int8
&& !(source_int8_match
&& target_int8_match
)) {
1453 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
1454 << "Size must be a multiple of 2";
1461 if (auto error
= CheckMemoryAccess(_
, inst
, 3)) return error
;
1463 if (auto error
= ValidateCopyMemoryMemoryAccess(_
, inst
)) return error
;
1465 // Get past the pointers to avoid checking a pointer copy.
1466 if (target_pointer_type
->opcode() == spv::Op::OpTypePointer
) {
1467 auto sub_type
= _
.FindDef(target_pointer_type
->GetOperandAs
<uint32_t>(2));
1468 while (sub_type
->opcode() == spv::Op::OpTypePointer
) {
1469 sub_type
= _
.FindDef(sub_type
->GetOperandAs
<uint32_t>(2));
1471 if (_
.HasCapability(spv::Capability::Shader
) &&
1472 _
.ContainsLimitedUseIntOrFloatType(sub_type
->id())) {
1473 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
1474 << "Cannot copy memory of objects containing 8- or 16-bit types";
1481 spv_result_t
ValidateAccessChain(ValidationState_t
& _
,
1482 const Instruction
* inst
) {
1483 std::string instr_name
=
1484 "Op" + std::string(spvOpcodeString(static_cast<spv::Op
>(inst
->opcode())));
1486 const bool untyped_pointer
= spvOpcodeGeneratesUntypedPointer(inst
->opcode());
1488 // The result type must be OpTypePointer for regular access chains and an
1489 // OpTypeUntypedPointerKHR for untyped access chains.
1490 auto result_type
= _
.FindDef(inst
->type_id());
1491 if (untyped_pointer
) {
1493 spv::Op::OpTypeUntypedPointerKHR
!= result_type
->opcode()) {
1494 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
1495 << "The Result Type of " << instr_name
<< " <id> "
1496 << _
.getIdName(inst
->id())
1497 << " must be OpTypeUntypedPointerKHR. Found Op"
1498 << spvOpcodeString(static_cast<spv::Op
>(result_type
->opcode()))
1502 if (!result_type
|| spv::Op::OpTypePointer
!= result_type
->opcode()) {
1503 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
1504 << "The Result Type of " << instr_name
<< " <id> "
1505 << _
.getIdName(inst
->id()) << " must be OpTypePointer. Found Op"
1506 << spvOpcodeString(static_cast<spv::Op
>(result_type
->opcode()))
1511 if (untyped_pointer
) {
1512 // Base type must be a non-pointer type.
1513 const auto base_type
= _
.FindDef(inst
->GetOperandAs
<uint32_t>(2));
1514 if (!base_type
|| !spvOpcodeGeneratesType(base_type
->opcode()) ||
1515 base_type
->opcode() == spv::Op::OpTypePointer
||
1516 base_type
->opcode() == spv::Op::OpTypeUntypedPointerKHR
) {
1517 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
1518 << "Base type must be a non-pointer type";
1522 // Base must be a pointer, pointing to the base of a composite object.
1523 const auto base_index
= untyped_pointer
? 3 : 2;
1524 const auto base_id
= inst
->GetOperandAs
<uint32_t>(base_index
);
1525 const auto base
= _
.FindDef(base_id
);
1526 const auto base_type
= _
.FindDef(base
->type_id());
1527 if (!base_type
|| !(spv::Op::OpTypePointer
== base_type
->opcode() ||
1528 (untyped_pointer
&& spv::Op::OpTypeUntypedPointerKHR
==
1529 base_type
->opcode()))) {
1530 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
1531 << "The Base <id> " << _
.getIdName(base_id
) << " in " << instr_name
1532 << " instruction must be a pointer.";
1535 // The result pointer storage class and base pointer storage class must match.
1536 // Word 2 of OpTypePointer is the Storage Class.
1537 auto result_type_storage_class
= result_type
->word(2);
1538 auto base_type_storage_class
= base_type
->word(2);
1539 if (result_type_storage_class
!= base_type_storage_class
) {
1540 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
1541 << "The result pointer storage class and base "
1542 "pointer storage class in "
1543 << instr_name
<< " do not match.";
1546 // The type pointed to by OpTypePointer (word 3) must be a composite type.
1547 auto type_pointee
= untyped_pointer
1548 ? _
.FindDef(inst
->GetOperandAs
<uint32_t>(2))
1549 : _
.FindDef(base_type
->word(3));
1551 // Check Universal Limit (SPIR-V Spec. Section 2.17).
1552 // The number of indexes passed to OpAccessChain may not exceed 255
1553 // The instruction includes 4 words + N words (for N indexes)
1554 size_t num_indexes
= inst
->words().size() - 4;
1555 if (inst
->opcode() == spv::Op::OpPtrAccessChain
||
1556 inst
->opcode() == spv::Op::OpInBoundsPtrAccessChain
||
1557 inst
->opcode() == spv::Op::OpUntypedPtrAccessChainKHR
||
1558 inst
->opcode() == spv::Op::OpUntypedInBoundsPtrAccessChainKHR
) {
1559 // In pointer access chains, the element operand is required, but not
1560 // counted as an index.
1563 const size_t num_indexes_limit
=
1564 _
.options()->universal_limits_
.max_access_chain_indexes
;
1565 if (num_indexes
> num_indexes_limit
) {
1566 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
1567 << "The number of indexes in " << instr_name
<< " may not exceed "
1568 << num_indexes_limit
<< ". Found " << num_indexes
<< " indexes.";
1570 // Indexes walk the type hierarchy to the desired depth, potentially down to
1571 // scalar granularity. The first index in Indexes will select the top-level
1572 // member/element/component/element of the base composite. All composite
1573 // constituents use zero-based numbering, as described by their OpType...
1574 // instruction. The second index will apply similarly to that result, and so
1575 // on. Once any non-composite type is reached, there must be no remaining
1576 // (unused) indexes.
1577 auto starting_index
= untyped_pointer
? 5 : 4;
1578 if (inst
->opcode() == spv::Op::OpPtrAccessChain
||
1579 inst
->opcode() == spv::Op::OpInBoundsPtrAccessChain
||
1580 inst
->opcode() == spv::Op::OpUntypedPtrAccessChainKHR
||
1581 inst
->opcode() == spv::Op::OpUntypedInBoundsPtrAccessChainKHR
) {
1584 for (size_t i
= starting_index
; i
< inst
->words().size(); ++i
) {
1585 const uint32_t cur_word
= inst
->words()[i
];
1586 // Earlier ID checks ensure that cur_word definition exists.
1587 auto cur_word_instr
= _
.FindDef(cur_word
);
1588 // The index must be a scalar integer type (See OpAccessChain in the Spec.)
1589 auto index_type
= _
.FindDef(cur_word_instr
->type_id());
1590 if (!index_type
|| spv::Op::OpTypeInt
!= index_type
->opcode()) {
1591 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
1592 << "Indexes passed to " << instr_name
1593 << " must be of type integer.";
1595 switch (type_pointee
->opcode()) {
1596 case spv::Op::OpTypeMatrix
:
1597 case spv::Op::OpTypeVector
:
1598 case spv::Op::OpTypeCooperativeMatrixNV
:
1599 case spv::Op::OpTypeCooperativeMatrixKHR
:
1600 case spv::Op::OpTypeArray
:
1601 case spv::Op::OpTypeRuntimeArray
: {
1602 // In OpTypeMatrix, OpTypeVector, spv::Op::OpTypeCooperativeMatrixNV,
1603 // OpTypeArray, and OpTypeRuntimeArray, word 2 is the Element Type.
1604 type_pointee
= _
.FindDef(type_pointee
->word(2));
1607 case spv::Op::OpTypeStruct
: {
1608 // In case of structures, there is an additional constraint on the
1609 // index: the index must be an OpConstant.
1611 if (!_
.EvalConstantValInt64(cur_word
, &cur_index
)) {
1612 return _
.diag(SPV_ERROR_INVALID_ID
, cur_word_instr
)
1613 << "The <id> passed to " << instr_name
1614 << " to index into a "
1615 "structure must be an OpConstant.";
1618 // The index points to the struct member we want, therefore, the index
1619 // should be less than the number of struct members.
1620 const int64_t num_struct_members
=
1621 static_cast<int64_t>(type_pointee
->words().size() - 2);
1622 if (cur_index
>= num_struct_members
|| cur_index
< 0) {
1623 return _
.diag(SPV_ERROR_INVALID_ID
, cur_word_instr
)
1624 << "Index is out of bounds: " << instr_name
1625 << " cannot find index " << cur_index
1626 << " into the structure <id> "
1627 << _
.getIdName(type_pointee
->id()) << ". This structure has "
1628 << num_struct_members
<< " members. Largest valid index is "
1629 << num_struct_members
- 1 << ".";
1631 // Struct members IDs start at word 2 of OpTypeStruct.
1632 const size_t word_index
= static_cast<size_t>(cur_index
) + 2;
1633 auto structMemberId
= type_pointee
->word(word_index
);
1634 type_pointee
= _
.FindDef(structMemberId
);
1638 // Give an error. reached non-composite type while indexes still remain.
1639 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
1641 << " reached non-composite type while indexes "
1642 "still remain to be traversed.";
1647 if (!untyped_pointer
) {
1648 // Result type is a pointer. Find out what it's pointing to.
1649 // This will be used to make sure the indexing results in the same type.
1650 // OpTypePointer word 3 is the type being pointed to.
1651 const auto result_type_pointee
= _
.FindDef(result_type
->word(3));
1652 // At this point, we have fully walked down from the base using the indeces.
1653 // The type being pointed to should be the same as the result type.
1654 if (type_pointee
->id() != result_type_pointee
->id()) {
1655 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
1656 << instr_name
<< " result type (Op"
1658 static_cast<spv::Op
>(result_type_pointee
->opcode()))
1659 << ") does not match the type that results from indexing into the "
1662 << spvOpcodeString(static_cast<spv::Op
>(type_pointee
->opcode()))
1670 spv_result_t
ValidateRawAccessChain(ValidationState_t
& _
,
1671 const Instruction
* inst
) {
1672 std::string instr_name
= "Op" + std::string(spvOpcodeString(inst
->opcode()));
1674 // The result type must be OpTypePointer.
1675 const auto result_type
= _
.FindDef(inst
->type_id());
1676 if (spv::Op::OpTypePointer
!= result_type
->opcode()) {
1677 return _
.diag(SPV_ERROR_INVALID_DATA
, inst
)
1678 << "The Result Type of " << instr_name
<< " <id> "
1679 << _
.getIdName(inst
->id()) << " must be OpTypePointer. Found Op"
1680 << spvOpcodeString(result_type
->opcode()) << '.';
1683 // The pointed storage class must be valid.
1684 const auto storage_class
= result_type
->GetOperandAs
<spv::StorageClass
>(1);
1685 if (storage_class
!= spv::StorageClass::StorageBuffer
&&
1686 storage_class
!= spv::StorageClass::PhysicalStorageBuffer
&&
1687 storage_class
!= spv::StorageClass::Uniform
) {
1688 return _
.diag(SPV_ERROR_INVALID_DATA
, inst
)
1689 << "The Result Type of " << instr_name
<< " <id> "
1690 << _
.getIdName(inst
->id())
1691 << " must point to a storage class of "
1692 "StorageBuffer, PhysicalStorageBuffer, or Uniform.";
1695 // The pointed type must not be one in the list below.
1696 const auto result_type_pointee
=
1697 _
.FindDef(result_type
->GetOperandAs
<uint32_t>(2));
1698 if (result_type_pointee
->opcode() == spv::Op::OpTypeArray
||
1699 result_type_pointee
->opcode() == spv::Op::OpTypeMatrix
||
1700 result_type_pointee
->opcode() == spv::Op::OpTypeStruct
) {
1701 return _
.diag(SPV_ERROR_INVALID_DATA
, inst
)
1702 << "The Result Type of " << instr_name
<< " <id> "
1703 << _
.getIdName(inst
->id())
1704 << " must not point to "
1705 "OpTypeArray, OpTypeMatrix, or OpTypeStruct.";
1708 // Validate Stride is a OpConstant.
1709 const auto stride
= _
.FindDef(inst
->GetOperandAs
<uint32_t>(3));
1710 if (stride
->opcode() != spv::Op::OpConstant
) {
1711 return _
.diag(SPV_ERROR_INVALID_DATA
, inst
)
1712 << "The Stride of " << instr_name
<< " <id> "
1713 << _
.getIdName(inst
->id()) << " must be OpConstant. Found Op"
1714 << spvOpcodeString(stride
->opcode()) << '.';
1716 // Stride type must be OpTypeInt
1717 const auto stride_type
= _
.FindDef(stride
->type_id());
1718 if (stride_type
->opcode() != spv::Op::OpTypeInt
) {
1719 return _
.diag(SPV_ERROR_INVALID_DATA
, inst
)
1720 << "The type of Stride of " << instr_name
<< " <id> "
1721 << _
.getIdName(inst
->id()) << " must be OpTypeInt. Found Op"
1722 << spvOpcodeString(stride_type
->opcode()) << '.';
1725 // Index and Offset type must be OpTypeInt with a width of 32
1726 const auto ValidateType
= [&](const char* name
,
1727 int operandIndex
) -> spv_result_t
{
1728 const auto value
= _
.FindDef(inst
->GetOperandAs
<uint32_t>(operandIndex
));
1729 const auto value_type
= _
.FindDef(value
->type_id());
1730 if (value_type
->opcode() != spv::Op::OpTypeInt
) {
1731 return _
.diag(SPV_ERROR_INVALID_DATA
, inst
)
1732 << "The type of " << name
<< " of " << instr_name
<< " <id> "
1733 << _
.getIdName(inst
->id()) << " must be OpTypeInt. Found Op"
1734 << spvOpcodeString(value_type
->opcode()) << '.';
1736 const auto width
= value_type
->GetOperandAs
<uint32_t>(1);
1738 return _
.diag(SPV_ERROR_INVALID_DATA
, inst
)
1739 << "The integer width of " << name
<< " of " << instr_name
1740 << " <id> " << _
.getIdName(inst
->id()) << " must be 32. Found "
1745 spv_result_t result
;
1746 result
= ValidateType("Index", 4);
1747 if (result
!= SPV_SUCCESS
) {
1750 result
= ValidateType("Offset", 5);
1751 if (result
!= SPV_SUCCESS
) {
1755 uint32_t access_operands
= 0;
1756 if (inst
->operands().size() >= 7) {
1757 access_operands
= inst
->GetOperandAs
<uint32_t>(6);
1759 if (access_operands
&
1760 uint32_t(spv::RawAccessChainOperandsMask::RobustnessPerElementNV
)) {
1761 uint64_t stride_value
= 0;
1762 if (_
.EvalConstantValUint64(stride
->id(), &stride_value
) &&
1763 stride_value
== 0) {
1764 return _
.diag(SPV_ERROR_INVALID_DATA
, inst
)
1765 << "Stride must not be zero when per-element robustness is used.";
1768 if (access_operands
&
1769 uint32_t(spv::RawAccessChainOperandsMask::RobustnessPerComponentNV
) ||
1771 uint32_t(spv::RawAccessChainOperandsMask::RobustnessPerElementNV
)) {
1772 if (storage_class
== spv::StorageClass::PhysicalStorageBuffer
) {
1773 return _
.diag(SPV_ERROR_INVALID_DATA
, inst
)
1774 << "Storage class cannot be PhysicalStorageBuffer when "
1775 "raw access chain robustness is used.";
1778 if (access_operands
&
1779 uint32_t(spv::RawAccessChainOperandsMask::RobustnessPerComponentNV
) &&
1781 uint32_t(spv::RawAccessChainOperandsMask::RobustnessPerElementNV
)) {
1782 return _
.diag(SPV_ERROR_INVALID_DATA
, inst
)
1783 << "Per-component robustness and per-element robustness are "
1784 "mutually exclusive.";
1790 spv_result_t
ValidatePtrAccessChain(ValidationState_t
& _
,
1791 const Instruction
* inst
) {
1792 if (_
.addressing_model() == spv::AddressingModel::Logical
&&
1793 inst
->opcode() == spv::Op::OpPtrAccessChain
) {
1794 if (!_
.features().variable_pointers
) {
1795 return _
.diag(SPV_ERROR_INVALID_DATA
, inst
)
1796 << "Generating variable pointers requires capability "
1797 << "VariablePointers or VariablePointersStorageBuffer";
1801 // Need to call first, will make sure Base is a valid ID
1802 if (auto error
= ValidateAccessChain(_
, inst
)) return error
;
1804 const bool untyped_pointer
= spvOpcodeGeneratesUntypedPointer(inst
->opcode());
1806 const auto base_id
= inst
->GetOperandAs
<uint32_t>(2);
1807 const auto base
= _
.FindDef(base_id
);
1808 const auto base_type
= untyped_pointer
1809 ? _
.FindDef(inst
->GetOperandAs
<uint32_t>(2))
1810 : _
.FindDef(base
->type_id());
1811 const auto base_type_storage_class
=
1812 base_type
->GetOperandAs
<spv::StorageClass
>(1);
1814 if (_
.HasCapability(spv::Capability::Shader
) &&
1815 (base_type_storage_class
== spv::StorageClass::Uniform
||
1816 base_type_storage_class
== spv::StorageClass::StorageBuffer
||
1817 base_type_storage_class
== spv::StorageClass::PhysicalStorageBuffer
||
1818 base_type_storage_class
== spv::StorageClass::PushConstant
||
1819 (_
.HasCapability(spv::Capability::WorkgroupMemoryExplicitLayoutKHR
) &&
1820 base_type_storage_class
== spv::StorageClass::Workgroup
)) &&
1821 !_
.HasDecoration(base_type
->id(), spv::Decoration::ArrayStride
)) {
1822 return _
.diag(SPV_ERROR_INVALID_DATA
, inst
)
1823 << "OpPtrAccessChain must have a Base whose type is decorated "
1827 if (spvIsVulkanEnv(_
.context()->target_env
)) {
1828 const auto untyped_cap
=
1829 untyped_pointer
&& _
.HasCapability(spv::Capability::UntypedPointersKHR
);
1830 if (base_type_storage_class
== spv::StorageClass::Workgroup
) {
1831 if (!_
.HasCapability(spv::Capability::VariablePointers
) && !untyped_cap
) {
1832 return _
.diag(SPV_ERROR_INVALID_DATA
, inst
)
1833 << _
.VkErrorID(7651)
1834 << "OpPtrAccessChain Base operand pointing to Workgroup "
1835 "storage class must use VariablePointers capability";
1837 } else if (base_type_storage_class
== spv::StorageClass::StorageBuffer
) {
1838 if (!_
.features().variable_pointers
&& !untyped_cap
) {
1839 return _
.diag(SPV_ERROR_INVALID_DATA
, inst
)
1840 << _
.VkErrorID(7652)
1841 << "OpPtrAccessChain Base operand pointing to StorageBuffer "
1842 "storage class must use VariablePointers or "
1843 "VariablePointersStorageBuffer capability";
1845 } else if (base_type_storage_class
!=
1846 spv::StorageClass::PhysicalStorageBuffer
&&
1848 return _
.diag(SPV_ERROR_INVALID_DATA
, inst
)
1849 << _
.VkErrorID(7650)
1850 << "OpPtrAccessChain Base operand must point to Workgroup, "
1851 "StorageBuffer, or PhysicalStorageBuffer storage class";
1858 spv_result_t
ValidateArrayLength(ValidationState_t
& state
,
1859 const Instruction
* inst
) {
1860 std::string instr_name
=
1861 "Op" + std::string(spvOpcodeString(static_cast<spv::Op
>(inst
->opcode())));
1863 // Result type must be a 32-bit unsigned int.
1864 auto result_type
= state
.FindDef(inst
->type_id());
1865 if (result_type
->opcode() != spv::Op::OpTypeInt
||
1866 result_type
->GetOperandAs
<uint32_t>(1) != 32 ||
1867 result_type
->GetOperandAs
<uint32_t>(2) != 0) {
1868 return state
.diag(SPV_ERROR_INVALID_ID
, inst
)
1869 << "The Result Type of " << instr_name
<< " <id> "
1870 << state
.getIdName(inst
->id())
1871 << " must be OpTypeInt with width 32 and signedness 0.";
1874 const bool untyped
= inst
->opcode() == spv::Op::OpUntypedArrayLengthKHR
;
1875 auto pointer_ty_id
= state
.GetOperandTypeId(inst
, (untyped
? 3 : 2));
1876 auto pointer_ty
= state
.FindDef(pointer_ty_id
);
1878 if (pointer_ty
->opcode() != spv::Op::OpTypeUntypedPointerKHR
) {
1879 return state
.diag(SPV_ERROR_INVALID_ID
, inst
)
1880 << "Pointer must be an untyped pointer";
1882 } else if (pointer_ty
->opcode() != spv::Op::OpTypePointer
) {
1883 return state
.diag(SPV_ERROR_INVALID_ID
, inst
)
1884 << "The Structure's type in " << instr_name
<< " <id> "
1885 << state
.getIdName(inst
->id())
1886 << " must be a pointer to an OpTypeStruct.";
1889 Instruction
* structure_type
= nullptr;
1891 structure_type
= state
.FindDef(inst
->GetOperandAs
<uint32_t>(2));
1893 structure_type
= state
.FindDef(pointer_ty
->GetOperandAs
<uint32_t>(2));
1896 if (structure_type
->opcode() != spv::Op::OpTypeStruct
) {
1897 return state
.diag(SPV_ERROR_INVALID_ID
, inst
)
1898 << "The Structure's type in " << instr_name
<< " <id> "
1899 << state
.getIdName(inst
->id())
1900 << " must be a pointer to an OpTypeStruct.";
1903 auto num_of_members
= structure_type
->operands().size() - 1;
1905 state
.FindDef(structure_type
->GetOperandAs
<uint32_t>(num_of_members
));
1906 if (last_member
->opcode() != spv::Op::OpTypeRuntimeArray
) {
1907 return state
.diag(SPV_ERROR_INVALID_ID
, inst
)
1908 << "The Structure's last member in " << instr_name
<< " <id> "
1909 << state
.getIdName(inst
->id()) << " must be an OpTypeRuntimeArray.";
1912 // The array member must the index of the last element (the run time
1914 const auto index
= untyped
? 4 : 3;
1915 if (inst
->GetOperandAs
<uint32_t>(index
) != num_of_members
- 1) {
1916 return state
.diag(SPV_ERROR_INVALID_ID
, inst
)
1917 << "The array member in " << instr_name
<< " <id> "
1918 << state
.getIdName(inst
->id())
1919 << " must be the last member of the struct.";
1924 spv_result_t
ValidateCooperativeMatrixLengthNV(ValidationState_t
& state
,
1925 const Instruction
* inst
) {
1926 std::string instr_name
=
1927 "Op" + std::string(spvOpcodeString(static_cast<spv::Op
>(inst
->opcode())));
1929 // Result type must be a 32-bit unsigned int.
1930 auto result_type
= state
.FindDef(inst
->type_id());
1931 if (result_type
->opcode() != spv::Op::OpTypeInt
||
1932 result_type
->GetOperandAs
<uint32_t>(1) != 32 ||
1933 result_type
->GetOperandAs
<uint32_t>(2) != 0) {
1934 return state
.diag(SPV_ERROR_INVALID_ID
, inst
)
1935 << "The Result Type of " << instr_name
<< " <id> "
1936 << state
.getIdName(inst
->id())
1937 << " must be OpTypeInt with width 32 and signedness 0.";
1940 bool isKhr
= inst
->opcode() == spv::Op::OpCooperativeMatrixLengthKHR
;
1941 auto type_id
= inst
->GetOperandAs
<uint32_t>(2);
1942 auto type
= state
.FindDef(type_id
);
1943 if (isKhr
&& type
->opcode() != spv::Op::OpTypeCooperativeMatrixKHR
) {
1944 return state
.diag(SPV_ERROR_INVALID_ID
, inst
)
1945 << "The type in " << instr_name
<< " <id> "
1946 << state
.getIdName(type_id
)
1947 << " must be OpTypeCooperativeMatrixKHR.";
1948 } else if (!isKhr
&& type
->opcode() != spv::Op::OpTypeCooperativeMatrixNV
) {
1949 return state
.diag(SPV_ERROR_INVALID_ID
, inst
)
1950 << "The type in " << instr_name
<< " <id> "
1951 << state
.getIdName(type_id
) << " must be OpTypeCooperativeMatrixNV.";
1956 spv_result_t
ValidateCooperativeMatrixLoadStoreNV(ValidationState_t
& _
,
1957 const Instruction
* inst
) {
1960 if (inst
->opcode() == spv::Op::OpCooperativeMatrixLoadNV
) {
1961 type_id
= inst
->type_id();
1962 opname
= "spv::Op::OpCooperativeMatrixLoadNV";
1964 // get Object operand's type
1965 type_id
= _
.FindDef(inst
->GetOperandAs
<uint32_t>(1))->type_id();
1966 opname
= "spv::Op::OpCooperativeMatrixStoreNV";
1969 auto matrix_type
= _
.FindDef(type_id
);
1971 if (matrix_type
->opcode() != spv::Op::OpTypeCooperativeMatrixNV
) {
1972 if (inst
->opcode() == spv::Op::OpCooperativeMatrixLoadNV
) {
1973 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
1974 << "spv::Op::OpCooperativeMatrixLoadNV Result Type <id> "
1975 << _
.getIdName(type_id
) << " is not a cooperative matrix type.";
1977 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
1978 << "spv::Op::OpCooperativeMatrixStoreNV Object type <id> "
1979 << _
.getIdName(type_id
) << " is not a cooperative matrix type.";
1983 const auto pointer_index
=
1984 (inst
->opcode() == spv::Op::OpCooperativeMatrixLoadNV
) ? 2u : 0u;
1985 const auto pointer_id
= inst
->GetOperandAs
<uint32_t>(pointer_index
);
1986 const auto pointer
= _
.FindDef(pointer_id
);
1988 ((_
.addressing_model() == spv::AddressingModel::Logical
) &&
1989 ((!_
.features().variable_pointers
&&
1990 !spvOpcodeReturnsLogicalPointer(pointer
->opcode())) ||
1991 (_
.features().variable_pointers
&&
1992 !spvOpcodeReturnsLogicalVariablePointer(pointer
->opcode()))))) {
1993 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
1994 << opname
<< " Pointer <id> " << _
.getIdName(pointer_id
)
1995 << " is not a logical pointer.";
1998 const auto pointer_type_id
= pointer
->type_id();
1999 const auto pointer_type
= _
.FindDef(pointer_type_id
);
2000 if (!pointer_type
|| pointer_type
->opcode() != spv::Op::OpTypePointer
) {
2001 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
2002 << opname
<< " type for pointer <id> " << _
.getIdName(pointer_id
)
2003 << " is not a pointer type.";
2006 const auto storage_class_index
= 1u;
2007 const auto storage_class
=
2008 pointer_type
->GetOperandAs
<spv::StorageClass
>(storage_class_index
);
2010 if (storage_class
!= spv::StorageClass::Workgroup
&&
2011 storage_class
!= spv::StorageClass::StorageBuffer
&&
2012 storage_class
!= spv::StorageClass::PhysicalStorageBuffer
) {
2013 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
2014 << opname
<< " storage class for pointer type <id> "
2015 << _
.getIdName(pointer_type_id
)
2016 << " is not Workgroup or StorageBuffer.";
2019 const auto pointee_id
= pointer_type
->GetOperandAs
<uint32_t>(2);
2020 const auto pointee_type
= _
.FindDef(pointee_id
);
2021 if (!pointee_type
|| !(_
.IsIntScalarOrVectorType(pointee_id
) ||
2022 _
.IsFloatScalarOrVectorType(pointee_id
))) {
2023 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
2024 << opname
<< " Pointer <id> " << _
.getIdName(pointer
->id())
2025 << "s Type must be a scalar or vector type.";
2028 const auto stride_index
=
2029 (inst
->opcode() == spv::Op::OpCooperativeMatrixLoadNV
) ? 3u : 2u;
2030 const auto stride_id
= inst
->GetOperandAs
<uint32_t>(stride_index
);
2031 const auto stride
= _
.FindDef(stride_id
);
2032 if (!stride
|| !_
.IsIntScalarType(stride
->type_id())) {
2033 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
2034 << "Stride operand <id> " << _
.getIdName(stride_id
)
2035 << " must be a scalar integer type.";
2038 const auto colmajor_index
=
2039 (inst
->opcode() == spv::Op::OpCooperativeMatrixLoadNV
) ? 4u : 3u;
2040 const auto colmajor_id
= inst
->GetOperandAs
<uint32_t>(colmajor_index
);
2041 const auto colmajor
= _
.FindDef(colmajor_id
);
2042 if (!colmajor
|| !_
.IsBoolScalarType(colmajor
->type_id()) ||
2043 !(spvOpcodeIsConstant(colmajor
->opcode()) ||
2044 spvOpcodeIsSpecConstant(colmajor
->opcode()))) {
2045 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
2046 << "Column Major operand <id> " << _
.getIdName(colmajor_id
)
2047 << " must be a boolean constant instruction.";
2050 const auto memory_access_index
=
2051 (inst
->opcode() == spv::Op::OpCooperativeMatrixLoadNV
) ? 5u : 4u;
2052 if (inst
->operands().size() > memory_access_index
) {
2053 if (auto error
= CheckMemoryAccess(_
, inst
, memory_access_index
))
2060 spv_result_t
ValidateCooperativeMatrixLoadStoreKHR(ValidationState_t
& _
,
2061 const Instruction
* inst
) {
2064 if (inst
->opcode() == spv::Op::OpCooperativeMatrixLoadKHR
) {
2065 type_id
= inst
->type_id();
2066 opname
= "spv::Op::OpCooperativeMatrixLoadKHR";
2068 // get Object operand's type
2069 type_id
= _
.FindDef(inst
->GetOperandAs
<uint32_t>(1))->type_id();
2070 opname
= "spv::Op::OpCooperativeMatrixStoreKHR";
2073 auto matrix_type
= _
.FindDef(type_id
);
2075 if (matrix_type
->opcode() != spv::Op::OpTypeCooperativeMatrixKHR
) {
2076 if (inst
->opcode() == spv::Op::OpCooperativeMatrixLoadKHR
) {
2077 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
2078 << "spv::Op::OpCooperativeMatrixLoadKHR Result Type <id> "
2079 << _
.getIdName(type_id
) << " is not a cooperative matrix type.";
2081 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
2082 << "spv::Op::OpCooperativeMatrixStoreKHR Object type <id> "
2083 << _
.getIdName(type_id
) << " is not a cooperative matrix type.";
2087 const auto pointer_index
=
2088 (inst
->opcode() == spv::Op::OpCooperativeMatrixLoadKHR
) ? 2u : 0u;
2089 const auto pointer_id
= inst
->GetOperandAs
<uint32_t>(pointer_index
);
2090 const auto pointer
= _
.FindDef(pointer_id
);
2092 ((_
.addressing_model() == spv::AddressingModel::Logical
) &&
2093 ((!_
.features().variable_pointers
&&
2094 !spvOpcodeReturnsLogicalPointer(pointer
->opcode())) ||
2095 (_
.features().variable_pointers
&&
2096 !spvOpcodeReturnsLogicalVariablePointer(pointer
->opcode()))))) {
2097 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
2098 << opname
<< " Pointer <id> " << _
.getIdName(pointer_id
)
2099 << " is not a logical pointer.";
2102 const auto pointer_type_id
= pointer
->type_id();
2103 const auto pointer_type
= _
.FindDef(pointer_type_id
);
2104 if (!pointer_type
||
2105 !(pointer_type
->opcode() == spv::Op::OpTypePointer
||
2106 pointer_type
->opcode() == spv::Op::OpTypeUntypedPointerKHR
)) {
2107 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
2108 << opname
<< " type for pointer <id> " << _
.getIdName(pointer_id
)
2109 << " is not a pointer type.";
2112 const bool untyped
=
2113 pointer_type
->opcode() == spv::Op::OpTypeUntypedPointerKHR
;
2114 const auto storage_class_index
= 1u;
2115 const auto storage_class
=
2116 pointer_type
->GetOperandAs
<spv::StorageClass
>(storage_class_index
);
2118 if (spvIsVulkanEnv(_
.context()->target_env
)) {
2119 if (storage_class
!= spv::StorageClass::Workgroup
&&
2120 storage_class
!= spv::StorageClass::StorageBuffer
&&
2121 storage_class
!= spv::StorageClass::PhysicalStorageBuffer
) {
2122 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
2123 << _
.VkErrorID(8973) << opname
2124 << " storage class for pointer type <id> "
2125 << _
.getIdName(pointer_type_id
)
2126 << " is not Workgroup, StorageBuffer, or PhysicalStorageBuffer.";
2131 const auto pointee_id
= pointer_type
->GetOperandAs
<uint32_t>(2);
2132 const auto pointee_type
= _
.FindDef(pointee_id
);
2133 if (!pointee_type
|| !(_
.IsIntScalarOrVectorType(pointee_id
) ||
2134 _
.IsFloatScalarOrVectorType(pointee_id
))) {
2135 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
2136 << opname
<< " Pointer <id> " << _
.getIdName(pointer
->id())
2137 << "s Type must be a scalar or vector type.";
2141 const auto layout_index
=
2142 (inst
->opcode() == spv::Op::OpCooperativeMatrixLoadKHR
) ? 3u : 2u;
2143 const auto layout_id
= inst
->GetOperandAs
<uint32_t>(layout_index
);
2144 const auto layout_inst
= _
.FindDef(layout_id
);
2145 if (!layout_inst
|| !_
.IsIntScalarType(layout_inst
->type_id()) ||
2146 !spvOpcodeIsConstant(layout_inst
->opcode())) {
2147 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
2148 << "MemoryLayout operand <id> " << _
.getIdName(layout_id
)
2149 << " must be a 32-bit integer constant instruction.";
2152 bool stride_required
= false;
2154 if (_
.EvalConstantValUint64(layout_id
, &layout
)) {
2156 (layout
== (uint64_t)spv::CooperativeMatrixLayout::RowMajorKHR
) ||
2157 (layout
== (uint64_t)spv::CooperativeMatrixLayout::ColumnMajorKHR
);
2160 const auto stride_index
=
2161 (inst
->opcode() == spv::Op::OpCooperativeMatrixLoadKHR
) ? 4u : 3u;
2162 if (inst
->operands().size() > stride_index
) {
2163 const auto stride_id
= inst
->GetOperandAs
<uint32_t>(stride_index
);
2164 const auto stride
= _
.FindDef(stride_id
);
2165 if (!stride
|| !_
.IsIntScalarType(stride
->type_id())) {
2166 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
2167 << "Stride operand <id> " << _
.getIdName(stride_id
)
2168 << " must be a scalar integer type.";
2170 } else if (stride_required
) {
2171 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
2172 << "MemoryLayout " << layout
<< " requires a Stride.";
2175 const auto memory_access_index
=
2176 (inst
->opcode() == spv::Op::OpCooperativeMatrixLoadKHR
) ? 5u : 4u;
2177 if (inst
->operands().size() > memory_access_index
) {
2178 if (auto error
= CheckMemoryAccess(_
, inst
, memory_access_index
))
2185 // Returns the number of instruction words taken up by a tensor addressing
2186 // operands argument and its implied operands.
2187 int TensorAddressingOperandsNumWords(spv::TensorAddressingOperandsMask mask
) {
2188 int result
= 1; // Count the mask
2189 if ((mask
& spv::TensorAddressingOperandsMask::TensorView
) !=
2190 spv::TensorAddressingOperandsMask::MaskNone
)
2192 if ((mask
& spv::TensorAddressingOperandsMask::DecodeFunc
) !=
2193 spv::TensorAddressingOperandsMask::MaskNone
)
2198 spv_result_t
ValidateCooperativeMatrixLoadStoreTensorNV(
2199 ValidationState_t
& _
, const Instruction
* inst
) {
2202 if (inst
->opcode() == spv::Op::OpCooperativeMatrixLoadTensorNV
) {
2203 type_id
= inst
->type_id();
2204 opname
= "spv::Op::OpCooperativeMatrixLoadTensorNV";
2206 // get Object operand's type
2207 type_id
= _
.FindDef(inst
->GetOperandAs
<uint32_t>(1))->type_id();
2208 opname
= "spv::Op::OpCooperativeMatrixStoreTensorNV";
2211 auto matrix_type
= _
.FindDef(type_id
);
2213 if (matrix_type
->opcode() != spv::Op::OpTypeCooperativeMatrixKHR
) {
2214 if (inst
->opcode() == spv::Op::OpCooperativeMatrixLoadTensorNV
) {
2215 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
2216 << "spv::Op::OpCooperativeMatrixLoadTensorNV Result Type <id> "
2217 << _
.getIdName(type_id
) << " is not a cooperative matrix type.";
2219 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
2220 << "spv::Op::OpCooperativeMatrixStoreTensorNV Object type <id> "
2221 << _
.getIdName(type_id
) << " is not a cooperative matrix type.";
2225 const auto pointer_index
=
2226 (inst
->opcode() == spv::Op::OpCooperativeMatrixLoadTensorNV
) ? 2u : 0u;
2227 const auto pointer_id
= inst
->GetOperandAs
<uint32_t>(pointer_index
);
2228 const auto pointer
= _
.FindDef(pointer_id
);
2230 ((_
.addressing_model() == spv::AddressingModel::Logical
) &&
2231 ((!_
.features().variable_pointers
&&
2232 !spvOpcodeReturnsLogicalPointer(pointer
->opcode())) ||
2233 (_
.features().variable_pointers
&&
2234 !spvOpcodeReturnsLogicalVariablePointer(pointer
->opcode()))))) {
2235 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
2236 << opname
<< " Pointer <id> " << _
.getIdName(pointer_id
)
2237 << " is not a logical pointer.";
2240 const auto pointer_type_id
= pointer
->type_id();
2241 const auto pointer_type
= _
.FindDef(pointer_type_id
);
2242 if (!pointer_type
|| pointer_type
->opcode() != spv::Op::OpTypePointer
) {
2243 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
2244 << opname
<< " type for pointer <id> " << _
.getIdName(pointer_id
)
2245 << " is not a pointer type.";
2248 const auto storage_class_index
= 1u;
2249 const auto storage_class
=
2250 pointer_type
->GetOperandAs
<spv::StorageClass
>(storage_class_index
);
2252 if (storage_class
!= spv::StorageClass::Workgroup
&&
2253 storage_class
!= spv::StorageClass::StorageBuffer
&&
2254 storage_class
!= spv::StorageClass::PhysicalStorageBuffer
) {
2255 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
2256 << _
.VkErrorID(8973) << opname
2257 << " storage class for pointer type <id> "
2258 << _
.getIdName(pointer_type_id
)
2259 << " is not Workgroup, StorageBuffer, or PhysicalStorageBuffer.";
2262 if (inst
->opcode() == spv::Op::OpCooperativeMatrixLoadTensorNV
) {
2263 const auto object_index
= 3;
2264 const auto object_id
= inst
->GetOperandAs
<uint32_t>(object_index
);
2265 const auto object
= _
.FindDef(object_id
);
2266 if (!object
|| object
->type_id() != type_id
) {
2267 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
2268 << opname
<< " Object <id> " << _
.getIdName(object_id
)
2269 << " type does not match Result Type.";
2273 const auto tensor_layout_index
=
2274 (inst
->opcode() == spv::Op::OpCooperativeMatrixLoadTensorNV
) ? 4u : 2u;
2275 const auto tensor_layout_id
=
2276 inst
->GetOperandAs
<uint32_t>(tensor_layout_index
);
2277 const auto tensor_layout
= _
.FindDef(tensor_layout_id
);
2278 if (!tensor_layout
|| _
.FindDef(tensor_layout
->type_id())->opcode() !=
2279 spv::Op::OpTypeTensorLayoutNV
) {
2280 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
2281 << opname
<< " TensorLayout <id> " << _
.getIdName(tensor_layout_id
)
2282 << " does not have a tensor layout type.";
2285 const auto memory_access_index
=
2286 (inst
->opcode() == spv::Op::OpCooperativeMatrixLoadTensorNV
) ? 5u : 3u;
2287 if (inst
->operands().size() > memory_access_index
) {
2288 if (auto error
= CheckMemoryAccess(_
, inst
, memory_access_index
))
2292 const auto memory_access_mask
=
2293 inst
->GetOperandAs
<uint32_t>(memory_access_index
);
2294 const auto tensor_operands_index
=
2295 memory_access_index
+ MemoryAccessNumWords(memory_access_mask
);
2296 const auto tensor_operands
=
2297 inst
->GetOperandAs
<spv::TensorAddressingOperandsMask
>(
2298 tensor_operands_index
);
2300 if (inst
->operands().size() <
2301 tensor_operands_index
+
2302 TensorAddressingOperandsNumWords(tensor_operands
)) {
2303 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
2304 << opname
<< " not enough tensor addressing operands.";
2307 uint32_t tensor_operand_index
= tensor_operands_index
+ 1;
2308 if ((tensor_operands
& spv::TensorAddressingOperandsMask::TensorView
) !=
2309 spv::TensorAddressingOperandsMask::MaskNone
) {
2310 const auto tensor_view_id
=
2311 inst
->GetOperandAs
<uint32_t>(tensor_operand_index
);
2312 const auto tensor_view
= _
.FindDef(tensor_view_id
);
2313 if (!tensor_view
|| _
.FindDef(tensor_view
->type_id())->opcode() !=
2314 spv::Op::OpTypeTensorViewNV
) {
2315 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
2316 << opname
<< " TensorView <id> " << _
.getIdName(tensor_view_id
)
2317 << " does not have a tensor view type.";
2320 tensor_operand_index
++;
2323 if ((tensor_operands
& spv::TensorAddressingOperandsMask::DecodeFunc
) !=
2324 spv::TensorAddressingOperandsMask::MaskNone
) {
2325 if (inst
->opcode() == spv::Op::OpCooperativeMatrixStoreTensorNV
) {
2326 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
2327 << "OpCooperativeMatrixStoreTensorNV does not support DecodeFunc.";
2329 const auto decode_func_id
=
2330 inst
->GetOperandAs
<uint32_t>(tensor_operand_index
);
2331 const auto decode_func
= _
.FindDef(decode_func_id
);
2333 if (!decode_func
|| decode_func
->opcode() != spv::Op::OpFunction
) {
2334 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
2335 << opname
<< " DecodeFunc <id> " << _
.getIdName(decode_func_id
)
2336 << " is not a function.";
2339 const auto component_type_index
= 1;
2340 const auto component_type_id
=
2341 matrix_type
->GetOperandAs
<uint32_t>(component_type_index
);
2343 const auto function_type
=
2344 _
.FindDef(decode_func
->GetOperandAs
<uint32_t>(3));
2345 if (function_type
->GetOperandAs
<uint32_t>(1) != component_type_id
) {
2346 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
2347 << opname
<< " DecodeFunc <id> " << _
.getIdName(decode_func_id
)
2348 << " return type must match matrix component type.";
2351 const auto decode_ptr_type_id
= function_type
->GetOperandAs
<uint32_t>(2);
2352 const auto decode_ptr_type
= _
.FindDef(decode_ptr_type_id
);
2353 auto decode_storage_class
=
2354 decode_ptr_type
->GetOperandAs
<spv::StorageClass
>(storage_class_index
);
2356 if (decode_storage_class
!= spv::StorageClass::PhysicalStorageBuffer
) {
2357 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
2358 << opname
<< " DecodeFunc <id> " << _
.getIdName(decode_func_id
)
2359 << " first parameter must be pointer to PhysicalStorageBuffer.";
2362 const auto tensor_layout_type
= _
.FindDef(tensor_layout
->type_id());
2364 for (uint32_t param
= 3; param
< 5; ++param
) {
2365 const auto param_type_id
= function_type
->GetOperandAs
<uint32_t>(param
);
2366 const auto param_type
= _
.FindDef(param_type_id
);
2367 if (param_type
->opcode() != spv::Op::OpTypeArray
) {
2368 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
2369 << opname
<< " DecodeFunc <id> " << _
.getIdName(decode_func_id
)
2370 << " second/third parameter must be array of 32-bit integer "
2372 << " dimension equal to the tensor dimension.";
2374 const auto length_index
= 2u;
2375 uint64_t array_length
;
2376 if (_
.EvalConstantValUint64(
2377 param_type
->GetOperandAs
<uint32_t>(length_index
),
2379 const auto tensor_layout_dim_id
=
2380 tensor_layout_type
->GetOperandAs
<uint32_t>(1);
2382 if (_
.EvalConstantValUint64(tensor_layout_dim_id
, &dim_value
)) {
2383 if (array_length
!= dim_value
) {
2384 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
2385 << opname
<< " DecodeFunc <id> "
2386 << _
.getIdName(decode_func_id
)
2387 << " second/third parameter must be array of 32-bit integer "
2389 << " dimension equal to the tensor dimension.";
2395 tensor_operand_index
++;
2401 spv_result_t
ValidatePtrComparison(ValidationState_t
& _
,
2402 const Instruction
* inst
) {
2403 if (_
.addressing_model() == spv::AddressingModel::Logical
&&
2404 !_
.features().variable_pointers
) {
2405 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
2406 << "Instruction cannot for logical addressing model be used without "
2407 "a variable pointers capability";
2410 const auto result_type
= _
.FindDef(inst
->type_id());
2411 if (inst
->opcode() == spv::Op::OpPtrDiff
) {
2412 if (!result_type
|| result_type
->opcode() != spv::Op::OpTypeInt
) {
2413 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
2414 << "Result Type must be an integer scalar";
2417 if (!result_type
|| result_type
->opcode() != spv::Op::OpTypeBool
) {
2418 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
2419 << "Result Type must be OpTypeBool";
2423 const auto op1
= _
.FindDef(inst
->GetOperandAs
<uint32_t>(2u));
2424 const auto op2
= _
.FindDef(inst
->GetOperandAs
<uint32_t>(3u));
2425 if (!op1
|| !op2
|| op1
->type_id() != op2
->type_id()) {
2426 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
2427 << "The types of Operand 1 and Operand 2 must match";
2429 const auto op1_type
= _
.FindDef(op1
->type_id());
2430 if (!op1_type
|| (op1_type
->opcode() != spv::Op::OpTypePointer
&&
2431 op1_type
->opcode() != spv::Op::OpTypeUntypedPointerKHR
)) {
2432 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
2433 << "Operand type must be a pointer";
2436 spv::StorageClass sc
= op1_type
->GetOperandAs
<spv::StorageClass
>(1u);
2437 if (_
.addressing_model() == spv::AddressingModel::Logical
) {
2438 if (sc
!= spv::StorageClass::Workgroup
&&
2439 sc
!= spv::StorageClass::StorageBuffer
) {
2440 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
2441 << "Invalid pointer storage class";
2444 if (sc
== spv::StorageClass::Workgroup
&&
2445 !_
.HasCapability(spv::Capability::VariablePointers
)) {
2446 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
2447 << "Workgroup storage class pointer requires VariablePointers "
2448 "capability to be specified";
2450 } else if (sc
== spv::StorageClass::PhysicalStorageBuffer
) {
2451 return _
.diag(SPV_ERROR_INVALID_ID
, inst
)
2452 << "Cannot use a pointer in the PhysicalStorageBuffer storage class";
2460 spv_result_t
MemoryPass(ValidationState_t
& _
, const Instruction
* inst
) {
2461 switch (inst
->opcode()) {
2462 case spv::Op::OpVariable
:
2463 case spv::Op::OpUntypedVariableKHR
:
2464 if (auto error
= ValidateVariable(_
, inst
)) return error
;
2466 case spv::Op::OpLoad
:
2467 if (auto error
= ValidateLoad(_
, inst
)) return error
;
2469 case spv::Op::OpStore
:
2470 if (auto error
= ValidateStore(_
, inst
)) return error
;
2472 case spv::Op::OpCopyMemory
:
2473 case spv::Op::OpCopyMemorySized
:
2474 if (auto error
= ValidateCopyMemory(_
, inst
)) return error
;
2476 case spv::Op::OpPtrAccessChain
:
2477 case spv::Op::OpUntypedPtrAccessChainKHR
:
2478 case spv::Op::OpUntypedInBoundsPtrAccessChainKHR
:
2479 if (auto error
= ValidatePtrAccessChain(_
, inst
)) return error
;
2481 case spv::Op::OpAccessChain
:
2482 case spv::Op::OpInBoundsAccessChain
:
2483 case spv::Op::OpInBoundsPtrAccessChain
:
2484 case spv::Op::OpUntypedAccessChainKHR
:
2485 case spv::Op::OpUntypedInBoundsAccessChainKHR
:
2486 if (auto error
= ValidateAccessChain(_
, inst
)) return error
;
2488 case spv::Op::OpRawAccessChainNV
:
2489 if (auto error
= ValidateRawAccessChain(_
, inst
)) return error
;
2491 case spv::Op::OpArrayLength
:
2492 case spv::Op::OpUntypedArrayLengthKHR
:
2493 if (auto error
= ValidateArrayLength(_
, inst
)) return error
;
2495 case spv::Op::OpCooperativeMatrixLoadNV
:
2496 case spv::Op::OpCooperativeMatrixStoreNV
:
2497 if (auto error
= ValidateCooperativeMatrixLoadStoreNV(_
, inst
))
2500 case spv::Op::OpCooperativeMatrixLengthKHR
:
2501 case spv::Op::OpCooperativeMatrixLengthNV
:
2502 if (auto error
= ValidateCooperativeMatrixLengthNV(_
, inst
)) return error
;
2504 case spv::Op::OpCooperativeMatrixLoadKHR
:
2505 case spv::Op::OpCooperativeMatrixStoreKHR
:
2506 if (auto error
= ValidateCooperativeMatrixLoadStoreKHR(_
, inst
))
2509 case spv::Op::OpCooperativeMatrixLoadTensorNV
:
2510 case spv::Op::OpCooperativeMatrixStoreTensorNV
:
2511 if (auto error
= ValidateCooperativeMatrixLoadStoreTensorNV(_
, inst
))
2514 case spv::Op::OpPtrEqual
:
2515 case spv::Op::OpPtrNotEqual
:
2516 case spv::Op::OpPtrDiff
:
2517 if (auto error
= ValidatePtrComparison(_
, inst
)) return error
;
2519 case spv::Op::OpImageTexelPointer
:
2520 case spv::Op::OpGenericPtrMemSemantics
:
2528 } // namespace spvtools