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