/src/spirv-tools/source/val/validate_id.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) 2015-2016 The Khronos Group Inc. |
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 <cassert> |
18 | | |
19 | | #include <algorithm> |
20 | | #include <iostream> |
21 | | #include <iterator> |
22 | | #include <stack> |
23 | | #include <string> |
24 | | #include <unordered_set> |
25 | | #include <utility> |
26 | | #include <vector> |
27 | | |
28 | | #include "source/diagnostic.h" |
29 | | #include "source/instruction.h" |
30 | | #include "source/opcode.h" |
31 | | #include "source/operand.h" |
32 | | #include "source/spirv_validator_options.h" |
33 | | #include "source/val/function.h" |
34 | | #include "source/val/validation_state.h" |
35 | | #include "spirv-tools/libspirv.h" |
36 | | |
37 | | namespace spvtools { |
38 | | namespace val { |
39 | | |
40 | 2.86M | spv_result_t UpdateIdUse(ValidationState_t& _, const Instruction* inst) { |
41 | 7.03M | for (auto& operand : inst->operands()) { |
42 | 7.03M | const spv_operand_type_t& type = operand.type; |
43 | 7.03M | const uint32_t operand_id = inst->word(operand.offset); |
44 | 7.03M | if (spvIsIdType(type) && type != SPV_OPERAND_TYPE_RESULT_ID) { |
45 | 4.37M | if (auto def = _.FindDef(operand_id)) |
46 | 4.37M | def->RegisterUse(inst, operand.offset); |
47 | 4.37M | } |
48 | 7.03M | } |
49 | | |
50 | 2.86M | return SPV_SUCCESS; |
51 | 2.86M | } |
52 | | |
53 | | /// This function checks all ID definitions dominate their use in the CFG. |
54 | | /// |
55 | | /// This function will iterate over all ID definitions that are defined in the |
56 | | /// functions of a module and make sure that the definitions appear in a |
57 | | /// block that dominates their use. |
58 | | /// |
59 | | /// NOTE: This function does NOT check module scoped functions which are |
60 | | /// checked during the initial binary parse in the IdPass below |
61 | 13.3k | spv_result_t CheckIdDefinitionDominateUse(ValidationState_t& _) { |
62 | 13.3k | std::vector<const Instruction*> phi_instructions; |
63 | 13.3k | std::unordered_set<uint32_t> phi_ids; |
64 | 2.17M | for (const auto& inst : _.ordered_instructions()) { |
65 | 2.17M | if (inst.id() == 0) continue; |
66 | 1.20M | if (const Function* func = inst.function()) { |
67 | 876k | if (const BasicBlock* block = inst.block()) { |
68 | | // If the Id is defined within a block then make sure all references to |
69 | | // that Id appear in a blocks that are dominated by the defining block |
70 | 995k | for (auto& use_index_pair : inst.uses()) { |
71 | 995k | const Instruction* use = use_index_pair.first; |
72 | 995k | if (const BasicBlock* use_block = use->block()) { |
73 | 930k | if (use_block->reachable() == false) continue; |
74 | 898k | if (use->opcode() == spv::Op::OpPhi) { |
75 | 48.1k | if (phi_ids.insert(use->id()).second) { |
76 | 31.9k | phi_instructions.push_back(use); |
77 | 31.9k | } |
78 | 850k | } else if (!block->dominates(*use->block())) { |
79 | 55 | return _.diag(SPV_ERROR_INVALID_ID, use_block->label()) |
80 | 55 | << "ID " << _.getIdName(inst.id()) << " defined in block " |
81 | 55 | << _.getIdName(block->id()) |
82 | 55 | << " does not dominate its use in block " |
83 | 55 | << _.getIdName(use_block->id()); |
84 | 55 | } |
85 | 898k | } |
86 | 995k | } |
87 | 641k | } else { |
88 | | // If the Ids defined within a function but not in a block(i.e. function |
89 | | // parameters, block ids), then make sure all references to that Id |
90 | | // appear within the same function |
91 | 448k | for (auto use : inst.uses()) { |
92 | 448k | const Instruction* user = use.first; |
93 | 448k | if (user->function() && user->function() != func) { |
94 | 12 | return _.diag(SPV_ERROR_INVALID_ID, _.FindDef(func->id())) |
95 | 12 | << "ID " << _.getIdName(inst.id()) << " used in function " |
96 | 12 | << _.getIdName(user->function()->id()) |
97 | 12 | << " is used outside of it's defining function " |
98 | 12 | << _.getIdName(func->id()); |
99 | 12 | } |
100 | 448k | } |
101 | 234k | } |
102 | 876k | } |
103 | | // NOTE: Ids defined outside of functions must appear before they are used |
104 | | // This check is being performed in the IdPass function |
105 | 1.20M | } |
106 | | |
107 | | // Check all OpPhi parent blocks are dominated by the variable's defining |
108 | | // blocks |
109 | 31.6k | for (const Instruction* phi : phi_instructions) { |
110 | 31.6k | if (phi->block()->reachable() == false) continue; |
111 | 97.0k | for (size_t i = 3; i < phi->operands().size(); i += 2) { |
112 | 65.3k | const Instruction* variable = _.FindDef(phi->word(i)); |
113 | 65.3k | const BasicBlock* parent = |
114 | 65.3k | phi->function()->GetBlock(phi->word(i + 1)).first; |
115 | 65.3k | if (variable->block() && parent->reachable() && |
116 | 65.3k | !variable->block()->dominates(*parent)) { |
117 | 16 | return _.diag(SPV_ERROR_INVALID_ID, phi) |
118 | 16 | << "In OpPhi instruction " << _.getIdName(phi->id()) << ", ID " |
119 | 16 | << _.getIdName(variable->id()) |
120 | 16 | << " definition does not dominate its parent " |
121 | 16 | << _.getIdName(parent->id()); |
122 | 16 | } |
123 | 65.3k | } |
124 | 31.6k | } |
125 | | |
126 | 13.2k | return SPV_SUCCESS; |
127 | 13.2k | } |
128 | | |
129 | | // Performs SSA validation on the IDs of an instruction. The |
130 | | // can_have_forward_declared_ids functor should return true if the |
131 | | // instruction operand's ID can be forward referenced. |
132 | 3.12M | spv_result_t IdPass(ValidationState_t& _, Instruction* inst) { |
133 | 3.12M | auto can_have_forward_declared_ids = |
134 | 3.12M | inst->opcode() == spv::Op::OpExtInst && |
135 | 3.12M | spvExtInstIsDebugInfo(inst->ext_inst_type()) |
136 | 3.12M | ? spvDbgInfoExtOperandCanBeForwardDeclaredFunction( |
137 | 49 | inst->ext_inst_type(), inst->word(4)) |
138 | 3.12M | : spvOperandCanBeForwardDeclaredFunction(inst->opcode()); |
139 | | |
140 | | // Keep track of a result id defined by this instruction. 0 means it |
141 | | // does not define an id. |
142 | 3.12M | uint32_t result_id = 0; |
143 | | |
144 | 11.6M | for (unsigned i = 0; i < inst->operands().size(); i++) { |
145 | 8.53M | const spv_parsed_operand_t& operand = inst->operand(i); |
146 | 8.53M | const spv_operand_type_t& type = operand.type; |
147 | | // We only care about Id operands, which are a single word. |
148 | 8.53M | const uint32_t operand_word = inst->word(operand.offset); |
149 | | |
150 | 8.53M | auto ret = SPV_ERROR_INTERNAL; |
151 | 8.53M | switch (type) { |
152 | 1.78M | case SPV_OPERAND_TYPE_RESULT_ID: |
153 | | // NOTE: Multiple Id definitions are being checked by the binary parser. |
154 | | // |
155 | | // Defer undefined-forward-reference removal until after we've analyzed |
156 | | // the remaining operands to this instruction. Deferral only matters |
157 | | // for OpPhi since it's the only case where it defines its own forward |
158 | | // reference. Other instructions that can have forward references |
159 | | // either don't define a value or the forward reference is to a function |
160 | | // Id (and hence defined outside of a function body). |
161 | 1.78M | result_id = operand_word; |
162 | | // NOTE: The result Id is added (in RegisterInstruction) *after* all of |
163 | | // the other Ids have been checked to avoid premature use in the same |
164 | | // instruction. |
165 | 1.78M | ret = SPV_SUCCESS; |
166 | 1.78M | break; |
167 | 4.36M | case SPV_OPERAND_TYPE_ID: |
168 | 4.36M | case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID: |
169 | 4.36M | case SPV_OPERAND_TYPE_SCOPE_ID: |
170 | 4.36M | if (const auto def = _.FindDef(operand_word)) { |
171 | 2.71M | const auto opcode = inst->opcode(); |
172 | 2.71M | if (spvOpcodeGeneratesType(def->opcode()) && |
173 | 2.71M | !spvOpcodeGeneratesType(opcode) && !spvOpcodeIsDebug(opcode) && |
174 | 2.71M | !inst->IsDebugInfo() && !inst->IsNonSemantic() && |
175 | 2.71M | !spvOpcodeIsDecoration(opcode) && opcode != spv::Op::OpFunction && |
176 | 2.71M | opcode != spv::Op::OpCooperativeMatrixLengthNV && |
177 | 2.71M | !(opcode == spv::Op::OpSpecConstantOp && |
178 | 40 | spv::Op(inst->word(3)) == |
179 | 37 | spv::Op::OpCooperativeMatrixLengthNV)) { |
180 | 37 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
181 | 37 | << "Operand " << _.getIdName(operand_word) |
182 | 37 | << " cannot be a type"; |
183 | 2.71M | } else if (def->type_id() == 0 && !spvOpcodeGeneratesType(opcode) && |
184 | 2.71M | !spvOpcodeIsDebug(opcode) && !inst->IsDebugInfo() && |
185 | 2.71M | !inst->IsNonSemantic() && !spvOpcodeIsDecoration(opcode) && |
186 | 2.71M | !spvOpcodeIsBranch(opcode) && opcode != spv::Op::OpPhi && |
187 | 2.71M | opcode != spv::Op::OpExtInst && |
188 | 2.71M | opcode != spv::Op::OpExtInstImport && |
189 | 2.71M | opcode != spv::Op::OpSelectionMerge && |
190 | 2.71M | opcode != spv::Op::OpLoopMerge && |
191 | 2.71M | opcode != spv::Op::OpFunction && |
192 | 2.71M | opcode != spv::Op::OpCooperativeMatrixLengthNV && |
193 | 2.71M | !(opcode == spv::Op::OpSpecConstantOp && |
194 | 31 | spv::Op(inst->word(3)) == |
195 | 28 | spv::Op::OpCooperativeMatrixLengthNV)) { |
196 | 28 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
197 | 28 | << "Operand " << _.getIdName(operand_word) |
198 | 28 | << " requires a type"; |
199 | 2.71M | } else if (def->IsNonSemantic() && !inst->IsNonSemantic()) { |
200 | 12 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
201 | 12 | << "Operand " << _.getIdName(operand_word) |
202 | 12 | << " in semantic instruction cannot be a non-semantic " |
203 | 12 | "instruction"; |
204 | 2.71M | } else { |
205 | 2.71M | ret = SPV_SUCCESS; |
206 | 2.71M | } |
207 | 2.71M | } else if (can_have_forward_declared_ids(i)) { |
208 | 1.65M | if (spvOpcodeGeneratesType(inst->opcode()) && |
209 | 1.65M | !_.IsForwardPointer(operand_word)) { |
210 | 461 | ret = _.diag(SPV_ERROR_INVALID_ID, inst) |
211 | 461 | << "Operand " << _.getIdName(operand_word) |
212 | 461 | << " requires a previous definition"; |
213 | 1.65M | } else { |
214 | 1.65M | ret = _.ForwardDeclareId(operand_word); |
215 | 1.65M | } |
216 | 1.65M | } else { |
217 | 351 | ret = _.diag(SPV_ERROR_INVALID_ID, inst) |
218 | 351 | << "ID " << _.getIdName(operand_word) |
219 | 351 | << " has not been defined"; |
220 | 351 | } |
221 | 4.36M | break; |
222 | 4.36M | case SPV_OPERAND_TYPE_TYPE_ID: |
223 | 1.20M | if (_.IsDefinedId(operand_word)) { |
224 | 1.19M | auto* def = _.FindDef(operand_word); |
225 | 1.19M | if (!spvOpcodeGeneratesType(def->opcode())) { |
226 | 11 | ret = _.diag(SPV_ERROR_INVALID_ID, inst) |
227 | 11 | << "ID " << _.getIdName(operand_word) << " is not a type id"; |
228 | 1.19M | } else { |
229 | 1.19M | ret = SPV_SUCCESS; |
230 | 1.19M | } |
231 | 1.19M | } else { |
232 | 517 | ret = _.diag(SPV_ERROR_INVALID_ID, inst) |
233 | 517 | << "ID " << _.getIdName(operand_word) |
234 | 517 | << " has not been defined"; |
235 | 517 | } |
236 | 1.20M | break; |
237 | 1.17M | default: |
238 | 1.17M | ret = SPV_SUCCESS; |
239 | 1.17M | break; |
240 | 8.53M | } |
241 | 8.53M | if (SPV_SUCCESS != ret) return ret; |
242 | 8.53M | } |
243 | 3.11M | if (result_id) _.RemoveIfForwardDeclared(result_id); |
244 | | |
245 | 3.11M | return SPV_SUCCESS; |
246 | 3.12M | } |
247 | | |
248 | | } // namespace val |
249 | | } // namespace spvtools |