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