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 | spv_result_t UpdateIdUse(ValidationState_t& _, const Instruction* inst) { |
41 | for (auto& operand : inst->operands()) { |
42 | const spv_operand_type_t& type = operand.type; |
43 | const uint32_t operand_id = inst->word(operand.offset); |
44 | if (spvIsIdType(type) && type != SPV_OPERAND_TYPE_RESULT_ID) { |
45 | if (auto def = _.FindDef(operand_id)) |
46 | def->RegisterUse(inst, operand.offset); |
47 | } |
48 | } |
49 | |
50 | return SPV_SUCCESS; |
51 | } |
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 | spv_result_t CheckIdDefinitionDominateUse(ValidationState_t& _) { |
62 | std::vector<const Instruction*> phi_instructions; |
63 | std::unordered_set<uint32_t> phi_ids; |
64 | for (const auto& inst : _.ordered_instructions()) { |
65 | if (inst.id() == 0) continue; |
66 | if (const Function* func = inst.function()) { |
67 | 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 | for (auto& use_index_pair : inst.uses()) { |
71 | const Instruction* use = use_index_pair.first; |
72 | if (const BasicBlock* use_block = use->block()) { |
73 | if (use_block->reachable() == false) continue; |
74 | if (use->opcode() == SpvOpPhi) { |
75 | if (phi_ids.insert(use->id()).second) { |
76 | phi_instructions.push_back(use); |
77 | } |
78 | } else if (!block->dominates(*use->block())) { |
79 | return _.diag(SPV_ERROR_INVALID_ID, use_block->label()) |
80 | << "ID " << _.getIdName(inst.id()) << " defined in block " |
81 | << _.getIdName(block->id()) |
82 | << " does not dominate its use in block " |
83 | << _.getIdName(use_block->id()); |
84 | } |
85 | } |
86 | } |
87 | } 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 | for (auto use : inst.uses()) { |
92 | const Instruction* user = use.first; |
93 | if (user->function() && user->function() != func) { |
94 | return _.diag(SPV_ERROR_INVALID_ID, _.FindDef(func->id())) |
95 | << "ID " << _.getIdName(inst.id()) << " used in function " |
96 | << _.getIdName(user->function()->id()) |
97 | << " is used outside of it's defining function " |
98 | << _.getIdName(func->id()); |
99 | } |
100 | } |
101 | } |
102 | } |
103 | // NOTE: Ids defined outside of functions must appear before they are used |
104 | // This check is being performed in the IdPass function |
105 | } |
106 | |
107 | // Check all OpPhi parent blocks are dominated by the variable's defining |
108 | // blocks |
109 | for (const Instruction* phi : phi_instructions) { |
110 | if (phi->block()->reachable() == false) continue; |
111 | for (size_t i = 3; i < phi->operands().size(); i += 2) { |
112 | const Instruction* variable = _.FindDef(phi->word(i)); |
113 | const BasicBlock* parent = |
114 | phi->function()->GetBlock(phi->word(i + 1)).first; |
115 | if (variable->block() && parent->reachable() && |
116 | !variable->block()->dominates(*parent)) { |
117 | return _.diag(SPV_ERROR_INVALID_ID, phi) |
118 | << "In OpPhi instruction " << _.getIdName(phi->id()) << ", ID " |
119 | << _.getIdName(variable->id()) |
120 | << " definition does not dominate its parent " |
121 | << _.getIdName(parent->id()); |
122 | } |
123 | } |
124 | } |
125 | |
126 | return SPV_SUCCESS; |
127 | } |
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 | spv_result_t IdPass(ValidationState_t& _, Instruction* inst) { |
133 | auto can_have_forward_declared_ids = |
134 | inst->opcode() == SpvOpExtInst && |
135 | spvExtInstIsDebugInfo(inst->ext_inst_type()) |
136 | ? spvDbgInfoExtOperandCanBeForwardDeclaredFunction( |
137 | inst->ext_inst_type(), inst->word(4)) |
138 | : 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 | uint32_t result_id = 0; |
143 | |
144 | for (unsigned i = 0; i < inst->operands().size(); i++) { |
145 | const spv_parsed_operand_t& operand = inst->operand(i); |
146 | const spv_operand_type_t& type = operand.type; |
147 | // We only care about Id operands, which are a single word. |
148 | const uint32_t operand_word = inst->word(operand.offset); |
149 | |
150 | auto ret = SPV_ERROR_INTERNAL; |
151 | switch (type) { |
152 | 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 | 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 | ret = SPV_SUCCESS; |
166 | break; |
167 | case SPV_OPERAND_TYPE_ID: |
168 | case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID: |
169 | case SPV_OPERAND_TYPE_SCOPE_ID: |
170 | if (const auto def = _.FindDef(operand_word)) { |
171 | const auto opcode = inst->opcode(); |
172 | if (spvOpcodeGeneratesType(def->opcode()) && |
173 | !spvOpcodeGeneratesType(opcode) && !spvOpcodeIsDebug(opcode) && |
174 | !inst->IsDebugInfo() && !inst->IsNonSemantic() && |
175 | !spvOpcodeIsDecoration(opcode) && opcode != SpvOpFunction && |
176 | opcode != SpvOpCooperativeMatrixLengthNV && |
177 | !(opcode == SpvOpSpecConstantOp && |
178 | inst->word(3) == SpvOpCooperativeMatrixLengthNV)) { |
179 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
180 | << "Operand " << _.getIdName(operand_word) |
181 | << " cannot be a type" ; |
182 | } else if (def->type_id() == 0 && !spvOpcodeGeneratesType(opcode) && |
183 | !spvOpcodeIsDebug(opcode) && !inst->IsDebugInfo() && |
184 | !inst->IsNonSemantic() && !spvOpcodeIsDecoration(opcode) && |
185 | !spvOpcodeIsBranch(opcode) && opcode != SpvOpPhi && |
186 | opcode != SpvOpExtInst && opcode != SpvOpExtInstImport && |
187 | opcode != SpvOpSelectionMerge && |
188 | opcode != SpvOpLoopMerge && opcode != SpvOpFunction && |
189 | opcode != SpvOpCooperativeMatrixLengthNV && |
190 | !(opcode == SpvOpSpecConstantOp && |
191 | inst->word(3) == SpvOpCooperativeMatrixLengthNV)) { |
192 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
193 | << "Operand " << _.getIdName(operand_word) |
194 | << " requires a type" ; |
195 | } else if (def->IsNonSemantic() && !inst->IsNonSemantic()) { |
196 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
197 | << "Operand " << _.getIdName(operand_word) |
198 | << " in semantic instruction cannot be a non-semantic " |
199 | "instruction" ; |
200 | } else { |
201 | ret = SPV_SUCCESS; |
202 | } |
203 | } else if (can_have_forward_declared_ids(i)) { |
204 | if (inst->opcode() == SpvOpTypeStruct && |
205 | !_.IsForwardPointer(operand_word)) { |
206 | ret = _.diag(SPV_ERROR_INVALID_ID, inst) |
207 | << "Operand " << _.getIdName(operand_word) |
208 | << " requires a previous definition" ; |
209 | } else { |
210 | ret = _.ForwardDeclareId(operand_word); |
211 | } |
212 | } else { |
213 | ret = _.diag(SPV_ERROR_INVALID_ID, inst) |
214 | << "ID " << _.getIdName(operand_word) |
215 | << " has not been defined" ; |
216 | } |
217 | break; |
218 | case SPV_OPERAND_TYPE_TYPE_ID: |
219 | if (_.IsDefinedId(operand_word)) { |
220 | auto* def = _.FindDef(operand_word); |
221 | if (!spvOpcodeGeneratesType(def->opcode())) { |
222 | ret = _.diag(SPV_ERROR_INVALID_ID, inst) |
223 | << "ID " << _.getIdName(operand_word) << " is not a type id" ; |
224 | } else { |
225 | ret = SPV_SUCCESS; |
226 | } |
227 | } else { |
228 | ret = _.diag(SPV_ERROR_INVALID_ID, inst) |
229 | << "ID " << _.getIdName(operand_word) |
230 | << " has not been defined" ; |
231 | } |
232 | break; |
233 | default: |
234 | ret = SPV_SUCCESS; |
235 | break; |
236 | } |
237 | if (SPV_SUCCESS != ret) return ret; |
238 | } |
239 | if (result_id) _.RemoveIfForwardDeclared(result_id); |
240 | |
241 | return SPV_SUCCESS; |
242 | } |
243 | |
244 | } // namespace val |
245 | } // namespace spvtools |
246 | |