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