• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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