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
26namespace spvtools {
27namespace val {
28namespace {
29
30// Returns true if \c inst is an input or output variable.
31bool 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.
45spv_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
107spv_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