1 | // Copyright (c) 2015-2016 The Khronos Group Inc. |
2 | // |
3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | // you may not use this file except in compliance with the License. |
5 | // You may obtain a copy of the License at |
6 | // |
7 | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | // |
9 | // Unless required by applicable law or agreed to in writing, software |
10 | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | // See the License for the specific language governing permissions and |
13 | // limitations under the License. |
14 | |
15 | #ifndef SOURCE_VAL_VALIDATION_STATE_H_ |
16 | #define SOURCE_VAL_VALIDATION_STATE_H_ |
17 | |
18 | #include <algorithm> |
19 | #include <map> |
20 | #include <set> |
21 | #include <string> |
22 | #include <tuple> |
23 | #include <unordered_map> |
24 | #include <unordered_set> |
25 | #include <vector> |
26 | |
27 | #include "source/assembly_grammar.h" |
28 | #include "source/diagnostic.h" |
29 | #include "source/disassemble.h" |
30 | #include "source/enum_set.h" |
31 | #include "source/latest_version_spirv_header.h" |
32 | #include "source/name_mapper.h" |
33 | #include "source/spirv_definition.h" |
34 | #include "source/spirv_validator_options.h" |
35 | #include "source/val/decoration.h" |
36 | #include "source/val/function.h" |
37 | #include "source/val/instruction.h" |
38 | #include "spirv-tools/libspirv.h" |
39 | |
40 | namespace spvtools { |
41 | namespace val { |
42 | |
43 | /// This enum represents the sections of a SPIRV module. See section 2.4 |
44 | /// of the SPIRV spec for additional details of the order. The enumerant values |
45 | /// are in the same order as the vector returned by GetModuleOrder |
46 | enum ModuleLayoutSection { |
47 | kLayoutCapabilities, /// < Section 2.4 #1 |
48 | kLayoutExtensions, /// < Section 2.4 #2 |
49 | kLayoutExtInstImport, /// < Section 2.4 #3 |
50 | kLayoutMemoryModel, /// < Section 2.4 #4 |
51 | kLayoutEntryPoint, /// < Section 2.4 #5 |
52 | kLayoutExecutionMode, /// < Section 2.4 #6 |
53 | kLayoutDebug1, /// < Section 2.4 #7 > 1 |
54 | kLayoutDebug2, /// < Section 2.4 #7 > 2 |
55 | kLayoutDebug3, /// < Section 2.4 #7 > 3 |
56 | kLayoutAnnotations, /// < Section 2.4 #8 |
57 | kLayoutTypes, /// < Section 2.4 #9 |
58 | kLayoutFunctionDeclarations, /// < Section 2.4 #10 |
59 | kLayoutFunctionDefinitions /// < Section 2.4 #11 |
60 | }; |
61 | |
62 | /// This class manages the state of the SPIR-V validation as it is being parsed. |
63 | class ValidationState_t { |
64 | public: |
65 | // Features that can optionally be turned on by a capability or environment. |
66 | struct Feature { |
67 | bool declare_int16_type = false; // Allow OpTypeInt with 16 bit width? |
68 | bool declare_float16_type = false; // Allow OpTypeFloat with 16 bit width? |
69 | bool free_fp_rounding_mode = false; // Allow the FPRoundingMode decoration |
70 | // and its vaules to be used without |
71 | // requiring any capability |
72 | |
73 | // Allow functionalities enabled by VariablePointers capability. |
74 | bool variable_pointers = false; |
75 | // Allow functionalities enabled by VariablePointersStorageBuffer |
76 | // capability. |
77 | bool variable_pointers_storage_buffer = false; |
78 | |
79 | // Permit group oerations Reduce, InclusiveScan, ExclusiveScan |
80 | bool group_ops_reduce_and_scans = false; |
81 | |
82 | // Allow OpTypeInt with 8 bit width? |
83 | bool declare_int8_type = false; |
84 | |
85 | // Target environment uses relaxed block layout. |
86 | // This is true for Vulkan 1.1 or later. |
87 | bool env_relaxed_block_layout = false; |
88 | |
89 | // Allow an OpTypeInt with 8 bit width to be used in more than just int |
90 | // conversion opcodes |
91 | bool use_int8_type = false; |
92 | |
93 | // Use scalar block layout. See VK_EXT_scalar_block_layout: |
94 | // Defines scalar alignment: |
95 | // - scalar alignment equals the scalar size in bytes |
96 | // - array alignment is same as its element alignment |
97 | // - array alignment is max alignment of any of its members |
98 | // - vector alignment is same as component alignment |
99 | // - matrix alignment is same as component alignment |
100 | // For struct in Uniform, StorageBuffer, PushConstant: |
101 | // - Offset of a member is multiple of scalar alignment of that member |
102 | // - ArrayStride and MatrixStride are multiples of scalar alignment |
103 | // Members need not be listed in offset order |
104 | bool scalar_block_layout = false; |
105 | |
106 | // SPIR-V 1.4 allows us to select between any two composite values |
107 | // of the same type. |
108 | bool select_between_composites = false; |
109 | |
110 | // SPIR-V 1.4 allows two memory access operands for OpCopyMemory and |
111 | // OpCopyMemorySized. |
112 | bool copy_memory_permits_two_memory_accesses = false; |
113 | |
114 | // SPIR-V 1.4 allows UConvert as a spec constant op in any environment. |
115 | // The Kernel capability already enables it, separately from this flag. |
116 | bool uconvert_spec_constant_op = false; |
117 | |
118 | // SPIR-V 1.4 allows Function and Private variables to be NonWritable |
119 | bool nonwritable_var_in_function_or_private = false; |
120 | }; |
121 | |
122 | ValidationState_t(const spv_const_context context, |
123 | const spv_const_validator_options opt, |
124 | const uint32_t* words, const size_t num_words, |
125 | const uint32_t max_warnings); |
126 | |
127 | /// Returns the context |
128 | spv_const_context context() const { return context_; } |
129 | |
130 | /// Returns the command line options |
131 | spv_const_validator_options options() const { return options_; } |
132 | |
133 | /// Sets the ID of the generator for this module. |
134 | void setGenerator(uint32_t gen) { generator_ = gen; } |
135 | |
136 | /// Returns the ID of the generator for this module. |
137 | uint32_t generator() const { return generator_; } |
138 | |
139 | /// Sets the SPIR-V version of this module. |
140 | void setVersion(uint32_t ver) { version_ = ver; } |
141 | |
142 | /// Gets the SPIR-V version of this module. |
143 | uint32_t version() const { return version_; } |
144 | |
145 | /// Forward declares the id in the module |
146 | spv_result_t ForwardDeclareId(uint32_t id); |
147 | |
148 | /// Removes a forward declared ID if it has been defined |
149 | spv_result_t RemoveIfForwardDeclared(uint32_t id); |
150 | |
151 | /// Registers an ID as a forward pointer |
152 | spv_result_t RegisterForwardPointer(uint32_t id); |
153 | |
154 | /// Returns whether or not an ID is a forward pointer |
155 | bool IsForwardPointer(uint32_t id) const; |
156 | |
157 | /// Assigns a name to an ID |
158 | void AssignNameToId(uint32_t id, std::string name); |
159 | |
160 | /// Returns a string representation of the ID in the format <id>[Name] where |
161 | /// the <id> is the numeric valid of the id and the Name is a name assigned by |
162 | /// the OpName instruction |
163 | std::string getIdName(uint32_t id) const; |
164 | |
165 | /// Accessor function for ID bound. |
166 | uint32_t getIdBound() const; |
167 | |
168 | /// Mutator function for ID bound. |
169 | void setIdBound(uint32_t bound); |
170 | |
171 | /// Returns the number of ID which have been forward referenced but not |
172 | /// defined |
173 | size_t unresolved_forward_id_count() const; |
174 | |
175 | /// Returns a vector of unresolved forward ids. |
176 | std::vector<uint32_t> UnresolvedForwardIds() const; |
177 | |
178 | /// Returns true if the id has been defined |
179 | bool IsDefinedId(uint32_t id) const; |
180 | |
181 | /// Increments the total number of instructions in the file. |
182 | void increment_total_instructions() { total_instructions_++; } |
183 | |
184 | /// Increments the total number of functions in the file. |
185 | void increment_total_functions() { total_functions_++; } |
186 | |
187 | /// Allocates internal storage. Note, calling this will invalidate any |
188 | /// pointers to |ordered_instructions_| or |module_functions_| and, hence, |
189 | /// should only be called at the beginning of validation. |
190 | void preallocateStorage(); |
191 | |
192 | /// Returns the current layout section which is being processed |
193 | ModuleLayoutSection current_layout_section() const; |
194 | |
195 | /// Increments the module_layout_order_section_ |
196 | void ProgressToNextLayoutSectionOrder(); |
197 | |
198 | /// Determines if the op instruction is part of the current section |
199 | bool IsOpcodeInCurrentLayoutSection(SpvOp op); |
200 | |
201 | DiagnosticStream diag(spv_result_t error_code, const Instruction* inst); |
202 | |
203 | /// Returns the function states |
204 | std::vector<Function>& functions(); |
205 | |
206 | /// Returns the function states |
207 | Function& current_function(); |
208 | const Function& current_function() const; |
209 | |
210 | /// Returns function state with the given id, or nullptr if no such function. |
211 | const Function* function(uint32_t id) const; |
212 | Function* function(uint32_t id); |
213 | |
214 | /// Returns true if the called after a function instruction but before the |
215 | /// function end instruction |
216 | bool in_function_body() const; |
217 | |
218 | /// Returns true if called after a label instruction but before a branch |
219 | /// instruction |
220 | bool in_block() const; |
221 | |
222 | struct EntryPointDescription { |
223 | std::string name; |
224 | std::vector<uint32_t> interfaces; |
225 | }; |
226 | |
227 | /// Registers |id| as an entry point with |execution_model| and |interfaces|. |
228 | void RegisterEntryPoint(const uint32_t id, SpvExecutionModel execution_model, |
229 | EntryPointDescription&& desc) { |
230 | entry_points_.push_back(id); |
231 | entry_point_to_execution_models_[id].insert(execution_model); |
232 | entry_point_descriptions_[id].emplace_back(desc); |
233 | } |
234 | |
235 | /// Returns a list of entry point function ids |
236 | const std::vector<uint32_t>& entry_points() const { return entry_points_; } |
237 | |
238 | /// Returns the set of entry points that root call graphs that contain |
239 | /// recursion. |
240 | const std::set<uint32_t>& recursive_entry_points() const { |
241 | return recursive_entry_points_; |
242 | } |
243 | |
244 | /// Registers execution mode for the given entry point. |
245 | void RegisterExecutionModeForEntryPoint(uint32_t entry_point, |
246 | SpvExecutionMode execution_mode) { |
247 | entry_point_to_execution_modes_[entry_point].insert(execution_mode); |
248 | } |
249 | |
250 | /// Returns the interface descriptions of a given entry point. |
251 | const std::vector<EntryPointDescription>& entry_point_descriptions( |
252 | uint32_t entry_point) { |
253 | return entry_point_descriptions_.at(entry_point); |
254 | } |
255 | |
256 | /// Returns Execution Models for the given Entry Point. |
257 | /// Returns nullptr if none found (would trigger assertion). |
258 | const std::set<SpvExecutionModel>* GetExecutionModels( |
259 | uint32_t entry_point) const { |
260 | const auto it = entry_point_to_execution_models_.find(entry_point); |
261 | if (it == entry_point_to_execution_models_.end()) { |
262 | assert(0); |
263 | return nullptr; |
264 | } |
265 | return &it->second; |
266 | } |
267 | |
268 | /// Returns Execution Modes for the given Entry Point. |
269 | /// Returns nullptr if none found. |
270 | const std::set<SpvExecutionMode>* GetExecutionModes( |
271 | uint32_t entry_point) const { |
272 | const auto it = entry_point_to_execution_modes_.find(entry_point); |
273 | if (it == entry_point_to_execution_modes_.end()) { |
274 | return nullptr; |
275 | } |
276 | return &it->second; |
277 | } |
278 | |
279 | /// Traverses call tree and computes function_to_entry_points_. |
280 | /// Note: called after fully parsing the binary. |
281 | void ComputeFunctionToEntryPointMapping(); |
282 | |
283 | /// Traverse call tree and computes recursive_entry_points_. |
284 | /// Note: called after fully parsing the binary and calling |
285 | /// ComputeFunctionToEntryPointMapping. |
286 | void ComputeRecursiveEntryPoints(); |
287 | |
288 | /// Returns all the entry points that can call |func|. |
289 | const std::vector<uint32_t>& FunctionEntryPoints(uint32_t func) const; |
290 | |
291 | /// Returns all the entry points that statically use |id|. |
292 | /// |
293 | /// Note: requires ComputeFunctionToEntryPointMapping to have been called. |
294 | std::set<uint32_t> EntryPointReferences(uint32_t id) const; |
295 | |
296 | /// Inserts an <id> to the set of functions that are target of OpFunctionCall. |
297 | void AddFunctionCallTarget(const uint32_t id) { |
298 | function_call_targets_.insert(id); |
299 | current_function().AddFunctionCallTarget(id); |
300 | } |
301 | |
302 | /// Returns whether or not a function<id> is the target of OpFunctionCall. |
303 | bool IsFunctionCallTarget(const uint32_t id) { |
304 | return (function_call_targets_.find(id) != function_call_targets_.end()); |
305 | } |
306 | |
307 | bool IsFunctionCallDefined(const uint32_t id) { |
308 | return (id_to_function_.find(id) != id_to_function_.end()); |
309 | } |
310 | /// Registers the capability and its dependent capabilities |
311 | void RegisterCapability(SpvCapability cap); |
312 | |
313 | /// Registers the extension. |
314 | void RegisterExtension(Extension ext); |
315 | |
316 | /// Registers the function in the module. Subsequent instructions will be |
317 | /// called against this function |
318 | spv_result_t RegisterFunction(uint32_t id, uint32_t ret_type_id, |
319 | SpvFunctionControlMask function_control, |
320 | uint32_t function_type_id); |
321 | |
322 | /// Register a function end instruction |
323 | spv_result_t RegisterFunctionEnd(); |
324 | |
325 | /// Returns true if the capability is enabled in the module. |
326 | bool HasCapability(SpvCapability cap) const { |
327 | return module_capabilities_.Contains(cap); |
328 | } |
329 | |
330 | /// Returns a reference to the set of capabilities in the module. |
331 | /// This is provided for debuggability. |
332 | const CapabilitySet& module_capabilities() const { |
333 | return module_capabilities_; |
334 | } |
335 | |
336 | /// Returns true if the extension is enabled in the module. |
337 | bool HasExtension(Extension ext) const { |
338 | return module_extensions_.Contains(ext); |
339 | } |
340 | |
341 | /// Returns true if any of the capabilities is enabled, or if |capabilities| |
342 | /// is an empty set. |
343 | bool HasAnyOfCapabilities(const CapabilitySet& capabilities) const; |
344 | |
345 | /// Returns true if any of the extensions is enabled, or if |extensions| |
346 | /// is an empty set. |
347 | bool HasAnyOfExtensions(const ExtensionSet& extensions) const; |
348 | |
349 | /// Sets the addressing model of this module (logical/physical). |
350 | void set_addressing_model(SpvAddressingModel am); |
351 | |
352 | /// Returns true if the OpMemoryModel was found. |
353 | bool has_memory_model_specified() const { |
354 | return addressing_model_ != SpvAddressingModelMax && |
355 | memory_model_ != SpvMemoryModelMax; |
356 | } |
357 | |
358 | /// Returns the addressing model of this module, or Logical if uninitialized. |
359 | SpvAddressingModel addressing_model() const; |
360 | |
361 | /// Returns the addressing model of this module, or Logical if uninitialized. |
362 | uint32_t pointer_size_and_alignment() const { |
363 | return pointer_size_and_alignment_; |
364 | } |
365 | |
366 | /// Sets the memory model of this module. |
367 | void set_memory_model(SpvMemoryModel mm); |
368 | |
369 | /// Returns the memory model of this module, or Simple if uninitialized. |
370 | SpvMemoryModel memory_model() const; |
371 | |
372 | const AssemblyGrammar& grammar() const { return grammar_; } |
373 | |
374 | /// Inserts the instruction into the list of ordered instructions in the file. |
375 | Instruction* AddOrderedInstruction(const spv_parsed_instruction_t* inst); |
376 | |
377 | /// Registers the instruction. This will add the instruction to the list of |
378 | /// definitions and register sampled image consumers. |
379 | void RegisterInstruction(Instruction* inst); |
380 | |
381 | /// Registers the debug instruction information. |
382 | void RegisterDebugInstruction(const Instruction* inst); |
383 | |
384 | /// Registers the decoration for the given <id> |
385 | void RegisterDecorationForId(uint32_t id, const Decoration& dec) { |
386 | auto& dec_list = id_decorations_[id]; |
387 | auto lb = std::find(dec_list.begin(), dec_list.end(), dec); |
388 | if (lb == dec_list.end()) { |
389 | dec_list.push_back(dec); |
390 | } |
391 | } |
392 | |
393 | /// Registers the list of decorations for the given <id> |
394 | template <class InputIt> |
395 | void RegisterDecorationsForId(uint32_t id, InputIt begin, InputIt end) { |
396 | std::vector<Decoration>& cur_decs = id_decorations_[id]; |
397 | cur_decs.insert(cur_decs.end(), begin, end); |
398 | } |
399 | |
400 | /// Registers the list of decorations for the given member of the given |
401 | /// structure. |
402 | template <class InputIt> |
403 | void RegisterDecorationsForStructMember(uint32_t struct_id, |
404 | uint32_t member_index, InputIt begin, |
405 | InputIt end) { |
406 | RegisterDecorationsForId(struct_id, begin, end); |
407 | for (auto& decoration : id_decorations_[struct_id]) { |
408 | decoration.set_struct_member_index(member_index); |
409 | } |
410 | } |
411 | |
412 | /// Returns all the decorations for the given <id>. If no decorations exist |
413 | /// for the <id>, it registers an empty vector for it in the map and |
414 | /// returns the empty vector. |
415 | std::vector<Decoration>& id_decorations(uint32_t id) { |
416 | return id_decorations_[id]; |
417 | } |
418 | |
419 | // Returns const pointer to the internal decoration container. |
420 | const std::map<uint32_t, std::vector<Decoration>>& id_decorations() const { |
421 | return id_decorations_; |
422 | } |
423 | |
424 | /// Returns true if the given id <id> has the given decoration <dec>, |
425 | /// otherwise returns false. |
426 | bool HasDecoration(uint32_t id, SpvDecoration dec) { |
427 | const auto& decorations = id_decorations_.find(id); |
428 | if (decorations == id_decorations_.end()) return false; |
429 | |
430 | return std::any_of( |
431 | decorations->second.begin(), decorations->second.end(), |
432 | [dec](const Decoration& d) { return dec == d.dec_type(); }); |
433 | } |
434 | |
435 | /// Finds id's def, if it exists. If found, returns the definition otherwise |
436 | /// nullptr |
437 | const Instruction* FindDef(uint32_t id) const; |
438 | |
439 | /// Finds id's def, if it exists. If found, returns the definition otherwise |
440 | /// nullptr |
441 | Instruction* FindDef(uint32_t id); |
442 | |
443 | /// Returns the instructions in the order they appear in the binary |
444 | const std::vector<Instruction>& ordered_instructions() const { |
445 | return ordered_instructions_; |
446 | } |
447 | |
448 | /// Returns a map of instructions mapped by their result id |
449 | const std::unordered_map<uint32_t, Instruction*>& all_definitions() const { |
450 | return all_definitions_; |
451 | } |
452 | |
453 | /// Returns a vector containing the instructions that consume the given |
454 | /// SampledImage id. |
455 | std::vector<Instruction*> getSampledImageConsumers(uint32_t id) const; |
456 | |
457 | /// Records cons_id as a consumer of sampled_image_id. |
458 | void RegisterSampledImageConsumer(uint32_t sampled_image_id, |
459 | Instruction* consumer); |
460 | |
461 | /// Returns the set of Global Variables. |
462 | std::unordered_set<uint32_t>& global_vars() { return global_vars_; } |
463 | |
464 | /// Returns the set of Local Variables. |
465 | std::unordered_set<uint32_t>& local_vars() { return local_vars_; } |
466 | |
467 | /// Returns the number of Global Variables. |
468 | size_t num_global_vars() { return global_vars_.size(); } |
469 | |
470 | /// Returns the number of Local Variables. |
471 | size_t num_local_vars() { return local_vars_.size(); } |
472 | |
473 | /// Inserts a new <id> to the set of Global Variables. |
474 | void registerGlobalVariable(const uint32_t id) { global_vars_.insert(id); } |
475 | |
476 | /// Inserts a new <id> to the set of Local Variables. |
477 | void registerLocalVariable(const uint32_t id) { local_vars_.insert(id); } |
478 | |
479 | // Returns true if using relaxed block layout, equivalent to |
480 | // VK_KHR_relaxed_block_layout. |
481 | bool IsRelaxedBlockLayout() const { |
482 | return features_.env_relaxed_block_layout || options()->relax_block_layout; |
483 | } |
484 | |
485 | /// Sets the struct nesting depth for a given struct ID |
486 | void set_struct_nesting_depth(uint32_t id, uint32_t depth) { |
487 | struct_nesting_depth_[id] = depth; |
488 | } |
489 | |
490 | /// Returns the nesting depth of a given structure ID |
491 | uint32_t struct_nesting_depth(uint32_t id) { |
492 | return struct_nesting_depth_[id]; |
493 | } |
494 | |
495 | /// Records the has a nested block/bufferblock decorated struct for a given |
496 | /// struct ID |
497 | void SetHasNestedBlockOrBufferBlockStruct(uint32_t id, bool has) { |
498 | struct_has_nested_blockorbufferblock_struct_[id] = has; |
499 | } |
500 | |
501 | /// For a given struct ID returns true if it has a nested block/bufferblock |
502 | /// decorated struct |
503 | bool GetHasNestedBlockOrBufferBlockStruct(uint32_t id) { |
504 | return struct_has_nested_blockorbufferblock_struct_[id]; |
505 | } |
506 | |
507 | /// Records that the structure type has a member decorated with a built-in. |
508 | void RegisterStructTypeWithBuiltInMember(uint32_t id) { |
509 | builtin_structs_.insert(id); |
510 | } |
511 | |
512 | /// Returns true if the struct type with the given Id has a BuiltIn member. |
513 | bool IsStructTypeWithBuiltInMember(uint32_t id) const { |
514 | return (builtin_structs_.find(id) != builtin_structs_.end()); |
515 | } |
516 | |
517 | // Returns the state of optional features. |
518 | const Feature& features() const { return features_; } |
519 | |
520 | /// Adds the instruction data to unique_type_declarations_. |
521 | /// Returns false if an identical type declaration already exists. |
522 | bool RegisterUniqueTypeDeclaration(const Instruction* inst); |
523 | |
524 | // Returns type_id of the scalar component of |id|. |
525 | // |id| can be either |
526 | // - scalar, vector or matrix type |
527 | // - object of either scalar, vector or matrix type |
528 | uint32_t GetComponentType(uint32_t id) const; |
529 | |
530 | // Returns |
531 | // - 1 for scalar types or objects |
532 | // - vector size for vector types or objects |
533 | // - num columns for matrix types or objects |
534 | // Should not be called with any other arguments (will return zero and invoke |
535 | // assertion). |
536 | uint32_t GetDimension(uint32_t id) const; |
537 | |
538 | // Returns bit width of scalar or component. |
539 | // |id| can be |
540 | // - scalar, vector or matrix type |
541 | // - object of either scalar, vector or matrix type |
542 | // Will invoke assertion and return 0 if |id| is none of the above. |
543 | uint32_t GetBitWidth(uint32_t id) const; |
544 | |
545 | // Provides detailed information on matrix type. |
546 | // Returns false iff |id| is not matrix type. |
547 | bool GetMatrixTypeInfo(uint32_t id, uint32_t* num_rows, uint32_t* num_cols, |
548 | uint32_t* column_type, uint32_t* component_type) const; |
549 | |
550 | // Collects struct member types into |member_types|. |
551 | // Returns false iff not struct type or has no members. |
552 | // Deletes prior contents of |member_types|. |
553 | bool GetStructMemberTypes(uint32_t struct_type_id, |
554 | std::vector<uint32_t>* member_types) const; |
555 | |
556 | // Returns true iff |id| is a type corresponding to the name of the function. |
557 | // Only works for types not for objects. |
558 | bool IsVoidType(uint32_t id) const; |
559 | bool IsFloatScalarType(uint32_t id) const; |
560 | bool IsFloatVectorType(uint32_t id) const; |
561 | bool IsFloatScalarOrVectorType(uint32_t id) const; |
562 | bool IsFloatMatrixType(uint32_t id) const; |
563 | bool IsIntScalarType(uint32_t id) const; |
564 | bool IsIntVectorType(uint32_t id) const; |
565 | bool IsIntScalarOrVectorType(uint32_t id) const; |
566 | bool IsUnsignedIntScalarType(uint32_t id) const; |
567 | bool IsUnsignedIntVectorType(uint32_t id) const; |
568 | bool IsSignedIntScalarType(uint32_t id) const; |
569 | bool IsSignedIntVectorType(uint32_t id) const; |
570 | bool IsBoolScalarType(uint32_t id) const; |
571 | bool IsBoolVectorType(uint32_t id) const; |
572 | bool IsBoolScalarOrVectorType(uint32_t id) const; |
573 | bool IsPointerType(uint32_t id) const; |
574 | bool IsCooperativeMatrixType(uint32_t id) const; |
575 | bool IsFloatCooperativeMatrixType(uint32_t id) const; |
576 | bool IsIntCooperativeMatrixType(uint32_t id) const; |
577 | bool IsUnsignedIntCooperativeMatrixType(uint32_t id) const; |
578 | |
579 | // Returns true if |id| is a type id that contains |type| (or integer or |
580 | // floating point type) of |width| bits. |
581 | bool ContainsSizedIntOrFloatType(uint32_t id, SpvOp type, |
582 | uint32_t width) const; |
583 | // Returns true if |id| is a type id that contains a 8- or 16-bit int or |
584 | // 16-bit float that is not generally enabled for use. |
585 | bool ContainsLimitedUseIntOrFloatType(uint32_t id) const; |
586 | |
587 | // Gets value from OpConstant and OpSpecConstant as uint64. |
588 | // Returns false on failure (no instruction, wrong instruction, not int). |
589 | bool GetConstantValUint64(uint32_t id, uint64_t* val) const; |
590 | |
591 | // Returns type_id if id has type or zero otherwise. |
592 | uint32_t GetTypeId(uint32_t id) const; |
593 | |
594 | // Returns opcode of the instruction which issued the id or OpNop if the |
595 | // instruction is not registered. |
596 | SpvOp GetIdOpcode(uint32_t id) const; |
597 | |
598 | // Returns type_id for given id operand if it has a type or zero otherwise. |
599 | // |operand_index| is expected to be pointing towards an operand which is an |
600 | // id. |
601 | uint32_t GetOperandTypeId(const Instruction* inst, |
602 | size_t operand_index) const; |
603 | |
604 | // Provides information on pointer type. Returns false iff not pointer type. |
605 | bool GetPointerTypeInfo(uint32_t id, uint32_t* data_type, |
606 | uint32_t* storage_class) const; |
607 | |
608 | // Is the ID the type of a pointer to a uniform block: Block-decorated struct |
609 | // in uniform storage class? The result is only valid after internal method |
610 | // CheckDecorationsOfBuffers has been called. |
611 | bool IsPointerToUniformBlock(uint32_t type_id) const { |
612 | return pointer_to_uniform_block_.find(type_id) != |
613 | pointer_to_uniform_block_.cend(); |
614 | } |
615 | // Save the ID of a pointer to uniform block. |
616 | void RegisterPointerToUniformBlock(uint32_t type_id) { |
617 | pointer_to_uniform_block_.insert(type_id); |
618 | } |
619 | // Is the ID the type of a struct used as a uniform block? |
620 | // The result is only valid after internal method CheckDecorationsOfBuffers |
621 | // has been called. |
622 | bool IsStructForUniformBlock(uint32_t type_id) const { |
623 | return struct_for_uniform_block_.find(type_id) != |
624 | struct_for_uniform_block_.cend(); |
625 | } |
626 | // Save the ID of a struct of a uniform block. |
627 | void RegisterStructForUniformBlock(uint32_t type_id) { |
628 | struct_for_uniform_block_.insert(type_id); |
629 | } |
630 | // Is the ID the type of a pointer to a storage buffer: BufferBlock-decorated |
631 | // struct in uniform storage class, or Block-decorated struct in StorageBuffer |
632 | // storage class? The result is only valid after internal method |
633 | // CheckDecorationsOfBuffers has been called. |
634 | bool IsPointerToStorageBuffer(uint32_t type_id) const { |
635 | return pointer_to_storage_buffer_.find(type_id) != |
636 | pointer_to_storage_buffer_.cend(); |
637 | } |
638 | // Save the ID of a pointer to a storage buffer. |
639 | void RegisterPointerToStorageBuffer(uint32_t type_id) { |
640 | pointer_to_storage_buffer_.insert(type_id); |
641 | } |
642 | // Is the ID the type of a struct for storage buffer? |
643 | // The result is only valid after internal method CheckDecorationsOfBuffers |
644 | // has been called. |
645 | bool IsStructForStorageBuffer(uint32_t type_id) const { |
646 | return struct_for_storage_buffer_.find(type_id) != |
647 | struct_for_storage_buffer_.cend(); |
648 | } |
649 | // Save the ID of a struct of a storage buffer. |
650 | void RegisterStructForStorageBuffer(uint32_t type_id) { |
651 | struct_for_storage_buffer_.insert(type_id); |
652 | } |
653 | |
654 | // Is the ID the type of a pointer to a storage image? That is, the pointee |
655 | // type is an image type which is known to not use a sampler. |
656 | bool IsPointerToStorageImage(uint32_t type_id) const { |
657 | return pointer_to_storage_image_.find(type_id) != |
658 | pointer_to_storage_image_.cend(); |
659 | } |
660 | // Save the ID of a pointer to a storage image. |
661 | void RegisterPointerToStorageImage(uint32_t type_id) { |
662 | pointer_to_storage_image_.insert(type_id); |
663 | } |
664 | |
665 | // Tries to evaluate a 32-bit signed or unsigned scalar integer constant. |
666 | // Returns tuple <is_int32, is_const_int32, value>. |
667 | // OpSpecConstant* return |is_const_int32| as false since their values cannot |
668 | // be relied upon during validation. |
669 | std::tuple<bool, bool, uint32_t> EvalInt32IfConst(uint32_t id) const; |
670 | |
671 | // Returns the disassembly string for the given instruction. |
672 | std::string Disassemble(const Instruction& inst) const; |
673 | |
674 | // Returns the disassembly string for the given instruction. |
675 | std::string Disassemble(const uint32_t* words, uint16_t num_words) const; |
676 | |
677 | // Returns whether type m1 and type m2 are cooperative matrices with |
678 | // the same "shape" (matching scope, rows, cols). If any are specialization |
679 | // constants, we assume they can match because we can't prove they don't. |
680 | spv_result_t CooperativeMatrixShapesMatch(const Instruction* inst, |
681 | uint32_t m1, uint32_t m2); |
682 | |
683 | // Returns true if |lhs| and |rhs| logically match and, if the decorations of |
684 | // |rhs| are a subset of |lhs|. |
685 | // |
686 | // 1. Must both be either OpTypeArray or OpTypeStruct |
687 | // 2. If OpTypeArray, then |
688 | // * Length must be the same |
689 | // * Element type must match or logically match |
690 | // 3. If OpTypeStruct, then |
691 | // * Both have same number of elements |
692 | // * Element N for both structs must match or logically match |
693 | // |
694 | // If |check_decorations| is false, then the decorations are not checked. |
695 | bool LogicallyMatch(const Instruction* lhs, const Instruction* rhs, |
696 | bool check_decorations); |
697 | |
698 | // Traces |inst| to find a single base pointer. Returns the base pointer. |
699 | // Will trace through the following instructions: |
700 | // * OpAccessChain |
701 | // * OpInBoundsAccessChain |
702 | // * OpPtrAccessChain |
703 | // * OpInBoundsPtrAccessChain |
704 | // * OpCopyObject |
705 | const Instruction* TracePointer(const Instruction* inst) const; |
706 | |
707 | // Validates the storage class for the target environment. |
708 | bool IsValidStorageClass(SpvStorageClass storage_class) const; |
709 | |
710 | private: |
711 | ValidationState_t(const ValidationState_t&); |
712 | |
713 | const spv_const_context context_; |
714 | |
715 | /// Stores the Validator command line options. Must be a valid options object. |
716 | const spv_const_validator_options options_; |
717 | |
718 | /// The SPIR-V binary module we're validating. |
719 | const uint32_t* words_; |
720 | const size_t num_words_; |
721 | |
722 | /// The generator of the SPIR-V. |
723 | uint32_t generator_ = 0; |
724 | |
725 | /// The version of the SPIR-V. |
726 | uint32_t version_ = 0; |
727 | |
728 | /// The total number of instructions in the binary. |
729 | size_t total_instructions_ = 0; |
730 | /// The total number of functions in the binary. |
731 | size_t total_functions_ = 0; |
732 | |
733 | /// IDs which have been forward declared but have not been defined |
734 | std::unordered_set<uint32_t> unresolved_forward_ids_; |
735 | |
736 | /// IDs that have been declared as forward pointers. |
737 | std::unordered_set<uint32_t> forward_pointer_ids_; |
738 | |
739 | /// Stores a vector of instructions that use the result of a given |
740 | /// OpSampledImage instruction. |
741 | std::unordered_map<uint32_t, std::vector<Instruction*>> |
742 | sampled_image_consumers_; |
743 | |
744 | /// A map of operand IDs and their names defined by the OpName instruction |
745 | std::unordered_map<uint32_t, std::string> operand_names_; |
746 | |
747 | /// The section of the code being processed |
748 | ModuleLayoutSection current_layout_section_; |
749 | |
750 | /// A list of functions in the module. |
751 | /// Pointers to objects in this container are guaranteed to be stable and |
752 | /// valid until the end of lifetime of the validation state. |
753 | std::vector<Function> module_functions_; |
754 | |
755 | /// Capabilities declared in the module |
756 | CapabilitySet module_capabilities_; |
757 | |
758 | /// Extensions declared in the module |
759 | ExtensionSet module_extensions_; |
760 | |
761 | /// List of all instructions in the order they appear in the binary |
762 | std::vector<Instruction> ordered_instructions_; |
763 | |
764 | /// Instructions that can be referenced by Ids |
765 | std::unordered_map<uint32_t, Instruction*> all_definitions_; |
766 | |
767 | /// IDs that are entry points, ie, arguments to OpEntryPoint. |
768 | std::vector<uint32_t> entry_points_; |
769 | |
770 | /// Maps an entry point id to its desciptions. |
771 | std::unordered_map<uint32_t, std::vector<EntryPointDescription>> |
772 | entry_point_descriptions_; |
773 | |
774 | /// IDs that are entry points, ie, arguments to OpEntryPoint, and root a call |
775 | /// graph that recurses. |
776 | std::set<uint32_t> recursive_entry_points_; |
777 | |
778 | /// Functions IDs that are target of OpFunctionCall. |
779 | std::unordered_set<uint32_t> function_call_targets_; |
780 | |
781 | /// ID Bound from the Header |
782 | uint32_t id_bound_; |
783 | |
784 | /// Set of Global Variable IDs (Storage Class other than 'Function') |
785 | std::unordered_set<uint32_t> global_vars_; |
786 | |
787 | /// Set of Local Variable IDs ('Function' Storage Class) |
788 | std::unordered_set<uint32_t> local_vars_; |
789 | |
790 | /// Set of struct types that have members with a BuiltIn decoration. |
791 | std::unordered_set<uint32_t> builtin_structs_; |
792 | |
793 | /// Structure Nesting Depth |
794 | std::unordered_map<uint32_t, uint32_t> struct_nesting_depth_; |
795 | |
796 | /// Structure has nested blockorbufferblock struct |
797 | std::unordered_map<uint32_t, bool> |
798 | struct_has_nested_blockorbufferblock_struct_; |
799 | |
800 | /// Stores the list of decorations for a given <id> |
801 | std::map<uint32_t, std::vector<Decoration>> id_decorations_; |
802 | |
803 | /// Stores type declarations which need to be unique (i.e. non-aggregates), |
804 | /// in the form [opcode, operand words], result_id is not stored. |
805 | /// Using ordered set to avoid the need for a vector hash function. |
806 | /// The size of this container is expected not to exceed double-digits. |
807 | std::set<std::vector<uint32_t>> unique_type_declarations_; |
808 | |
809 | AssemblyGrammar grammar_; |
810 | |
811 | SpvAddressingModel addressing_model_; |
812 | SpvMemoryModel memory_model_; |
813 | // pointer size derived from addressing model. Assumes all storage classes |
814 | // have the same pointer size (for physical pointer types). |
815 | uint32_t pointer_size_and_alignment_; |
816 | |
817 | /// NOTE: See correspoding getter functions |
818 | bool in_function_; |
819 | |
820 | /// The state of optional features. These are determined by capabilities |
821 | /// declared by the module and the environment. |
822 | Feature features_; |
823 | |
824 | /// Maps function ids to function stat objects. |
825 | std::unordered_map<uint32_t, Function*> id_to_function_; |
826 | |
827 | /// Mapping entry point -> execution models. It is presumed that the same |
828 | /// function could theoretically be used as 'main' by multiple OpEntryPoint |
829 | /// instructions. |
830 | std::unordered_map<uint32_t, std::set<SpvExecutionModel>> |
831 | entry_point_to_execution_models_; |
832 | |
833 | /// Mapping entry point -> execution modes. |
834 | std::unordered_map<uint32_t, std::set<SpvExecutionMode>> |
835 | entry_point_to_execution_modes_; |
836 | |
837 | /// Mapping function -> array of entry points inside this |
838 | /// module which can (indirectly) call the function. |
839 | std::unordered_map<uint32_t, std::vector<uint32_t>> function_to_entry_points_; |
840 | const std::vector<uint32_t> empty_ids_; |
841 | |
842 | // The IDs of types of pointers to Block-decorated structs in Uniform storage |
843 | // class. This is populated at the start of ValidateDecorations. |
844 | std::unordered_set<uint32_t> pointer_to_uniform_block_; |
845 | // The IDs of struct types for uniform blocks. |
846 | // This is populated at the start of ValidateDecorations. |
847 | std::unordered_set<uint32_t> struct_for_uniform_block_; |
848 | // The IDs of types of pointers to BufferBlock-decorated structs in Uniform |
849 | // storage class, or Block-decorated structs in StorageBuffer storage class. |
850 | // This is populated at the start of ValidateDecorations. |
851 | std::unordered_set<uint32_t> pointer_to_storage_buffer_; |
852 | // The IDs of struct types for storage buffers. |
853 | // This is populated at the start of ValidateDecorations. |
854 | std::unordered_set<uint32_t> struct_for_storage_buffer_; |
855 | // The IDs of types of pointers to storage images. This is populated in the |
856 | // TypePass. |
857 | std::unordered_set<uint32_t> pointer_to_storage_image_; |
858 | |
859 | /// Maps ids to friendly names. |
860 | std::unique_ptr<spvtools::FriendlyNameMapper> friendly_mapper_; |
861 | spvtools::NameMapper name_mapper_; |
862 | |
863 | /// Variables used to reduce the number of diagnostic messages. |
864 | uint32_t num_of_warnings_; |
865 | uint32_t max_num_of_warnings_; |
866 | }; |
867 | |
868 | } // namespace val |
869 | } // namespace spvtools |
870 | |
871 | #endif // SOURCE_VAL_VALIDATION_STATE_H_ |
872 | |