File: | root/firefox-clang/third_party/rust/glslopt/glsl-optimizer/src/compiler/glsl/opt_function_inlining.cpp |
Warning: | line 273, column 11 Branch condition evaluates to a garbage value |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* | |||
2 | * Copyright © 2010 Intel Corporation | |||
3 | * | |||
4 | * Permission is hereby granted, free of charge, to any person obtaining a | |||
5 | * copy of this software and associated documentation files (the "Software"), | |||
6 | * to deal in the Software without restriction, including without limitation | |||
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |||
8 | * and/or sell copies of the Software, and to permit persons to whom the | |||
9 | * Software is furnished to do so, subject to the following conditions: | |||
10 | * | |||
11 | * The above copyright notice and this permission notice (including the next | |||
12 | * paragraph) shall be included in all copies or substantial portions of the | |||
13 | * Software. | |||
14 | * | |||
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |||
18 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |||
20 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |||
21 | * DEALINGS IN THE SOFTWARE. | |||
22 | */ | |||
23 | ||||
24 | /** | |||
25 | * \file opt_function_inlining.cpp | |||
26 | * | |||
27 | * Replaces calls to functions with the body of the function. | |||
28 | */ | |||
29 | ||||
30 | #include "ir.h" | |||
31 | #include "ir_visitor.h" | |||
32 | #include "ir_function_inlining.h" | |||
33 | #include "ir_expression_flattening.h" | |||
34 | #include "compiler/glsl_types.h" | |||
35 | #include "util/hash_table.h" | |||
36 | ||||
37 | static void | |||
38 | do_variable_replacement(exec_list *instructions, | |||
39 | ir_variable *orig, | |||
40 | ir_dereference *repl); | |||
41 | ||||
42 | namespace { | |||
43 | ||||
44 | class ir_function_inlining_visitor : public ir_hierarchical_visitor { | |||
45 | public: | |||
46 | ir_function_inlining_visitor() | |||
47 | { | |||
48 | progress = false; | |||
49 | } | |||
50 | ||||
51 | virtual ~ir_function_inlining_visitor() | |||
52 | { | |||
53 | /* empty */ | |||
54 | } | |||
55 | ||||
56 | virtual ir_visitor_status visit_enter(ir_expression *); | |||
57 | virtual ir_visitor_status visit_enter(ir_call *); | |||
58 | virtual ir_visitor_status visit_enter(ir_return *); | |||
59 | virtual ir_visitor_status visit_enter(ir_texture *); | |||
60 | virtual ir_visitor_status visit_enter(ir_swizzle *); | |||
61 | ||||
62 | bool progress; | |||
63 | }; | |||
64 | ||||
65 | class ir_save_lvalue_visitor : public ir_hierarchical_visitor { | |||
66 | public: | |||
67 | virtual ir_visitor_status visit_enter(ir_dereference_array *); | |||
68 | }; | |||
69 | ||||
70 | } /* unnamed namespace */ | |||
71 | ||||
72 | bool | |||
73 | do_function_inlining(exec_list *instructions) | |||
74 | { | |||
75 | ir_function_inlining_visitor v; | |||
76 | ||||
77 | v.run(instructions); | |||
78 | ||||
79 | return v.progress; | |||
80 | } | |||
81 | ||||
82 | static void | |||
83 | replace_return_with_assignment(ir_instruction *ir, void *data) | |||
84 | { | |||
85 | void *ctx = ralloc_parent(ir); | |||
86 | ir_dereference *orig_deref = (ir_dereference *) data; | |||
87 | ir_return *ret = ir->as_return(); | |||
88 | ||||
89 | if (ret) { | |||
90 | if (ret->value) { | |||
91 | ir_rvalue *lhs = orig_deref->clone(ctx, NULL__null); | |||
92 | ret->replace_with(new(ctx) ir_assignment(lhs, ret->value)); | |||
93 | } else { | |||
94 | /* un-valued return has to be the last return, or we shouldn't | |||
95 | * have reached here. (see can_inline()). | |||
96 | */ | |||
97 | assert(ret->next->is_tail_sentinel())(static_cast <bool> (ret->next->is_tail_sentinel( )) ? void (0) : __assert_fail ("ret->next->is_tail_sentinel()" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); | |||
98 | ret->remove(); | |||
99 | } | |||
100 | } | |||
101 | } | |||
102 | ||||
103 | /* Save the given lvalue before the given instruction. | |||
104 | * | |||
105 | * This is done by adding temporary variables into which the current value | |||
106 | * of any array indices are saved, and then modifying the dereference chain | |||
107 | * in-place to point to those temporary variables. | |||
108 | * | |||
109 | * The hierarchical visitor is only used to traverse the left-hand-side chain | |||
110 | * of derefs. | |||
111 | */ | |||
112 | ir_visitor_status | |||
113 | ir_save_lvalue_visitor::visit_enter(ir_dereference_array *deref) | |||
114 | { | |||
115 | if (deref->array_index->ir_type != ir_type_constant) { | |||
116 | void *ctx = ralloc_parent(deref); | |||
117 | ir_variable *index; | |||
118 | ir_assignment *assignment; | |||
119 | ||||
120 | index = new(ctx) ir_variable(deref->array_index->type, "saved_idx", ir_var_temporary); | |||
121 | base_ir->insert_before(index); | |||
122 | ||||
123 | assignment = new(ctx) ir_assignment(new(ctx) ir_dereference_variable(index), | |||
124 | deref->array_index); | |||
125 | base_ir->insert_before(assignment); | |||
126 | ||||
127 | deref->array_index = new(ctx) ir_dereference_variable(index); | |||
128 | } | |||
129 | ||||
130 | deref->array->accept(this); | |||
131 | return visit_stop; | |||
132 | } | |||
133 | ||||
134 | static bool | |||
135 | should_replace_variable(ir_variable *sig_param, ir_rvalue *param) { | |||
136 | /* For opaque types, we want the inlined variable references | |||
137 | * referencing the passed in variable, since that will have | |||
138 | * the location information, which an assignment of an opaque | |||
139 | * variable wouldn't. | |||
140 | */ | |||
141 | return sig_param->type->contains_opaque() && | |||
142 | param->is_dereference() && | |||
143 | sig_param->data.mode == ir_var_function_in; | |||
144 | } | |||
145 | ||||
146 | void | |||
147 | ir_call::generate_inline(ir_instruction *next_ir) | |||
148 | { | |||
149 | void *ctx = ralloc_parent(this); | |||
150 | ir_variable **parameters; | |||
151 | unsigned num_parameters; | |||
152 | int i; | |||
153 | struct hash_table *ht; | |||
154 | ||||
155 | ht = _mesa_pointer_hash_table_create(NULL__null); | |||
156 | ||||
157 | num_parameters = this->callee->parameters.length(); | |||
158 | parameters = new ir_variable *[num_parameters]; | |||
159 | ||||
160 | /* Generate the declarations for the parameters to our inlined code, | |||
161 | * and set up the mapping of real function body variables to ours. | |||
162 | */ | |||
163 | i = 0; | |||
164 | foreach_two_lists(formal_node, &this->callee->parameters,for (struct exec_node * formal_node = (&this->callee-> parameters)->head_sentinel.next, * actual_node = (&this ->actual_parameters)->head_sentinel.next, * __next1 = formal_node ->next, * __next2 = actual_node->next ; __next1 != __null && __next2 != __null ; formal_node = __next1, actual_node = __next2, __next1 = __next1->next, __next2 = __next2-> next) | |||
165 | actual_node, &this->actual_parameters)for (struct exec_node * formal_node = (&this->callee-> parameters)->head_sentinel.next, * actual_node = (&this ->actual_parameters)->head_sentinel.next, * __next1 = formal_node ->next, * __next2 = actual_node->next ; __next1 != __null && __next2 != __null ; formal_node = __next1, actual_node = __next2, __next1 = __next1->next, __next2 = __next2-> next) { | |||
166 | ir_variable *sig_param = (ir_variable *) formal_node; | |||
167 | ir_rvalue *param = (ir_rvalue *) actual_node; | |||
168 | ||||
169 | /* Generate a new variable for the parameter. */ | |||
170 | if (should_replace_variable(sig_param, param)) { | |||
171 | /* Actual replacement happens below */ | |||
172 | parameters[i] = NULL__null; | |||
173 | } else { | |||
174 | parameters[i] = sig_param->clone(ctx, ht); | |||
175 | parameters[i]->data.mode = ir_var_temporary; | |||
176 | ||||
177 | /* Remove the read-only decoration because we're going to write | |||
178 | * directly to this variable. If the cloned variable is left | |||
179 | * read-only and the inlined function is inside a loop, the loop | |||
180 | * analysis code will get confused. | |||
181 | */ | |||
182 | parameters[i]->data.read_only = false; | |||
183 | next_ir->insert_before(parameters[i]); | |||
184 | } | |||
185 | ||||
186 | /* Section 6.1.1 (Function Calling Conventions) of the OpenGL Shading | |||
187 | * Language 4.5 spec says: | |||
188 | * | |||
189 | * "All arguments are evaluated at call time, exactly once, in order, | |||
190 | * from left to right. [...] Evaluation of an out parameter results | |||
191 | * in an l-value that is used to copy out a value when the function | |||
192 | * returns." | |||
193 | * | |||
194 | * I.e., we have to take temporary copies of any relevant array indices | |||
195 | * before the function body is executed. | |||
196 | * | |||
197 | * This ensures that | |||
198 | * (a) if an array index expressions refers to a variable that is | |||
199 | * modified by the execution of the function body, we use the | |||
200 | * original value as intended, and | |||
201 | * (b) if an array index expression has side effects, those side effects | |||
202 | * are only executed once and at the right time. | |||
203 | */ | |||
204 | if (parameters[i]) { | |||
205 | if (sig_param->data.mode == ir_var_function_in || | |||
206 | sig_param->data.mode == ir_var_const_in) { | |||
207 | ir_assignment *assign; | |||
208 | ||||
209 | assign = new(ctx) ir_assignment(new(ctx) ir_dereference_variable(parameters[i]), | |||
210 | param); | |||
211 | next_ir->insert_before(assign); | |||
212 | } else { | |||
213 | assert(sig_param->data.mode == ir_var_function_out ||(static_cast <bool> (sig_param->data.mode == ir_var_function_out || sig_param->data.mode == ir_var_function_inout) ? void ( 0) : __assert_fail ("sig_param->data.mode == ir_var_function_out || sig_param->data.mode == ir_var_function_inout" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )) | |||
214 | sig_param->data.mode == ir_var_function_inout)(static_cast <bool> (sig_param->data.mode == ir_var_function_out || sig_param->data.mode == ir_var_function_inout) ? void ( 0) : __assert_fail ("sig_param->data.mode == ir_var_function_out || sig_param->data.mode == ir_var_function_inout" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); | |||
215 | assert(param->is_lvalue())(static_cast <bool> (param->is_lvalue()) ? void (0) : __assert_fail ("param->is_lvalue()", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); | |||
216 | ||||
217 | ir_save_lvalue_visitor v; | |||
218 | v.base_ir = next_ir; | |||
219 | ||||
220 | param->accept(&v); | |||
221 | ||||
222 | if (sig_param->data.mode == ir_var_function_inout) { | |||
223 | ir_assignment *assign; | |||
224 | ||||
225 | assign = new(ctx) ir_assignment(new(ctx) ir_dereference_variable(parameters[i]), | |||
226 | param->clone(ctx, NULL__null)->as_rvalue()); | |||
227 | next_ir->insert_before(assign); | |||
228 | } | |||
229 | } | |||
230 | } | |||
231 | ||||
232 | ++i; | |||
233 | } | |||
234 | ||||
235 | exec_list new_instructions; | |||
236 | ||||
237 | /* Generate the inlined body of the function to a new list */ | |||
238 | foreach_in_list(ir_instruction, ir, &callee->body)for (ir_instruction *ir = (!exec_node_is_tail_sentinel((& callee->body)->head_sentinel.next) ? (ir_instruction *) ((&callee->body)->head_sentinel.next) : __null); ( ir) != __null; (ir) = (!exec_node_is_tail_sentinel((ir)->next ) ? (ir_instruction *) ((ir)->next) : __null)) { | |||
239 | ir_instruction *new_ir = ir->clone(ctx, ht); | |||
240 | ||||
241 | new_instructions.push_tail(new_ir); | |||
242 | visit_tree(new_ir, replace_return_with_assignment, this->return_deref); | |||
243 | } | |||
244 | ||||
245 | /* If any opaque types were passed in, replace any deref of the | |||
246 | * opaque variable with a deref of the argument. | |||
247 | */ | |||
248 | foreach_two_lists(formal_node, &this->callee->parameters,for (struct exec_node * formal_node = (&this->callee-> parameters)->head_sentinel.next, * actual_node = (&this ->actual_parameters)->head_sentinel.next, * __next1 = formal_node ->next, * __next2 = actual_node->next ; __next1 != __null && __next2 != __null ; formal_node = __next1, actual_node = __next2, __next1 = __next1->next, __next2 = __next2-> next) | |||
249 | actual_node, &this->actual_parameters)for (struct exec_node * formal_node = (&this->callee-> parameters)->head_sentinel.next, * actual_node = (&this ->actual_parameters)->head_sentinel.next, * __next1 = formal_node ->next, * __next2 = actual_node->next ; __next1 != __null && __next2 != __null ; formal_node = __next1, actual_node = __next2, __next1 = __next1->next, __next2 = __next2-> next) { | |||
250 | ir_rvalue *const param = (ir_rvalue *) actual_node; | |||
251 | ir_variable *sig_param = (ir_variable *) formal_node; | |||
252 | ||||
253 | if (should_replace_variable(sig_param, param)) { | |||
254 | ir_dereference *deref = param->as_dereference(); | |||
255 | ||||
256 | do_variable_replacement(&new_instructions, sig_param, deref); | |||
257 | } | |||
258 | } | |||
259 | ||||
260 | /* Now push those new instructions in. */ | |||
261 | next_ir->insert_before(&new_instructions); | |||
262 | ||||
263 | /* Copy back the value of any 'out' parameters from the function body | |||
264 | * variables to our own. | |||
265 | */ | |||
266 | i = 0; | |||
267 | foreach_two_lists(formal_node, &this->callee->parameters,for (struct exec_node * formal_node = (&this->callee-> parameters)->head_sentinel.next, * actual_node = (&this ->actual_parameters)->head_sentinel.next, * __next1 = formal_node ->next, * __next2 = actual_node->next ; __next1 != __null && __next2 != __null ; formal_node = __next1, actual_node = __next2, __next1 = __next1->next, __next2 = __next2-> next) | |||
268 | actual_node, &this->actual_parameters)for (struct exec_node * formal_node = (&this->callee-> parameters)->head_sentinel.next, * actual_node = (&this ->actual_parameters)->head_sentinel.next, * __next1 = formal_node ->next, * __next2 = actual_node->next ; __next1 != __null && __next2 != __null ; formal_node = __next1, actual_node = __next2, __next1 = __next1->next, __next2 = __next2-> next) { | |||
269 | ir_rvalue *const param = (ir_rvalue *) actual_node; | |||
270 | const ir_variable *const sig_param = (ir_variable *) formal_node; | |||
271 | ||||
272 | /* Move our param variable into the actual param if it's an 'out' type. */ | |||
273 | if (parameters[i] && (sig_param->data.mode == ir_var_function_out || | |||
| ||||
274 | sig_param->data.mode == ir_var_function_inout)) { | |||
275 | ir_assignment *assign; | |||
276 | ||||
277 | assign = new(ctx) ir_assignment(param, | |||
278 | new(ctx) ir_dereference_variable(parameters[i])); | |||
279 | next_ir->insert_before(assign); | |||
280 | } | |||
281 | ||||
282 | ++i; | |||
283 | } | |||
284 | ||||
285 | delete [] parameters; | |||
286 | ||||
287 | _mesa_hash_table_destroy(ht, NULL__null); | |||
288 | } | |||
289 | ||||
290 | ||||
291 | ir_visitor_status | |||
292 | ir_function_inlining_visitor::visit_enter(ir_expression *ir) | |||
293 | { | |||
294 | (void) ir; | |||
295 | return visit_continue_with_parent; | |||
296 | } | |||
297 | ||||
298 | ||||
299 | ir_visitor_status | |||
300 | ir_function_inlining_visitor::visit_enter(ir_return *ir) | |||
301 | { | |||
302 | (void) ir; | |||
303 | return visit_continue_with_parent; | |||
304 | } | |||
305 | ||||
306 | ||||
307 | ir_visitor_status | |||
308 | ir_function_inlining_visitor::visit_enter(ir_texture *ir) | |||
309 | { | |||
310 | (void) ir; | |||
311 | return visit_continue_with_parent; | |||
312 | } | |||
313 | ||||
314 | ||||
315 | ir_visitor_status | |||
316 | ir_function_inlining_visitor::visit_enter(ir_swizzle *ir) | |||
317 | { | |||
318 | (void) ir; | |||
319 | return visit_continue_with_parent; | |||
320 | } | |||
321 | ||||
322 | ||||
323 | ir_visitor_status | |||
324 | ir_function_inlining_visitor::visit_enter(ir_call *ir) | |||
325 | { | |||
326 | if (can_inline(ir)) { | |||
| ||||
327 | ir->generate_inline(ir); | |||
328 | ir->remove(); | |||
329 | this->progress = true; | |||
330 | } | |||
331 | ||||
332 | return visit_continue; | |||
333 | } | |||
334 | ||||
335 | ||||
336 | /** | |||
337 | * Replaces references to the "orig" variable with a clone of "repl." | |||
338 | * | |||
339 | * From the spec, opaque types can appear in the tree as function | |||
340 | * (non-out) parameters and as the result of array indexing and | |||
341 | * structure field selection. In our builtin implementation, they | |||
342 | * also appear in the sampler field of an ir_tex instruction. | |||
343 | */ | |||
344 | ||||
345 | class ir_variable_replacement_visitor : public ir_hierarchical_visitor { | |||
346 | public: | |||
347 | ir_variable_replacement_visitor(ir_variable *orig, ir_dereference *repl) | |||
348 | { | |||
349 | this->orig = orig; | |||
350 | this->repl = repl; | |||
351 | } | |||
352 | ||||
353 | virtual ~ir_variable_replacement_visitor() | |||
354 | { | |||
355 | } | |||
356 | ||||
357 | virtual ir_visitor_status visit_leave(ir_call *); | |||
358 | virtual ir_visitor_status visit_leave(ir_dereference_array *); | |||
359 | virtual ir_visitor_status visit_leave(ir_dereference_record *); | |||
360 | virtual ir_visitor_status visit_leave(ir_texture *); | |||
361 | virtual ir_visitor_status visit_leave(ir_assignment *); | |||
362 | virtual ir_visitor_status visit_leave(ir_expression *); | |||
363 | virtual ir_visitor_status visit_leave(ir_return *); | |||
364 | ||||
365 | void replace_deref(ir_dereference **deref); | |||
366 | void replace_rvalue(ir_rvalue **rvalue); | |||
367 | ||||
368 | ir_variable *orig; | |||
369 | ir_dereference *repl; | |||
370 | }; | |||
371 | ||||
372 | void | |||
373 | ir_variable_replacement_visitor::replace_deref(ir_dereference **deref) | |||
374 | { | |||
375 | ir_dereference_variable *deref_var = (*deref)->as_dereference_variable(); | |||
376 | if (deref_var && deref_var->var == this->orig) { | |||
377 | *deref = this->repl->clone(ralloc_parent(*deref), NULL__null); | |||
378 | } | |||
379 | } | |||
380 | ||||
381 | void | |||
382 | ir_variable_replacement_visitor::replace_rvalue(ir_rvalue **rvalue) | |||
383 | { | |||
384 | if (!*rvalue) | |||
385 | return; | |||
386 | ||||
387 | ir_dereference *deref = (*rvalue)->as_dereference(); | |||
388 | ||||
389 | if (!deref) | |||
390 | return; | |||
391 | ||||
392 | replace_deref(&deref); | |||
393 | *rvalue = deref; | |||
394 | } | |||
395 | ||||
396 | ir_visitor_status | |||
397 | ir_variable_replacement_visitor::visit_leave(ir_texture *ir) | |||
398 | { | |||
399 | replace_deref(&ir->sampler); | |||
400 | ||||
401 | return visit_continue; | |||
402 | } | |||
403 | ||||
404 | ir_visitor_status | |||
405 | ir_variable_replacement_visitor::visit_leave(ir_assignment *ir) | |||
406 | { | |||
407 | replace_deref(&ir->lhs); | |||
408 | replace_rvalue(&ir->rhs); | |||
409 | ||||
410 | return visit_continue; | |||
411 | } | |||
412 | ||||
413 | ir_visitor_status | |||
414 | ir_variable_replacement_visitor::visit_leave(ir_expression *ir) | |||
415 | { | |||
416 | for (uint8_t i = 0; i < ir->num_operands; i++) | |||
417 | replace_rvalue(&ir->operands[i]); | |||
418 | ||||
419 | return visit_continue; | |||
420 | } | |||
421 | ||||
422 | ir_visitor_status | |||
423 | ir_variable_replacement_visitor::visit_leave(ir_return *ir) | |||
424 | { | |||
425 | replace_rvalue(&ir->value); | |||
426 | ||||
427 | return visit_continue; | |||
428 | } | |||
429 | ||||
430 | ir_visitor_status | |||
431 | ir_variable_replacement_visitor::visit_leave(ir_dereference_array *ir) | |||
432 | { | |||
433 | replace_rvalue(&ir->array); | |||
434 | return visit_continue; | |||
435 | } | |||
436 | ||||
437 | ir_visitor_status | |||
438 | ir_variable_replacement_visitor::visit_leave(ir_dereference_record *ir) | |||
439 | { | |||
440 | replace_rvalue(&ir->record); | |||
441 | return visit_continue; | |||
442 | } | |||
443 | ||||
444 | ir_visitor_status | |||
445 | ir_variable_replacement_visitor::visit_leave(ir_call *ir) | |||
446 | { | |||
447 | foreach_in_list_safe(ir_rvalue, param, &ir->actual_parameters)for (ir_rvalue *param = (!exec_node_is_tail_sentinel((&ir ->actual_parameters)->head_sentinel.next) ? (ir_rvalue * ) ((&ir->actual_parameters)->head_sentinel.next) : __null ), *__next = (param) ? (!exec_node_is_tail_sentinel((&ir-> actual_parameters)->head_sentinel.next->next) ? (ir_rvalue *) ((&ir->actual_parameters)->head_sentinel.next-> next) : __null) : __null; (param) != __null; (param) = __next , __next = __next ? (!exec_node_is_tail_sentinel(__next->next ) ? (ir_rvalue *) (__next->next) : __null) : __null) { | |||
448 | ir_rvalue *new_param = param; | |||
449 | replace_rvalue(&new_param); | |||
450 | ||||
451 | if (new_param != param) { | |||
452 | param->replace_with(new_param); | |||
453 | } | |||
454 | } | |||
455 | return visit_continue; | |||
456 | } | |||
457 | ||||
458 | static void | |||
459 | do_variable_replacement(exec_list *instructions, | |||
460 | ir_variable *orig, | |||
461 | ir_dereference *repl) | |||
462 | { | |||
463 | ir_variable_replacement_visitor v(orig, repl); | |||
464 | ||||
465 | visit_list_elements(&v, instructions); | |||
466 | } |