1 // Copyright (c) 2018 Google LLC.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <algorithm>
16 #include <vector>
17
18 #include "source/spirv_constant.h"
19 #include "source/spirv_target_env.h"
20 #include "source/val/function.h"
21 #include "source/val/instruction.h"
22 #include "source/val/validate.h"
23 #include "source/val/validation_state.h"
24
25 namespace spvtools {
26 namespace val {
27 namespace {
28
29 // Limit the number of checked locations to 4096. Multiplied by 4 to represent
30 // all the components. This limit is set to be well beyond practical use cases.
31 const uint32_t kMaxLocations = 4096 * 4;
32
33 // Returns true if \c inst is an input or output variable.
is_interface_variable(const Instruction * inst,bool is_spv_1_4)34 bool is_interface_variable(const Instruction* inst, bool is_spv_1_4) {
35 if (is_spv_1_4) {
36 // Starting in SPIR-V 1.4, all global variables are interface variables.
37 return inst->opcode() == spv::Op::OpVariable &&
38 inst->GetOperandAs<spv::StorageClass>(2u) !=
39 spv::StorageClass::Function;
40 } else {
41 return inst->opcode() == spv::Op::OpVariable &&
42 (inst->GetOperandAs<spv::StorageClass>(2u) ==
43 spv::StorageClass::Input ||
44 inst->GetOperandAs<spv::StorageClass>(2u) ==
45 spv::StorageClass::Output);
46 }
47 }
48
49 // Checks that \c var is listed as an interface in all the entry points that use
50 // it.
check_interface_variable(ValidationState_t & _,const Instruction * var)51 spv_result_t check_interface_variable(ValidationState_t& _,
52 const Instruction* var) {
53 std::vector<const Function*> functions;
54 std::vector<const Instruction*> uses;
55 for (auto use : var->uses()) {
56 uses.push_back(use.first);
57 }
58 for (uint32_t i = 0; i < uses.size(); ++i) {
59 const auto user = uses[i];
60 if (const Function* func = user->function()) {
61 functions.push_back(func);
62 } else {
63 // In the rare case that the variable is used by another instruction in
64 // the global scope, continue searching for an instruction used in a
65 // function.
66 for (auto use : user->uses()) {
67 uses.push_back(use.first);
68 }
69 }
70 }
71
72 std::sort(functions.begin(), functions.end(),
73 [](const Function* lhs, const Function* rhs) {
74 return lhs->id() < rhs->id();
75 });
76 functions.erase(std::unique(functions.begin(), functions.end()),
77 functions.end());
78
79 std::vector<uint32_t> entry_points;
80 for (const auto func : functions) {
81 for (auto id : _.FunctionEntryPoints(func->id())) {
82 entry_points.push_back(id);
83 }
84 }
85
86 std::sort(entry_points.begin(), entry_points.end());
87 entry_points.erase(std::unique(entry_points.begin(), entry_points.end()),
88 entry_points.end());
89
90 for (auto id : entry_points) {
91 for (const auto& desc : _.entry_point_descriptions(id)) {
92 bool found = false;
93 for (auto interface : desc.interfaces) {
94 if (var->id() == interface) {
95 found = true;
96 break;
97 }
98 }
99 if (!found) {
100 return _.diag(SPV_ERROR_INVALID_ID, var)
101 << "Interface variable id <" << var->id()
102 << "> is used by entry point '" << desc.name << "' id <" << id
103 << ">, but is not listed as an interface";
104 }
105 }
106 }
107
108 return SPV_SUCCESS;
109 }
110
111 // This function assumes a base location has been determined already. As such
112 // any further location decorations are invalid.
113 // TODO: if this code turns out to be slow, there is an opportunity to cache
114 // the result for a given type id.
NumConsumedLocations(ValidationState_t & _,const Instruction * type,uint32_t * num_locations)115 spv_result_t NumConsumedLocations(ValidationState_t& _, const Instruction* type,
116 uint32_t* num_locations) {
117 *num_locations = 0;
118 switch (type->opcode()) {
119 case spv::Op::OpTypeInt:
120 case spv::Op::OpTypeFloat:
121 // Scalars always consume a single location.
122 *num_locations = 1;
123 break;
124 case spv::Op::OpTypeVector:
125 // 3- and 4-component 64-bit vectors consume two locations.
126 if ((_.ContainsSizedIntOrFloatType(type->id(), spv::Op::OpTypeInt, 64) ||
127 _.ContainsSizedIntOrFloatType(type->id(), spv::Op::OpTypeFloat,
128 64)) &&
129 (type->GetOperandAs<uint32_t>(2) > 2)) {
130 *num_locations = 2;
131 } else {
132 *num_locations = 1;
133 }
134 break;
135 case spv::Op::OpTypeMatrix:
136 // Matrices consume locations equal to the underlying vector type for
137 // each column.
138 NumConsumedLocations(_, _.FindDef(type->GetOperandAs<uint32_t>(1)),
139 num_locations);
140 *num_locations *= type->GetOperandAs<uint32_t>(2);
141 break;
142 case spv::Op::OpTypeArray: {
143 // Arrays consume locations equal to the underlying type times the number
144 // of elements in the vector.
145 NumConsumedLocations(_, _.FindDef(type->GetOperandAs<uint32_t>(1)),
146 num_locations);
147 bool is_int = false;
148 bool is_const = false;
149 uint32_t value = 0;
150 // Attempt to evaluate the number of array elements.
151 std::tie(is_int, is_const, value) =
152 _.EvalInt32IfConst(type->GetOperandAs<uint32_t>(2));
153 if (is_int && is_const) *num_locations *= value;
154 break;
155 }
156 case spv::Op::OpTypeStruct: {
157 // Members cannot have location decorations at this point.
158 if (_.HasDecoration(type->id(), spv::Decoration::Location)) {
159 return _.diag(SPV_ERROR_INVALID_DATA, type)
160 << _.VkErrorID(4918) << "Members cannot be assigned a location";
161 }
162
163 // Structs consume locations equal to the sum of the locations consumed
164 // by the members.
165 for (uint32_t i = 1; i < type->operands().size(); ++i) {
166 uint32_t member_locations = 0;
167 if (auto error = NumConsumedLocations(
168 _, _.FindDef(type->GetOperandAs<uint32_t>(i)),
169 &member_locations)) {
170 return error;
171 }
172 *num_locations += member_locations;
173 }
174 break;
175 }
176 case spv::Op::OpTypePointer: {
177 if (_.addressing_model() ==
178 spv::AddressingModel::PhysicalStorageBuffer64 &&
179 type->GetOperandAs<spv::StorageClass>(1) ==
180 spv::StorageClass::PhysicalStorageBuffer) {
181 *num_locations = 1;
182 break;
183 }
184 [[fallthrough]];
185 }
186 default:
187 return _.diag(SPV_ERROR_INVALID_DATA, type)
188 << "Invalid type to assign a location";
189 }
190
191 return SPV_SUCCESS;
192 }
193
194 // Returns the number of components consumed by types that support a component
195 // decoration.
NumConsumedComponents(ValidationState_t & _,const Instruction * type)196 uint32_t NumConsumedComponents(ValidationState_t& _, const Instruction* type) {
197 uint32_t num_components = 0;
198 switch (type->opcode()) {
199 case spv::Op::OpTypeInt:
200 case spv::Op::OpTypeFloat:
201 // 64-bit types consume two components.
202 if (type->GetOperandAs<uint32_t>(1) == 64) {
203 num_components = 2;
204 } else {
205 num_components = 1;
206 }
207 break;
208 case spv::Op::OpTypeVector:
209 // Vectors consume components equal to the underlying type's consumption
210 // times the number of elements in the vector. Note that 3- and 4-element
211 // vectors cannot have a component decoration (i.e. assumed to be zero).
212 num_components =
213 NumConsumedComponents(_, _.FindDef(type->GetOperandAs<uint32_t>(1)));
214 num_components *= type->GetOperandAs<uint32_t>(2);
215 break;
216 case spv::Op::OpTypeArray:
217 // Skip the array.
218 return NumConsumedComponents(_,
219 _.FindDef(type->GetOperandAs<uint32_t>(1)));
220 case spv::Op::OpTypePointer:
221 if (_.addressing_model() ==
222 spv::AddressingModel::PhysicalStorageBuffer64 &&
223 type->GetOperandAs<spv::StorageClass>(1) ==
224 spv::StorageClass::PhysicalStorageBuffer) {
225 return 2;
226 }
227 break;
228 default:
229 // This is an error that is validated elsewhere.
230 break;
231 }
232
233 return num_components;
234 }
235
236 // Populates |locations| (and/or |output_index1_locations|) with the use
237 // location and component coordinates for |variable|. Indices are calculated as
238 // 4 * location + component.
GetLocationsForVariable(ValidationState_t & _,const Instruction * entry_point,const Instruction * variable,std::unordered_set<uint32_t> * locations,std::unordered_set<uint32_t> * output_index1_locations)239 spv_result_t GetLocationsForVariable(
240 ValidationState_t& _, const Instruction* entry_point,
241 const Instruction* variable, std::unordered_set<uint32_t>* locations,
242 std::unordered_set<uint32_t>* output_index1_locations) {
243 const bool is_fragment = entry_point->GetOperandAs<spv::ExecutionModel>(0) ==
244 spv::ExecutionModel::Fragment;
245 const bool is_output =
246 variable->GetOperandAs<spv::StorageClass>(2) == spv::StorageClass::Output;
247 auto ptr_type_id = variable->GetOperandAs<uint32_t>(0);
248 auto ptr_type = _.FindDef(ptr_type_id);
249 auto type_id = ptr_type->GetOperandAs<uint32_t>(2);
250 auto type = _.FindDef(type_id);
251
252 // Check for Location, Component and Index decorations on the variable. The
253 // validator allows duplicate decorations if the location/component/index are
254 // equal. Also track Patch and PerTaskNV decorations.
255 bool has_location = false;
256 uint32_t location = 0;
257 uint32_t component = 0;
258 bool has_index = false;
259 uint32_t index = 0;
260 bool has_patch = false;
261 bool has_per_task_nv = false;
262 bool has_per_vertex_khr = false;
263 // Duplicate Location, Component, Index are checked elsewhere.
264 for (auto& dec : _.id_decorations(variable->id())) {
265 if (dec.dec_type() == spv::Decoration::Location) {
266 has_location = true;
267 location = dec.params()[0];
268 } else if (dec.dec_type() == spv::Decoration::Component) {
269 component = dec.params()[0];
270 } else if (dec.dec_type() == spv::Decoration::Index) {
271 if (!is_output || !is_fragment) {
272 return _.diag(SPV_ERROR_INVALID_DATA, variable)
273 << "Index can only be applied to Fragment output variables";
274 }
275 has_index = true;
276 index = dec.params()[0];
277 } else if (dec.dec_type() == spv::Decoration::BuiltIn) {
278 // Don't check built-ins.
279 return SPV_SUCCESS;
280 } else if (dec.dec_type() == spv::Decoration::Patch) {
281 has_patch = true;
282 } else if (dec.dec_type() == spv::Decoration::PerTaskNV) {
283 has_per_task_nv = true;
284 } else if (dec.dec_type() == spv::Decoration::PerVertexKHR) {
285 if (!is_fragment) {
286 return _.diag(SPV_ERROR_INVALID_DATA, variable)
287 << _.VkErrorID(6777)
288 << "PerVertexKHR can only be applied to Fragment Execution "
289 "Models";
290 }
291 if (type->opcode() != spv::Op::OpTypeArray &&
292 type->opcode() != spv::Op::OpTypeRuntimeArray) {
293 return _.diag(SPV_ERROR_INVALID_DATA, variable)
294 << _.VkErrorID(6778)
295 << "PerVertexKHR must be declared as arrays";
296 }
297 has_per_vertex_khr = true;
298 }
299 }
300
301 // Vulkan 14.1.3: Tessellation control and mesh per-vertex outputs and
302 // tessellation control, evaluation and geometry per-vertex inputs have a
303 // layer of arraying that is not included in interface matching.
304 bool is_arrayed = false;
305 switch (entry_point->GetOperandAs<spv::ExecutionModel>(0)) {
306 case spv::ExecutionModel::TessellationControl:
307 if (!has_patch) {
308 is_arrayed = true;
309 }
310 break;
311 case spv::ExecutionModel::TessellationEvaluation:
312 if (!is_output && !has_patch) {
313 is_arrayed = true;
314 }
315 break;
316 case spv::ExecutionModel::Geometry:
317 if (!is_output) {
318 is_arrayed = true;
319 }
320 break;
321 case spv::ExecutionModel::Fragment:
322 if (!is_output && has_per_vertex_khr) {
323 is_arrayed = true;
324 }
325 break;
326 case spv::ExecutionModel::MeshNV:
327 if (is_output && !has_per_task_nv) {
328 is_arrayed = true;
329 }
330 break;
331 default:
332 break;
333 }
334
335 // Unpack arrayness.
336 if (is_arrayed && (type->opcode() == spv::Op::OpTypeArray ||
337 type->opcode() == spv::Op::OpTypeRuntimeArray)) {
338 type_id = type->GetOperandAs<uint32_t>(1);
339 type = _.FindDef(type_id);
340 }
341
342 if (type->opcode() == spv::Op::OpTypeStruct) {
343 // Don't check built-ins.
344 if (_.HasDecoration(type_id, spv::Decoration::BuiltIn)) return SPV_SUCCESS;
345 }
346
347 // Only block-decorated structs don't need a location on the variable.
348 const bool is_block = _.HasDecoration(type_id, spv::Decoration::Block);
349 if (!has_location && !is_block) {
350 const auto vuid = (type->opcode() == spv::Op::OpTypeStruct) ? 4917 : 4916;
351 return _.diag(SPV_ERROR_INVALID_DATA, variable)
352 << _.VkErrorID(vuid) << "Variable must be decorated with a location";
353 }
354
355 const std::string storage_class = is_output ? "output" : "input";
356 if (has_location) {
357 auto sub_type = type;
358 bool is_int = false;
359 bool is_const = false;
360 uint32_t array_size = 1;
361 // If the variable is still arrayed, mark the locations/components per
362 // index.
363 if (type->opcode() == spv::Op::OpTypeArray) {
364 // Determine the array size if possible and get the element type.
365 std::tie(is_int, is_const, array_size) =
366 _.EvalInt32IfConst(type->GetOperandAs<uint32_t>(2));
367 if (!is_int || !is_const) array_size = 1;
368 auto sub_type_id = type->GetOperandAs<uint32_t>(1);
369 sub_type = _.FindDef(sub_type_id);
370 }
371
372 uint32_t num_locations = 0;
373 if (auto error = NumConsumedLocations(_, sub_type, &num_locations))
374 return error;
375 uint32_t num_components = NumConsumedComponents(_, sub_type);
376
377 for (uint32_t array_idx = 0; array_idx < array_size; ++array_idx) {
378 uint32_t array_location = location + (num_locations * array_idx);
379 uint32_t start = array_location * 4;
380 if (kMaxLocations <= start) {
381 // Too many locations, give up.
382 break;
383 }
384
385 uint32_t end = (array_location + num_locations) * 4;
386 if (num_components != 0) {
387 start += component;
388 end = array_location * 4 + component + num_components;
389 }
390
391 auto locs = locations;
392 if (has_index && index == 1) locs = output_index1_locations;
393
394 for (uint32_t i = start; i < end; ++i) {
395 if (!locs->insert(i).second) {
396 return _.diag(SPV_ERROR_INVALID_DATA, entry_point)
397 << (is_output ? _.VkErrorID(8722) : _.VkErrorID(8721))
398 << "Entry-point has conflicting " << storage_class
399 << " location assignment at location " << i / 4
400 << ", component " << i % 4;
401 }
402 }
403 }
404 } else {
405 // For Block-decorated structs with no location assigned to the variable,
406 // each member of the block must be assigned a location. Also record any
407 // member component assignments. The validator allows duplicate decorations
408 // if they agree on the location/component.
409 std::unordered_map<uint32_t, uint32_t> member_locations;
410 std::unordered_map<uint32_t, uint32_t> member_components;
411 for (auto& dec : _.id_decorations(type_id)) {
412 if (dec.dec_type() == spv::Decoration::Location) {
413 auto where = member_locations.find(dec.struct_member_index());
414 if (where == member_locations.end()) {
415 member_locations[dec.struct_member_index()] = dec.params()[0];
416 } else if (where->second != dec.params()[0]) {
417 return _.diag(SPV_ERROR_INVALID_DATA, type)
418 << "Member index " << dec.struct_member_index()
419 << " has conflicting location assignments";
420 }
421 } else if (dec.dec_type() == spv::Decoration::Component) {
422 auto where = member_components.find(dec.struct_member_index());
423 if (where == member_components.end()) {
424 member_components[dec.struct_member_index()] = dec.params()[0];
425 } else if (where->second != dec.params()[0]) {
426 return _.diag(SPV_ERROR_INVALID_DATA, type)
427 << "Member index " << dec.struct_member_index()
428 << " has conflicting component assignments";
429 }
430 }
431 }
432
433 for (uint32_t i = 1; i < type->operands().size(); ++i) {
434 auto where = member_locations.find(i - 1);
435 if (where == member_locations.end()) {
436 return _.diag(SPV_ERROR_INVALID_DATA, type)
437 << _.VkErrorID(4919) << "Member index " << i - 1
438 << " is missing a location assignment";
439 }
440
441 location = where->second;
442 auto member = _.FindDef(type->GetOperandAs<uint32_t>(i));
443 uint32_t num_locations = 0;
444 if (auto error = NumConsumedLocations(_, member, &num_locations))
445 return error;
446
447 // If the component is not specified, it is assumed to be zero.
448 uint32_t num_components = NumConsumedComponents(_, member);
449 component = 0;
450 if (member_components.count(i - 1)) {
451 component = member_components[i - 1];
452 }
453
454 uint32_t start = location * 4;
455 if (kMaxLocations <= start) {
456 // Too many locations, give up.
457 continue;
458 }
459
460 if (member->opcode() == spv::Op::OpTypeArray && num_components >= 1 &&
461 num_components < 4) {
462 // When an array has an element that takes less than a location in
463 // size, calculate the used locations in a strided manner.
464 for (uint32_t l = location; l < num_locations + location; ++l) {
465 for (uint32_t c = component; c < component + num_components; ++c) {
466 uint32_t check = 4 * l + c;
467 if (!locations->insert(check).second) {
468 return _.diag(SPV_ERROR_INVALID_DATA, entry_point)
469 << (is_output ? _.VkErrorID(8722) : _.VkErrorID(8721))
470 << "Entry-point has conflicting " << storage_class
471 << " location assignment at location " << l
472 << ", component " << c;
473 }
474 }
475 }
476 } else {
477 // TODO: There is a hole here is the member is an array of 3- or
478 // 4-element vectors of 64-bit types.
479 uint32_t end = (location + num_locations) * 4;
480 if (num_components != 0) {
481 start += component;
482 end = location * 4 + component + num_components;
483 }
484 for (uint32_t l = start; l < end; ++l) {
485 if (!locations->insert(l).second) {
486 return _.diag(SPV_ERROR_INVALID_DATA, entry_point)
487 << (is_output ? _.VkErrorID(8722) : _.VkErrorID(8721))
488 << "Entry-point has conflicting " << storage_class
489 << " location assignment at location " << l / 4
490 << ", component " << l % 4;
491 }
492 }
493 }
494 }
495 }
496
497 return SPV_SUCCESS;
498 }
499
ValidateLocations(ValidationState_t & _,const Instruction * entry_point)500 spv_result_t ValidateLocations(ValidationState_t& _,
501 const Instruction* entry_point) {
502 // According to Vulkan 14.1 only the following execution models have
503 // locations assigned.
504 // TODO(dneto): SPV_NV_ray_tracing also uses locations on interface variables,
505 // in other shader stages. Similarly, the *provisional* version of
506 // SPV_KHR_ray_tracing did as well, but not the final version.
507 switch (entry_point->GetOperandAs<spv::ExecutionModel>(0)) {
508 case spv::ExecutionModel::Vertex:
509 case spv::ExecutionModel::TessellationControl:
510 case spv::ExecutionModel::TessellationEvaluation:
511 case spv::ExecutionModel::Geometry:
512 case spv::ExecutionModel::Fragment:
513 break;
514 default:
515 return SPV_SUCCESS;
516 }
517
518 // Locations are stored as a combined location and component values.
519 std::unordered_set<uint32_t> input_locations;
520 std::unordered_set<uint32_t> output_locations_index0;
521 std::unordered_set<uint32_t> output_locations_index1;
522 std::unordered_set<uint32_t> seen;
523 for (uint32_t i = 3; i < entry_point->operands().size(); ++i) {
524 auto interface_id = entry_point->GetOperandAs<uint32_t>(i);
525 auto interface_var = _.FindDef(interface_id);
526 auto storage_class = interface_var->GetOperandAs<spv::StorageClass>(2);
527 if (storage_class != spv::StorageClass::Input &&
528 storage_class != spv::StorageClass::Output) {
529 continue;
530 }
531 if (!seen.insert(interface_id).second) {
532 // Pre-1.4 an interface variable could be listed multiple times in an
533 // entry point. Validation for 1.4 or later is done elsewhere.
534 continue;
535 }
536
537 auto locations = (storage_class == spv::StorageClass::Input)
538 ? &input_locations
539 : &output_locations_index0;
540 if (auto error = GetLocationsForVariable(
541 _, entry_point, interface_var, locations, &output_locations_index1))
542 return error;
543 }
544
545 return SPV_SUCCESS;
546 }
547
ValidateStorageClass(ValidationState_t & _,const Instruction * entry_point)548 spv_result_t ValidateStorageClass(ValidationState_t& _,
549 const Instruction* entry_point) {
550 bool has_push_constant = false;
551 bool has_ray_payload = false;
552 bool has_hit_attribute = false;
553 bool has_callable_data = false;
554 for (uint32_t i = 3; i < entry_point->operands().size(); ++i) {
555 auto interface_id = entry_point->GetOperandAs<uint32_t>(i);
556 auto interface_var = _.FindDef(interface_id);
557 auto storage_class = interface_var->GetOperandAs<spv::StorageClass>(2);
558 switch (storage_class) {
559 case spv::StorageClass::PushConstant: {
560 if (has_push_constant) {
561 return _.diag(SPV_ERROR_INVALID_DATA, entry_point)
562 << _.VkErrorID(6673)
563 << "Entry-point has more than one variable with the "
564 "PushConstant storage class in the interface";
565 }
566 has_push_constant = true;
567 break;
568 }
569 case spv::StorageClass::IncomingRayPayloadKHR: {
570 if (has_ray_payload) {
571 return _.diag(SPV_ERROR_INVALID_DATA, entry_point)
572 << _.VkErrorID(4700)
573 << "Entry-point has more than one variable with the "
574 "IncomingRayPayloadKHR storage class in the interface";
575 }
576 has_ray_payload = true;
577 break;
578 }
579 case spv::StorageClass::HitAttributeKHR: {
580 if (has_hit_attribute) {
581 return _.diag(SPV_ERROR_INVALID_DATA, entry_point)
582 << _.VkErrorID(4702)
583 << "Entry-point has more than one variable with the "
584 "HitAttributeKHR storage class in the interface";
585 }
586 has_hit_attribute = true;
587 break;
588 }
589 case spv::StorageClass::IncomingCallableDataKHR: {
590 if (has_callable_data) {
591 return _.diag(SPV_ERROR_INVALID_DATA, entry_point)
592 << _.VkErrorID(4706)
593 << "Entry-point has more than one variable with the "
594 "IncomingCallableDataKHR storage class in the interface";
595 }
596 has_callable_data = true;
597 break;
598 }
599 default:
600 break;
601 }
602 }
603 return SPV_SUCCESS;
604 }
605
606 } // namespace
607
ValidateInterfaces(ValidationState_t & _)608 spv_result_t ValidateInterfaces(ValidationState_t& _) {
609 bool is_spv_1_4 = _.version() >= SPV_SPIRV_VERSION_WORD(1, 4);
610 for (auto& inst : _.ordered_instructions()) {
611 if (is_interface_variable(&inst, is_spv_1_4)) {
612 if (auto error = check_interface_variable(_, &inst)) {
613 return error;
614 }
615 }
616 }
617
618 if (spvIsVulkanEnv(_.context()->target_env)) {
619 for (auto& inst : _.ordered_instructions()) {
620 if (inst.opcode() == spv::Op::OpEntryPoint) {
621 if (auto error = ValidateLocations(_, &inst)) {
622 return error;
623 }
624 if (auto error = ValidateStorageClass(_, &inst)) {
625 return error;
626 }
627 }
628 if (inst.opcode() == spv::Op::OpTypeVoid) break;
629 }
630 }
631
632 return SPV_SUCCESS;
633 }
634
635 } // namespace val
636 } // namespace spvtools
637