/src/spirv-tools/source/val/validate_instruction.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | // Performs validation on instructions that appear inside of a SPIR-V block. |
16 | | |
17 | | #include <cassert> |
18 | | #include <sstream> |
19 | | #include <string> |
20 | | #include <vector> |
21 | | |
22 | | #include "source/enum_set.h" |
23 | | #include "source/enum_string_mapping.h" |
24 | | #include "source/extensions.h" |
25 | | #include "source/opcode.h" |
26 | | #include "source/operand.h" |
27 | | #include "source/spirv_constant.h" |
28 | | #include "source/spirv_target_env.h" |
29 | | #include "source/spirv_validator_options.h" |
30 | | #include "source/util/string_utils.h" |
31 | | #include "source/val/validate.h" |
32 | | #include "source/val/validation_state.h" |
33 | | |
34 | | namespace spvtools { |
35 | | namespace val { |
36 | | namespace { |
37 | | |
38 | | std::string ToString(const CapabilitySet& capabilities, |
39 | 334 | const AssemblyGrammar& grammar) { |
40 | 334 | std::stringstream ss; |
41 | 430 | for (auto capability : capabilities) { |
42 | 430 | spv_operand_desc desc; |
43 | 430 | if (SPV_SUCCESS == grammar.lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, |
44 | 430 | uint32_t(capability), &desc)) |
45 | 430 | ss << desc->name << " "; |
46 | 0 | else |
47 | 0 | ss << uint32_t(capability) << " "; |
48 | 430 | } |
49 | 334 | return ss.str(); |
50 | 334 | } |
51 | | |
52 | | // Returns capabilities that enable an opcode. An empty result is interpreted |
53 | | // as no prohibition of use of the opcode. If the result is non-empty, then |
54 | | // the opcode may only be used if at least one of the capabilities is specified |
55 | | // by the module. |
56 | | CapabilitySet EnablingCapabilitiesForOp(const ValidationState_t& state, |
57 | 10.6M | spv::Op opcode) { |
58 | | // Exceptions for SPV_AMD_shader_ballot |
59 | 10.6M | switch (opcode) { |
60 | | // Normally these would require Group capability |
61 | 0 | case spv::Op::OpGroupIAddNonUniformAMD: |
62 | 0 | case spv::Op::OpGroupFAddNonUniformAMD: |
63 | 0 | case spv::Op::OpGroupFMinNonUniformAMD: |
64 | 0 | case spv::Op::OpGroupUMinNonUniformAMD: |
65 | 0 | case spv::Op::OpGroupSMinNonUniformAMD: |
66 | 0 | case spv::Op::OpGroupFMaxNonUniformAMD: |
67 | 0 | case spv::Op::OpGroupUMaxNonUniformAMD: |
68 | 0 | case spv::Op::OpGroupSMaxNonUniformAMD: |
69 | 0 | if (state.HasExtension(kSPV_AMD_shader_ballot)) return CapabilitySet(); |
70 | 0 | break; |
71 | 10.6M | default: |
72 | 10.6M | break; |
73 | 10.6M | } |
74 | | // Look it up in the grammar |
75 | 10.6M | spv_opcode_desc opcode_desc = {}; |
76 | 10.6M | if (SPV_SUCCESS == state.grammar().lookupOpcode(opcode, &opcode_desc)) { |
77 | 10.6M | return state.grammar().filterCapsAgainstTargetEnv( |
78 | 10.6M | opcode_desc->capabilities, opcode_desc->numCapabilities); |
79 | 10.6M | } |
80 | 0 | return CapabilitySet(); |
81 | 10.6M | } |
82 | | |
83 | | // Returns SPV_SUCCESS if, for the given operand, the target environment |
84 | | // satsifies minimum version requirements, or if the module declares an |
85 | | // enabling extension for the operand. Otherwise emit a diagnostic and |
86 | | // return an error code. |
87 | | spv_result_t OperandVersionExtensionCheck( |
88 | | ValidationState_t& _, const Instruction* inst, size_t which_operand, |
89 | 1.46M | const spv_operand_desc_t& operand_desc, uint32_t word) { |
90 | 1.46M | const uint32_t module_version = _.version(); |
91 | 1.46M | const uint32_t operand_min_version = operand_desc.minVersion; |
92 | 1.46M | const uint32_t operand_last_version = operand_desc.lastVersion; |
93 | 1.46M | const bool reserved = operand_min_version == 0xffffffffu; |
94 | 1.46M | const bool version_satisfied = !reserved && |
95 | 1.46M | (operand_min_version <= module_version) && |
96 | 1.46M | (module_version <= operand_last_version); |
97 | | |
98 | 1.46M | if (version_satisfied) { |
99 | 1.46M | return SPV_SUCCESS; |
100 | 1.46M | } |
101 | | |
102 | 4.80k | if (operand_last_version < module_version) { |
103 | 6 | return _.diag(SPV_ERROR_WRONG_VERSION, inst) |
104 | 6 | << spvtools::utils::CardinalToOrdinal(which_operand) |
105 | 6 | << " operand of " << spvOpcodeString(inst->opcode()) << ": operand " |
106 | 6 | << operand_desc.name << "(" << word << ") requires SPIR-V version " |
107 | 6 | << SPV_SPIRV_VERSION_MAJOR_PART(operand_last_version) << "." |
108 | 6 | << SPV_SPIRV_VERSION_MINOR_PART(operand_last_version) |
109 | 6 | << " or earlier"; |
110 | 6 | } |
111 | | |
112 | 4.80k | if (!reserved && operand_desc.numExtensions == 0) { |
113 | 43 | return _.diag(SPV_ERROR_WRONG_VERSION, inst) |
114 | 43 | << spvtools::utils::CardinalToOrdinal(which_operand) |
115 | 43 | << " operand of " << spvOpcodeString(inst->opcode()) << ": operand " |
116 | 43 | << operand_desc.name << "(" << word << ") requires SPIR-V version " |
117 | 43 | << SPV_SPIRV_VERSION_MAJOR_PART(operand_min_version) << "." |
118 | 43 | << SPV_SPIRV_VERSION_MINOR_PART(operand_min_version) << " or later"; |
119 | 4.76k | } else { |
120 | 4.76k | ExtensionSet required_extensions(operand_desc.numExtensions, |
121 | 4.76k | operand_desc.extensions); |
122 | 4.76k | if (!_.HasAnyOfExtensions(required_extensions)) { |
123 | 275 | return _.diag(SPV_ERROR_MISSING_EXTENSION, inst) |
124 | 275 | << spvtools::utils::CardinalToOrdinal(which_operand) |
125 | 275 | << " operand of " << spvOpcodeString(inst->opcode()) |
126 | 275 | << ": operand " << operand_desc.name << "(" << word |
127 | 275 | << ") requires one of these extensions: " |
128 | 275 | << ExtensionSetToString(required_extensions); |
129 | 275 | } |
130 | 4.76k | } |
131 | 4.48k | return SPV_SUCCESS; |
132 | 4.80k | } |
133 | | |
134 | | // Returns SPV_SUCCESS if the given operand is enabled by capabilities declared |
135 | | // in the module. Otherwise issues an error message and returns |
136 | | // SPV_ERROR_INVALID_CAPABILITY. |
137 | | spv_result_t CheckRequiredCapabilities(ValidationState_t& state, |
138 | | const Instruction* inst, |
139 | | size_t which_operand, |
140 | | const spv_parsed_operand_t& operand, |
141 | 2.74M | uint32_t word) { |
142 | | // Mere mention of PointSize, ClipDistance, or CullDistance in a Builtin |
143 | | // decoration does not require the associated capability. The use of such |
144 | | // a variable value should trigger the capability requirement, but that's |
145 | | // not implemented yet. This rule is independent of target environment. |
146 | | // See https://github.com/KhronosGroup/SPIRV-Tools/issues/365 |
147 | 2.74M | if (operand.type == SPV_OPERAND_TYPE_BUILT_IN) { |
148 | 3.78k | switch (spv::BuiltIn(word)) { |
149 | 111 | case spv::BuiltIn::PointSize: |
150 | 450 | case spv::BuiltIn::ClipDistance: |
151 | 499 | case spv::BuiltIn::CullDistance: |
152 | 499 | return SPV_SUCCESS; |
153 | 3.28k | default: |
154 | 3.28k | break; |
155 | 3.78k | } |
156 | 2.74M | } else if (operand.type == SPV_OPERAND_TYPE_FP_ROUNDING_MODE) { |
157 | | // Allow all FP rounding modes if requested |
158 | 69 | if (state.features().free_fp_rounding_mode) { |
159 | 0 | return SPV_SUCCESS; |
160 | 0 | } |
161 | 2.74M | } else if (operand.type == SPV_OPERAND_TYPE_GROUP_OPERATION && |
162 | 2.74M | state.features().group_ops_reduce_and_scans && |
163 | 2.74M | (word <= uint32_t(spv::GroupOperation::ExclusiveScan))) { |
164 | | // Allow certain group operations if requested. |
165 | 0 | return SPV_SUCCESS; |
166 | 0 | } |
167 | | |
168 | 2.74M | CapabilitySet enabling_capabilities; |
169 | 2.74M | spv_operand_desc operand_desc = nullptr; |
170 | 2.74M | const auto lookup_result = |
171 | 2.74M | state.grammar().lookupOperand(operand.type, word, &operand_desc); |
172 | 2.74M | if (lookup_result == SPV_SUCCESS) { |
173 | | // Allow FPRoundingMode decoration if requested. |
174 | 1.46M | if (operand.type == SPV_OPERAND_TYPE_DECORATION && |
175 | 1.46M | spv::Decoration(operand_desc->value) == |
176 | 1.10M | spv::Decoration::FPRoundingMode) { |
177 | 69 | if (state.features().free_fp_rounding_mode) return SPV_SUCCESS; |
178 | | |
179 | | // Vulkan API requires more capabilities on rounding mode. |
180 | 69 | if (spvIsVulkanEnv(state.context()->target_env)) { |
181 | 0 | enabling_capabilities.insert( |
182 | 0 | spv::Capability::StorageUniformBufferBlock16); |
183 | 0 | enabling_capabilities.insert(spv::Capability::StorageUniform16); |
184 | 0 | enabling_capabilities.insert(spv::Capability::StoragePushConstant16); |
185 | 0 | enabling_capabilities.insert(spv::Capability::StorageInputOutput16); |
186 | 0 | } |
187 | 1.46M | } else { |
188 | 1.46M | enabling_capabilities = state.grammar().filterCapsAgainstTargetEnv( |
189 | 1.46M | operand_desc->capabilities, operand_desc->numCapabilities); |
190 | 1.46M | } |
191 | | |
192 | | // When encountering an OpCapability instruction, the instruction pass |
193 | | // registers a capability with the module *before* checking capabilities. |
194 | | // So in the case of an OpCapability instruction, don't bother checking |
195 | | // enablement by another capability. |
196 | 1.46M | if (inst->opcode() != spv::Op::OpCapability) { |
197 | 1.43M | const bool enabled_by_cap = |
198 | 1.43M | state.HasAnyOfCapabilities(enabling_capabilities); |
199 | 1.43M | if (!enabling_capabilities.empty() && !enabled_by_cap) { |
200 | 258 | return state.diag(SPV_ERROR_INVALID_CAPABILITY, inst) |
201 | 258 | << "Operand " << which_operand << " of " |
202 | 258 | << spvOpcodeString(inst->opcode()) |
203 | 258 | << " requires one of these capabilities: " |
204 | 258 | << ToString(enabling_capabilities, state.grammar()); |
205 | 258 | } |
206 | 1.43M | } |
207 | 1.46M | return OperandVersionExtensionCheck(state, inst, which_operand, |
208 | 1.46M | *operand_desc, word); |
209 | 1.46M | } |
210 | 1.27M | return SPV_SUCCESS; |
211 | 2.74M | } |
212 | | |
213 | | // Returns SPV_ERROR_INVALID_BINARY and emits a diagnostic if the instruction |
214 | | // is explicitly reserved in the SPIR-V core spec. Otherwise return |
215 | | // SPV_SUCCESS. |
216 | 10.6M | spv_result_t ReservedCheck(ValidationState_t& _, const Instruction* inst) { |
217 | 10.6M | const spv::Op opcode = inst->opcode(); |
218 | 10.6M | switch (opcode) { |
219 | | // These instructions are enabled by a capability, but should never |
220 | | // be used anyway. |
221 | 0 | case spv::Op::OpImageSparseSampleProjImplicitLod: |
222 | 1 | case spv::Op::OpImageSparseSampleProjExplicitLod: |
223 | 2 | case spv::Op::OpImageSparseSampleProjDrefImplicitLod: |
224 | 2 | case spv::Op::OpImageSparseSampleProjDrefExplicitLod: { |
225 | 2 | spv_opcode_desc inst_desc; |
226 | 2 | _.grammar().lookupOpcode(opcode, &inst_desc); |
227 | 2 | return _.diag(SPV_ERROR_INVALID_BINARY, inst) |
228 | 2 | << "Invalid Opcode name 'Op" << inst_desc->name << "'"; |
229 | 2 | } |
230 | 10.6M | default: |
231 | 10.6M | break; |
232 | 10.6M | } |
233 | 10.6M | return SPV_SUCCESS; |
234 | 10.6M | } |
235 | | |
236 | | // Returns SPV_ERROR_INVALID_CAPABILITY and emits a diagnostic if the |
237 | | // instruction is invalid because the required capability isn't declared |
238 | | // in the module. |
239 | 10.6M | spv_result_t CapabilityCheck(ValidationState_t& _, const Instruction* inst) { |
240 | 10.6M | const spv::Op opcode = inst->opcode(); |
241 | 10.6M | CapabilitySet opcode_caps = EnablingCapabilitiesForOp(_, opcode); |
242 | 10.6M | if (!_.HasAnyOfCapabilities(opcode_caps)) { |
243 | 76 | return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst) |
244 | 76 | << "Opcode " << spvOpcodeString(opcode) |
245 | 76 | << " requires one of these capabilities: " |
246 | 76 | << ToString(opcode_caps, _.grammar()); |
247 | 76 | } |
248 | 27.4M | for (size_t i = 0; i < inst->operands().size(); ++i) { |
249 | 16.8M | const auto& operand = inst->operand(i); |
250 | 16.8M | const auto word = inst->word(operand.offset); |
251 | 16.8M | if (spvOperandIsConcreteMask(operand.type)) { |
252 | | // Check for required capabilities for each bit position of the mask. |
253 | 4.90M | for (uint32_t mask_bit = 0x80000000; mask_bit; mask_bit >>= 1) { |
254 | 4.75M | if (word & mask_bit) { |
255 | 54.8k | spv_result_t status = |
256 | 54.8k | CheckRequiredCapabilities(_, inst, i + 1, operand, mask_bit); |
257 | 54.8k | if (status != SPV_SUCCESS) return status; |
258 | 54.8k | } |
259 | 4.75M | } |
260 | 16.6M | } else if (spvIsIdType(operand.type)) { |
261 | | // TODO(dneto): Check the value referenced by this Id, if we can compute |
262 | | // it. For now, just punt, to fix issue 248: |
263 | | // https://github.com/KhronosGroup/SPIRV-Tools/issues/248 |
264 | 13.9M | } else { |
265 | | // Check the operand word as a whole. |
266 | 2.69M | spv_result_t status = |
267 | 2.69M | CheckRequiredCapabilities(_, inst, i + 1, operand, word); |
268 | 2.69M | if (status != SPV_SUCCESS) return status; |
269 | 2.69M | } |
270 | 16.8M | } |
271 | 10.6M | return SPV_SUCCESS; |
272 | 10.6M | } |
273 | | |
274 | | // Checks that the instruction can be used in this target environment's base |
275 | | // version. Assumes that CapabilityCheck has checked direct capability |
276 | | // dependencies for the opcode. |
277 | 10.6M | spv_result_t VersionCheck(ValidationState_t& _, const Instruction* inst) { |
278 | 10.6M | const auto opcode = inst->opcode(); |
279 | 10.6M | spv_opcode_desc inst_desc; |
280 | 10.6M | const spv_result_t r = _.grammar().lookupOpcode(opcode, &inst_desc); |
281 | 10.6M | assert(r == SPV_SUCCESS); |
282 | 10.6M | (void)r; |
283 | | |
284 | 10.6M | const auto min_version = inst_desc->minVersion; |
285 | 10.6M | const auto last_version = inst_desc->lastVersion; |
286 | 10.6M | const auto module_version = _.version(); |
287 | | |
288 | 10.6M | if (last_version < module_version) { |
289 | 0 | return _.diag(SPV_ERROR_WRONG_VERSION, inst) |
290 | 0 | << spvOpcodeString(opcode) << " requires SPIR-V version " |
291 | 0 | << SPV_SPIRV_VERSION_MAJOR_PART(last_version) << "." |
292 | 0 | << SPV_SPIRV_VERSION_MINOR_PART(last_version) << " or earlier"; |
293 | 0 | } |
294 | | |
295 | | // OpTerminateInvocation is special because it is enabled by Shader |
296 | | // capability, but also requires an extension and/or version check. |
297 | 10.6M | const bool capability_check_is_sufficient = |
298 | 10.6M | inst->opcode() != spv::Op::OpTerminateInvocation; |
299 | | |
300 | 10.6M | if (capability_check_is_sufficient && (inst_desc->numCapabilities > 0u)) { |
301 | | // We already checked that the direct capability dependency has been |
302 | | // satisfied. We don't need to check any further. |
303 | 21.8k | return SPV_SUCCESS; |
304 | 21.8k | } |
305 | | |
306 | 10.6M | ExtensionSet exts(inst_desc->numExtensions, inst_desc->extensions); |
307 | 10.6M | if (exts.empty()) { |
308 | | // If no extensions can enable this instruction, then emit error |
309 | | // messages only concerning core SPIR-V versions if errors happen. |
310 | 10.6M | if (min_version == ~0u) { |
311 | 0 | return _.diag(SPV_ERROR_WRONG_VERSION, inst) |
312 | 0 | << spvOpcodeString(opcode) << " is reserved for future use."; |
313 | 0 | } |
314 | | |
315 | 10.6M | if (module_version < min_version) { |
316 | 0 | return _.diag(SPV_ERROR_WRONG_VERSION, inst) |
317 | 0 | << spvOpcodeString(opcode) << " requires SPIR-V version " |
318 | 0 | << SPV_SPIRV_VERSION_MAJOR_PART(min_version) << "." |
319 | 0 | << SPV_SPIRV_VERSION_MINOR_PART(min_version) << " at minimum."; |
320 | 0 | } |
321 | 10.6M | } else if (!_.HasAnyOfExtensions(exts)) { |
322 | | // Otherwise, we only error out when no enabling extensions are |
323 | | // registered. |
324 | 12 | if (min_version == ~0u) { |
325 | 0 | return _.diag(SPV_ERROR_MISSING_EXTENSION, inst) |
326 | 0 | << spvOpcodeString(opcode) |
327 | 0 | << " requires one of the following extensions: " |
328 | 0 | << ExtensionSetToString(exts); |
329 | 0 | } |
330 | | |
331 | 12 | if (module_version < min_version) { |
332 | 12 | return _.diag(SPV_ERROR_WRONG_VERSION, inst) |
333 | 12 | << spvOpcodeString(opcode) << " requires SPIR-V version " |
334 | 12 | << SPV_SPIRV_VERSION_MAJOR_PART(min_version) << "." |
335 | 12 | << SPV_SPIRV_VERSION_MINOR_PART(min_version) |
336 | 12 | << " at minimum or one of the following extensions: " |
337 | 12 | << ExtensionSetToString(exts); |
338 | 12 | } |
339 | 12 | } |
340 | | |
341 | 10.6M | return SPV_SUCCESS; |
342 | 10.6M | } |
343 | | |
344 | | // Checks that the Resuld <id> is within the valid bound. |
345 | 10.6M | spv_result_t LimitCheckIdBound(ValidationState_t& _, const Instruction* inst) { |
346 | 10.6M | if (inst->id() >= _.getIdBound()) { |
347 | 671 | return _.diag(SPV_ERROR_INVALID_BINARY, inst) |
348 | 671 | << "Result <id> '" << inst->id() |
349 | 671 | << "' must be less than the ID bound '" << _.getIdBound() << "'."; |
350 | 671 | } |
351 | 10.6M | return SPV_SUCCESS; |
352 | 10.6M | } |
353 | | |
354 | | // Checks that the number of OpTypeStruct members is within the limit. |
355 | 10.6M | spv_result_t LimitCheckStruct(ValidationState_t& _, const Instruction* inst) { |
356 | 10.6M | if (spv::Op::OpTypeStruct != inst->opcode()) { |
357 | 10.6M | return SPV_SUCCESS; |
358 | 10.6M | } |
359 | | |
360 | | // Number of members is the number of operands of the instruction minus 1. |
361 | | // One operand is the result ID. |
362 | 17.8k | const uint16_t limit = |
363 | 17.8k | static_cast<uint16_t>(_.options()->universal_limits_.max_struct_members); |
364 | 17.8k | if (inst->operands().size() - 1 > limit) { |
365 | 0 | return _.diag(SPV_ERROR_INVALID_BINARY, inst) |
366 | 0 | << "Number of OpTypeStruct members (" << inst->operands().size() - 1 |
367 | 0 | << ") has exceeded the limit (" << limit << ")."; |
368 | 0 | } |
369 | | |
370 | | // Section 2.17 of SPIRV Spec specifies that the "Structure Nesting Depth" |
371 | | // must be less than or equal to 255. |
372 | | // This is interpreted as structures including other structures as |
373 | | // members. The code does not follow pointers or look into arrays to see |
374 | | // if we reach a structure downstream. The nesting depth of a struct is |
375 | | // 1+(largest depth of any member). Scalars are at depth 0. |
376 | 17.8k | uint32_t max_member_depth = 0; |
377 | | // Struct members start at word 2 of OpTypeStruct instruction. |
378 | 34.2k | for (size_t word_i = 2; word_i < inst->words().size(); ++word_i) { |
379 | 16.3k | auto member = inst->word(word_i); |
380 | 16.3k | auto memberTypeInstr = _.FindDef(member); |
381 | 16.3k | if (memberTypeInstr && spv::Op::OpTypeStruct == memberTypeInstr->opcode()) { |
382 | 1.32k | max_member_depth = std::max( |
383 | 1.32k | max_member_depth, _.struct_nesting_depth(memberTypeInstr->id())); |
384 | 1.32k | } |
385 | 16.3k | } |
386 | | |
387 | 17.8k | const uint32_t depth_limit = _.options()->universal_limits_.max_struct_depth; |
388 | 17.8k | const uint32_t cur_depth = 1 + max_member_depth; |
389 | 17.8k | _.set_struct_nesting_depth(inst->id(), cur_depth); |
390 | 17.8k | if (cur_depth > depth_limit) { |
391 | 0 | return _.diag(SPV_ERROR_INVALID_BINARY, inst) |
392 | 0 | << "Structure Nesting Depth may not be larger than " << depth_limit |
393 | 0 | << ". Found " << cur_depth << "."; |
394 | 0 | } |
395 | 17.8k | return SPV_SUCCESS; |
396 | 17.8k | } |
397 | | |
398 | | // Checks that the number of (literal, label) pairs in OpSwitch is within |
399 | | // the limit. |
400 | 10.6M | spv_result_t LimitCheckSwitch(ValidationState_t& _, const Instruction* inst) { |
401 | 10.6M | if (spv::Op::OpSwitch == inst->opcode()) { |
402 | | // The instruction syntax is as follows: |
403 | | // OpSwitch <selector ID> <Default ID> literal label literal label ... |
404 | | // literal,label pairs come after the first 2 operands. |
405 | | // It is guaranteed at this point that num_operands is an even number. |
406 | 11.7k | size_t num_pairs = (inst->operands().size() - 2) / 2; |
407 | 11.7k | const unsigned int num_pairs_limit = |
408 | 11.7k | _.options()->universal_limits_.max_switch_branches; |
409 | 11.7k | if (num_pairs > num_pairs_limit) { |
410 | 0 | return _.diag(SPV_ERROR_INVALID_BINARY, inst) |
411 | 0 | << "Number of (literal, label) pairs in OpSwitch (" << num_pairs |
412 | 0 | << ") exceeds the limit (" << num_pairs_limit << ")."; |
413 | 0 | } |
414 | 11.7k | } |
415 | 10.6M | return SPV_SUCCESS; |
416 | 10.6M | } |
417 | | |
418 | | // Ensure the number of variables of the given class does not exceed the |
419 | | // limit. |
420 | | spv_result_t LimitCheckNumVars(ValidationState_t& _, const uint32_t var_id, |
421 | 109k | const spv::StorageClass storage_class) { |
422 | 109k | if (spv::StorageClass::Function == storage_class) { |
423 | 76.7k | _.registerLocalVariable(var_id); |
424 | 76.7k | const uint32_t num_local_vars_limit = |
425 | 76.7k | _.options()->universal_limits_.max_local_variables; |
426 | 76.7k | if (_.num_local_vars() > num_local_vars_limit) { |
427 | 0 | return _.diag(SPV_ERROR_INVALID_BINARY, nullptr) |
428 | 0 | << "Number of local variables ('Function' Storage Class) " |
429 | 0 | "exceeded the valid limit (" |
430 | 0 | << num_local_vars_limit << ")."; |
431 | 0 | } |
432 | 76.7k | } else { |
433 | 32.2k | _.registerGlobalVariable(var_id); |
434 | 32.2k | const uint32_t num_global_vars_limit = |
435 | 32.2k | _.options()->universal_limits_.max_global_variables; |
436 | 32.2k | if (_.num_global_vars() > num_global_vars_limit) { |
437 | 0 | return _.diag(SPV_ERROR_INVALID_BINARY, nullptr) |
438 | 0 | << "Number of Global Variables (Storage Class other than " |
439 | 0 | "'Function') exceeded the valid limit (" |
440 | 0 | << num_global_vars_limit << ")."; |
441 | 0 | } |
442 | 32.2k | } |
443 | 109k | return SPV_SUCCESS; |
444 | 109k | } |
445 | | |
446 | | // Parses OpExtension instruction and logs warnings if unsuccessful. |
447 | | spv_result_t CheckIfKnownExtension(ValidationState_t& _, |
448 | 8.02k | const Instruction* inst) { |
449 | 8.02k | const std::string extension_str = GetExtensionString(&(inst->c_inst())); |
450 | 8.02k | Extension extension; |
451 | 8.02k | if (!GetExtensionFromString(extension_str.c_str(), &extension)) { |
452 | 6.64k | return _.diag(SPV_WARNING, inst) |
453 | 6.64k | << "Found unrecognized extension " << extension_str; |
454 | 6.64k | } |
455 | 1.37k | return SPV_SUCCESS; |
456 | 8.02k | } |
457 | | |
458 | | } // namespace |
459 | | |
460 | 10.6M | spv_result_t InstructionPass(ValidationState_t& _, const Instruction* inst) { |
461 | 10.6M | const spv::Op opcode = inst->opcode(); |
462 | 10.6M | if (opcode == spv::Op::OpExtension) { |
463 | 8.02k | CheckIfKnownExtension(_, inst); |
464 | 10.6M | } else if (opcode == spv::Op::OpCapability) { |
465 | 35.0k | _.RegisterCapability(inst->GetOperandAs<spv::Capability>(0)); |
466 | 10.5M | } else if (opcode == spv::Op::OpMemoryModel) { |
467 | 24.5k | if (_.has_memory_model_specified()) { |
468 | 12 | return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) |
469 | 12 | << "OpMemoryModel should only be provided once."; |
470 | 12 | } |
471 | 24.5k | _.set_addressing_model(inst->GetOperandAs<spv::AddressingModel>(0)); |
472 | 24.5k | _.set_memory_model(inst->GetOperandAs<spv::MemoryModel>(1)); |
473 | 10.5M | } else if (opcode == spv::Op::OpExecutionMode || |
474 | 10.5M | opcode == spv::Op::OpExecutionModeId) { |
475 | 26.1k | const uint32_t entry_point = inst->word(1); |
476 | 26.1k | _.RegisterExecutionModeForEntryPoint(entry_point, |
477 | 26.1k | spv::ExecutionMode(inst->word(2))); |
478 | 10.5M | } else if (opcode == spv::Op::OpVariable) { |
479 | 109k | const auto storage_class = inst->GetOperandAs<spv::StorageClass>(2); |
480 | 109k | if (auto error = LimitCheckNumVars(_, inst->id(), storage_class)) { |
481 | 0 | return error; |
482 | 0 | } |
483 | 10.4M | } else if (opcode == spv::Op::OpSamplerImageAddressingModeNV) { |
484 | 2 | if (!_.HasCapability(spv::Capability::BindlessTextureNV)) { |
485 | 2 | return _.diag(SPV_ERROR_MISSING_EXTENSION, inst) |
486 | 2 | << "OpSamplerImageAddressingModeNV supported only with extension " |
487 | 2 | "SPV_NV_bindless_texture"; |
488 | 2 | } |
489 | 0 | uint32_t bitwidth = inst->GetOperandAs<uint32_t>(0); |
490 | 0 | if (_.samplerimage_variable_address_mode() != 0) { |
491 | 0 | return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) |
492 | 0 | << "OpSamplerImageAddressingModeNV should only be provided once"; |
493 | 0 | } |
494 | 0 | if (bitwidth != 32 && bitwidth != 64) { |
495 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
496 | 0 | << "OpSamplerImageAddressingModeNV bitwidth should be 64 or 32"; |
497 | 0 | } |
498 | 0 | _.set_samplerimage_variable_address_mode(bitwidth); |
499 | 0 | } |
500 | | |
501 | 10.6M | if (auto error = ReservedCheck(_, inst)) return error; |
502 | 10.6M | if (auto error = CapabilityCheck(_, inst)) return error; |
503 | 10.6M | if (auto error = LimitCheckIdBound(_, inst)) return error; |
504 | 10.6M | if (auto error = LimitCheckStruct(_, inst)) return error; |
505 | 10.6M | if (auto error = LimitCheckSwitch(_, inst)) return error; |
506 | 10.6M | if (auto error = VersionCheck(_, inst)) return error; |
507 | | |
508 | | // All instruction checks have passed. |
509 | 10.6M | return SPV_SUCCESS; |
510 | 10.6M | } |
511 | | |
512 | | } // namespace val |
513 | | } // namespace spvtools |