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 | // Source code for logical layout validation as described in section 2.4 |
16 | |
17 | #include <cassert> |
18 | |
19 | #include "DebugInfo.h" |
20 | #include "OpenCLDebugInfo100.h" |
21 | #include "source/diagnostic.h" |
22 | #include "source/opcode.h" |
23 | #include "source/operand.h" |
24 | #include "source/val/function.h" |
25 | #include "source/val/instruction.h" |
26 | #include "source/val/validate.h" |
27 | #include "source/val/validation_state.h" |
28 | |
29 | namespace spvtools { |
30 | namespace val { |
31 | namespace { |
32 | |
33 | // Module scoped instructions are processed by determining if the opcode |
34 | // is part of the current layout section. If it is not then the next sections is |
35 | // checked. |
36 | spv_result_t ModuleScopedInstructions(ValidationState_t& _, |
37 | const Instruction* inst, SpvOp opcode) { |
38 | switch (opcode) { |
39 | case SpvOpExtInst: |
40 | if (spvExtInstIsNonSemantic(inst->ext_inst_type())) { |
41 | // non-semantic extinst opcodes are allowed beginning in the types |
42 | // section, but since they must name a return type they cannot be the |
43 | // first instruction in the types section. Therefore check that we are |
44 | // already in it. |
45 | if (_.current_layout_section() < kLayoutTypes) { |
46 | return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) |
47 | << "Non-semantic OpExtInst must not appear before types " |
48 | << "section" ; |
49 | } |
50 | } else if (spvExtInstIsDebugInfo(inst->ext_inst_type())) { |
51 | const uint32_t ext_inst_index = inst->word(4); |
52 | bool local_debug_info = false; |
53 | if (inst->ext_inst_type() == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) { |
54 | const OpenCLDebugInfo100Instructions ext_inst_key = |
55 | OpenCLDebugInfo100Instructions(ext_inst_index); |
56 | if (ext_inst_key == OpenCLDebugInfo100DebugScope || |
57 | ext_inst_key == OpenCLDebugInfo100DebugNoScope || |
58 | ext_inst_key == OpenCLDebugInfo100DebugDeclare || |
59 | ext_inst_key == OpenCLDebugInfo100DebugValue) { |
60 | local_debug_info = true; |
61 | } |
62 | } else { |
63 | const DebugInfoInstructions ext_inst_key = |
64 | DebugInfoInstructions(ext_inst_index); |
65 | if (ext_inst_key == DebugInfoDebugScope || |
66 | ext_inst_key == DebugInfoDebugNoScope || |
67 | ext_inst_key == DebugInfoDebugDeclare || |
68 | ext_inst_key == DebugInfoDebugValue) { |
69 | local_debug_info = true; |
70 | } |
71 | } |
72 | |
73 | if (local_debug_info) { |
74 | if (_.in_function_body() == false) { |
75 | // DebugScope, DebugNoScope, DebugDeclare, DebugValue must |
76 | // appear in a function body. |
77 | return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) |
78 | << "DebugScope, DebugNoScope, DebugDeclare, DebugValue " |
79 | << "of debug info extension must appear in a function " |
80 | << "body" ; |
81 | } |
82 | } else { |
83 | // Debug info extinst opcodes other than DebugScope, DebugNoScope, |
84 | // DebugDeclare, DebugValue must be placed between section 9 (types, |
85 | // constants, global variables) and section 10 (function |
86 | // declarations). |
87 | if (_.current_layout_section() < kLayoutTypes || |
88 | _.current_layout_section() >= kLayoutFunctionDeclarations) { |
89 | return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) |
90 | << "Debug info extension instructions other than " |
91 | << "DebugScope, DebugNoScope, DebugDeclare, DebugValue " |
92 | << "must appear between section 9 (types, constants, " |
93 | << "global variables) and section 10 (function " |
94 | << "declarations)" ; |
95 | } |
96 | } |
97 | } else { |
98 | // otherwise they must be used in a block |
99 | if (_.current_layout_section() < kLayoutFunctionDefinitions) { |
100 | return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) |
101 | << spvOpcodeString(opcode) << " must appear in a block" ; |
102 | } |
103 | } |
104 | break; |
105 | default: |
106 | break; |
107 | } |
108 | |
109 | while (_.IsOpcodeInCurrentLayoutSection(opcode) == false) { |
110 | _.ProgressToNextLayoutSectionOrder(); |
111 | |
112 | switch (_.current_layout_section()) { |
113 | case kLayoutMemoryModel: |
114 | if (opcode != SpvOpMemoryModel) { |
115 | return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) |
116 | << spvOpcodeString(opcode) |
117 | << " cannot appear before the memory model instruction" ; |
118 | } |
119 | break; |
120 | case kLayoutFunctionDeclarations: |
121 | // All module sections have been processed. Recursively call |
122 | // ModuleLayoutPass to process the next section of the module |
123 | return ModuleLayoutPass(_, inst); |
124 | default: |
125 | break; |
126 | } |
127 | } |
128 | return SPV_SUCCESS; |
129 | } |
130 | |
131 | // Function declaration validation is performed by making sure that the |
132 | // FunctionParameter and FunctionEnd instructions only appear inside of |
133 | // functions. It also ensures that the Function instruction does not appear |
134 | // inside of another function. This stage ends when the first label is |
135 | // encountered inside of a function. |
136 | spv_result_t FunctionScopedInstructions(ValidationState_t& _, |
137 | const Instruction* inst, SpvOp opcode) { |
138 | if (_.IsOpcodeInCurrentLayoutSection(opcode)) { |
139 | switch (opcode) { |
140 | case SpvOpFunction: { |
141 | if (_.in_function_body()) { |
142 | return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) |
143 | << "Cannot declare a function in a function body" ; |
144 | } |
145 | auto control_mask = inst->GetOperandAs<SpvFunctionControlMask>(2); |
146 | if (auto error = |
147 | _.RegisterFunction(inst->id(), inst->type_id(), control_mask, |
148 | inst->GetOperandAs<uint32_t>(3))) |
149 | return error; |
150 | if (_.current_layout_section() == kLayoutFunctionDefinitions) { |
151 | if (auto error = _.current_function().RegisterSetFunctionDeclType( |
152 | FunctionDecl::kFunctionDeclDefinition)) |
153 | return error; |
154 | } |
155 | } break; |
156 | |
157 | case SpvOpFunctionParameter: |
158 | if (_.in_function_body() == false) { |
159 | return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) |
160 | << "Function parameter instructions must be in a " |
161 | "function body" ; |
162 | } |
163 | if (_.current_function().block_count() != 0) { |
164 | return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) |
165 | << "Function parameters must only appear immediately after " |
166 | "the function definition" ; |
167 | } |
168 | if (auto error = _.current_function().RegisterFunctionParameter( |
169 | inst->id(), inst->type_id())) |
170 | return error; |
171 | break; |
172 | |
173 | case SpvOpFunctionEnd: |
174 | if (_.in_function_body() == false) { |
175 | return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) |
176 | << "Function end instructions must be in a function body" ; |
177 | } |
178 | if (_.in_block()) { |
179 | return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) |
180 | << "Function end cannot be called in blocks" ; |
181 | } |
182 | if (_.current_function().block_count() == 0 && |
183 | _.current_layout_section() == kLayoutFunctionDefinitions) { |
184 | return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) |
185 | << "Function declarations must appear before " |
186 | "function definitions." ; |
187 | } |
188 | if (_.current_layout_section() == kLayoutFunctionDeclarations) { |
189 | if (auto error = _.current_function().RegisterSetFunctionDeclType( |
190 | FunctionDecl::kFunctionDeclDeclaration)) |
191 | return error; |
192 | } |
193 | if (auto error = _.RegisterFunctionEnd()) return error; |
194 | break; |
195 | |
196 | case SpvOpLine: |
197 | case SpvOpNoLine: |
198 | break; |
199 | case SpvOpLabel: |
200 | // If the label is encountered then the current function is a |
201 | // definition so set the function to a declaration and update the |
202 | // module section |
203 | if (_.in_function_body() == false) { |
204 | return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) |
205 | << "Label instructions must be in a function body" ; |
206 | } |
207 | if (_.in_block()) { |
208 | return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) |
209 | << "A block must end with a branch instruction." ; |
210 | } |
211 | if (_.current_layout_section() == kLayoutFunctionDeclarations) { |
212 | _.ProgressToNextLayoutSectionOrder(); |
213 | if (auto error = _.current_function().RegisterSetFunctionDeclType( |
214 | FunctionDecl::kFunctionDeclDefinition)) |
215 | return error; |
216 | } |
217 | break; |
218 | |
219 | case SpvOpExtInst: |
220 | if (spvExtInstIsNonSemantic(inst->ext_inst_type())) { |
221 | // non-semantic extinst opcodes are allowed beginning in the types |
222 | // section, but must either be placed outside a function declaration, |
223 | // or inside a block. |
224 | if (_.current_layout_section() < kLayoutTypes) { |
225 | return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) |
226 | << "Non-semantic OpExtInst must not appear before types " |
227 | << "section" ; |
228 | } else if (_.in_function_body() && _.in_block() == false) { |
229 | return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) |
230 | << "Non-semantic OpExtInst within function definition must " |
231 | "appear in a block" ; |
232 | } |
233 | } else if (spvExtInstIsDebugInfo(inst->ext_inst_type())) { |
234 | const uint32_t ext_inst_index = inst->word(4); |
235 | bool local_debug_info = false; |
236 | if (inst->ext_inst_type() == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) { |
237 | const OpenCLDebugInfo100Instructions ext_inst_key = |
238 | OpenCLDebugInfo100Instructions(ext_inst_index); |
239 | if (ext_inst_key == OpenCLDebugInfo100DebugScope || |
240 | ext_inst_key == OpenCLDebugInfo100DebugNoScope || |
241 | ext_inst_key == OpenCLDebugInfo100DebugDeclare || |
242 | ext_inst_key == OpenCLDebugInfo100DebugValue) { |
243 | local_debug_info = true; |
244 | } |
245 | } else { |
246 | const DebugInfoInstructions ext_inst_key = |
247 | DebugInfoInstructions(ext_inst_index); |
248 | if (ext_inst_key == DebugInfoDebugScope || |
249 | ext_inst_key == DebugInfoDebugNoScope || |
250 | ext_inst_key == DebugInfoDebugDeclare || |
251 | ext_inst_key == DebugInfoDebugValue) { |
252 | local_debug_info = true; |
253 | } |
254 | } |
255 | |
256 | if (local_debug_info) { |
257 | if (_.in_function_body() == false) { |
258 | // DebugScope, DebugNoScope, DebugDeclare, DebugValue must |
259 | // appear in a function body. |
260 | return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) |
261 | << "DebugScope, DebugNoScope, DebugDeclare, DebugValue " |
262 | << "of debug info extension must appear in a function " |
263 | << "body" ; |
264 | } |
265 | } else { |
266 | // Debug info extinst opcodes other than DebugScope, DebugNoScope, |
267 | // DebugDeclare, DebugValue must be placed between section 9 (types, |
268 | // constants, global variables) and section 10 (function |
269 | // declarations). |
270 | if (_.current_layout_section() < kLayoutTypes || |
271 | _.current_layout_section() >= kLayoutFunctionDeclarations) { |
272 | return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) |
273 | << "Debug info extension instructions other than " |
274 | << "DebugScope, DebugNoScope, DebugDeclare, DebugValue " |
275 | << "must appear between section 9 (types, constants, " |
276 | << "global variables) and section 10 (function " |
277 | << "declarations)" ; |
278 | } |
279 | } |
280 | } else { |
281 | // otherwise they must be used in a block |
282 | if (_.in_block() == false) { |
283 | return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) |
284 | << spvOpcodeString(opcode) << " must appear in a block" ; |
285 | } |
286 | } |
287 | break; |
288 | |
289 | default: |
290 | if (_.current_layout_section() == kLayoutFunctionDeclarations && |
291 | _.in_function_body()) { |
292 | return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) |
293 | << "A function must begin with a label" ; |
294 | } else { |
295 | if (_.in_block() == false) { |
296 | return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) |
297 | << spvOpcodeString(opcode) << " must appear in a block" ; |
298 | } |
299 | } |
300 | break; |
301 | } |
302 | } else { |
303 | return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) |
304 | << spvOpcodeString(opcode) |
305 | << " cannot appear in a function declaration" ; |
306 | } |
307 | return SPV_SUCCESS; |
308 | } |
309 | |
310 | } // namespace |
311 | |
312 | // TODO(umar): Check linkage capabilities for function declarations |
313 | // TODO(umar): Better error messages |
314 | // NOTE: This function does not handle CFG related validation |
315 | // Performs logical layout validation. See Section 2.4 |
316 | spv_result_t ModuleLayoutPass(ValidationState_t& _, const Instruction* inst) { |
317 | const SpvOp opcode = inst->opcode(); |
318 | |
319 | switch (_.current_layout_section()) { |
320 | case kLayoutCapabilities: |
321 | case kLayoutExtensions: |
322 | case kLayoutExtInstImport: |
323 | case kLayoutMemoryModel: |
324 | case kLayoutEntryPoint: |
325 | case kLayoutExecutionMode: |
326 | case kLayoutDebug1: |
327 | case kLayoutDebug2: |
328 | case kLayoutDebug3: |
329 | case kLayoutAnnotations: |
330 | case kLayoutTypes: |
331 | if (auto error = ModuleScopedInstructions(_, inst, opcode)) return error; |
332 | break; |
333 | case kLayoutFunctionDeclarations: |
334 | case kLayoutFunctionDefinitions: |
335 | if (auto error = FunctionScopedInstructions(_, inst, opcode)) { |
336 | return error; |
337 | } |
338 | break; |
339 | } |
340 | return SPV_SUCCESS; |
341 | } |
342 | |
343 | } // namespace val |
344 | } // namespace spvtools |
345 | |