• 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/diagnostic.h"
19 #include "source/spirv_constant.h"
20 #include "source/spirv_target_env.h"
21 #include "source/val/function.h"
22 #include "source/val/instruction.h"
23 #include "source/val/validate.h"
24 #include "source/val/validation_state.h"
25 
26 namespace spvtools {
27 namespace val {
28 namespace {
29 
30 // Returns true if \c inst is an input or output variable.
is_interface_variable(const Instruction * inst,bool is_spv_1_4)31 bool is_interface_variable(const Instruction* inst, bool is_spv_1_4) {
32   if (is_spv_1_4) {
33     // Starting in SPIR-V 1.4, all global variables are interface variables.
34     return inst->opcode() == SpvOpVariable &&
35            inst->word(3u) != SpvStorageClassFunction;
36   } else {
37     return inst->opcode() == SpvOpVariable &&
38            (inst->word(3u) == SpvStorageClassInput ||
39             inst->word(3u) == SpvStorageClassOutput);
40   }
41 }
42 
43 // Checks that \c var is listed as an interface in all the entry points that use
44 // it.
check_interface_variable(ValidationState_t & _,const Instruction * var)45 spv_result_t check_interface_variable(ValidationState_t& _,
46                                       const Instruction* var) {
47   std::vector<const Function*> functions;
48   std::vector<const Instruction*> uses;
49   for (auto use : var->uses()) {
50     uses.push_back(use.first);
51   }
52   for (uint32_t i = 0; i < uses.size(); ++i) {
53     const auto user = uses[i];
54     if (const Function* func = user->function()) {
55       functions.push_back(func);
56     } else {
57       // In the rare case that the variable is used by another instruction in
58       // the global scope, continue searching for an instruction used in a
59       // function.
60       for (auto use : user->uses()) {
61         uses.push_back(use.first);
62       }
63     }
64   }
65 
66   std::sort(functions.begin(), functions.end(),
67             [](const Function* lhs, const Function* rhs) {
68               return lhs->id() < rhs->id();
69             });
70   functions.erase(std::unique(functions.begin(), functions.end()),
71                   functions.end());
72 
73   std::vector<uint32_t> entry_points;
74   for (const auto func : functions) {
75     for (auto id : _.FunctionEntryPoints(func->id())) {
76       entry_points.push_back(id);
77     }
78   }
79 
80   std::sort(entry_points.begin(), entry_points.end());
81   entry_points.erase(std::unique(entry_points.begin(), entry_points.end()),
82                      entry_points.end());
83 
84   for (auto id : entry_points) {
85     for (const auto& desc : _.entry_point_descriptions(id)) {
86       bool found = false;
87       for (auto interface : desc.interfaces) {
88         if (var->id() == interface) {
89           found = true;
90           break;
91         }
92       }
93       if (!found) {
94         return _.diag(SPV_ERROR_INVALID_ID, var)
95                << "Interface variable id <" << var->id()
96                << "> is used by entry point '" << desc.name << "' id <" << id
97                << ">, but is not listed as an interface";
98       }
99     }
100   }
101 
102   return SPV_SUCCESS;
103 }
104 
105 }  // namespace
106 
ValidateInterfaces(ValidationState_t & _)107 spv_result_t ValidateInterfaces(ValidationState_t& _) {
108   bool is_spv_1_4 = _.version() >= SPV_SPIRV_VERSION_WORD(1, 4);
109   for (auto& inst : _.ordered_instructions()) {
110     if (is_interface_variable(&inst, is_spv_1_4)) {
111       if (auto error = check_interface_variable(_, &inst)) {
112         return error;
113       }
114     }
115   }
116 
117   return SPV_SUCCESS;
118 }
119 
120 }  // namespace val
121 }  // namespace spvtools
122