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. |
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. |
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 | |
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 | |