• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <inttypes.h>
31 #include "ir.h"
32 #include "ir_visitor.h"
33 #include "ir_function_inlining.h"
34 #include "ir_expression_flattening.h"
35 #include "glsl_types.h"
36 #include "program/hash_table.h"
37 
38 static void
39 do_sampler_replacement(exec_list *instructions,
40 		       ir_variable *sampler,
41 		       ir_dereference *deref);
42 
43 class ir_function_inlining_visitor : public ir_hierarchical_visitor {
44 public:
ir_function_inlining_visitor()45    ir_function_inlining_visitor()
46    {
47       progress = false;
48    }
49 
~ir_function_inlining_visitor()50    virtual ~ir_function_inlining_visitor()
51    {
52       /* empty */
53    }
54 
55    virtual ir_visitor_status visit_enter(ir_expression *);
56    virtual ir_visitor_status visit_enter(ir_call *);
57    virtual ir_visitor_status visit_enter(ir_assignment *);
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 
66 bool
automatic_inlining_predicate(ir_instruction * ir)67 automatic_inlining_predicate(ir_instruction *ir)
68 {
69    ir_call *call = ir->as_call();
70 
71    if (call && can_inline(call))
72       return true;
73 
74    return false;
75 }
76 
77 bool
do_function_inlining(exec_list * instructions)78 do_function_inlining(exec_list *instructions)
79 {
80    ir_function_inlining_visitor v;
81 
82    do_expression_flattening(instructions, automatic_inlining_predicate);
83 
84    v.run(instructions);
85 
86    return v.progress;
87 }
88 
89 static void
replace_return_with_assignment(ir_instruction * ir,void * data)90 replace_return_with_assignment(ir_instruction *ir, void *data)
91 {
92    void *ctx = hieralloc_parent(ir);
93    ir_variable *retval = (ir_variable *)data;
94    ir_return *ret = ir->as_return();
95 
96    if (ret) {
97       if (ret->value) {
98 	 ir_rvalue *lhs = new(ctx) ir_dereference_variable(retval);
99 	 ret->replace_with(new(ctx) ir_assignment(lhs, ret->value, NULL));
100       } else {
101 	 /* un-valued return has to be the last return, or we shouldn't
102 	  * have reached here. (see can_inline()).
103 	  */
104 	 assert(ret->next->is_tail_sentinel());
105 	 ret->remove();
106       }
107    }
108 }
109 
110 ir_rvalue *
generate_inline(ir_instruction * next_ir)111 ir_call::generate_inline(ir_instruction *next_ir)
112 {
113    void *ctx = hieralloc_parent(this);
114    ir_variable **parameters;
115    int num_parameters;
116    int i;
117    ir_variable *retval = NULL;
118    struct hash_table *ht;
119 
120    ht = hash_table_ctor(0, hash_table_pointer_hash, hash_table_pointer_compare);
121 
122    num_parameters = 0;
123    foreach_iter(exec_list_iterator, iter_sig, this->callee->parameters)
124       num_parameters++;
125 
126    parameters = new ir_variable *[num_parameters];
127 
128    /* Generate storage for the return value. */
129    if (this->callee->return_type) {
130       retval = new(ctx) ir_variable(this->callee->return_type, "_ret_val",
131 				    ir_var_auto);
132       next_ir->insert_before(retval);
133    }
134 
135    /* Generate the declarations for the parameters to our inlined code,
136     * and set up the mapping of real function body variables to ours.
137     */
138    i = 0;
139    exec_list_iterator sig_param_iter = this->callee->parameters.iterator();
140    exec_list_iterator param_iter = this->actual_parameters.iterator();
141    for (i = 0; i < num_parameters; i++) {
142       ir_variable *sig_param = (ir_variable *) sig_param_iter.get();
143       ir_rvalue *param = (ir_rvalue *) param_iter.get();
144 
145       /* Generate a new variable for the parameter. */
146       if (sig_param->type->base_type == GLSL_TYPE_SAMPLER) {
147 	 /* For samplers, we want the inlined sampler references
148 	  * referencing the passed in sampler variable, since that
149 	  * will have the location information, which an assignment of
150 	  * a sampler wouldn't.  Fix it up below.
151 	  */
152 	 parameters[i] = NULL;
153       } else {
154 	 parameters[i] = sig_param->clone(ctx, ht);
155 	 parameters[i]->mode = ir_var_auto;
156 
157 	 /* Remove the read-only decoration becuase we're going to write
158 	  * directly to this variable.  If the cloned variable is left
159 	  * read-only and the inlined function is inside a loop, the loop
160 	  * analysis code will get confused.
161 	  */
162 	 parameters[i]->read_only = false;
163 	 next_ir->insert_before(parameters[i]);
164       }
165 
166       /* Move the actual param into our param variable if it's an 'in' type. */
167       if (parameters[i] && (sig_param->mode == ir_var_in ||
168 			    sig_param->mode == ir_var_inout)) {
169 	 ir_assignment *assign;
170 
171 	 assign = new(ctx) ir_assignment(new(ctx) ir_dereference_variable(parameters[i]),
172 					 param, NULL);
173 	 next_ir->insert_before(assign);
174       }
175 
176       sig_param_iter.next();
177       param_iter.next();
178    }
179 
180    exec_list new_instructions;
181 
182    /* Generate the inlined body of the function to a new list */
183    foreach_iter(exec_list_iterator, iter, callee->body) {
184       ir_instruction *ir = (ir_instruction *)iter.get();
185       ir_instruction *new_ir = ir->clone(ctx, ht);
186 
187       new_instructions.push_tail(new_ir);
188       visit_tree(new_ir, replace_return_with_assignment, retval);
189    }
190 
191    /* If any samplers were passed in, replace any deref of the sampler
192     * with a deref of the sampler argument.
193     */
194    param_iter = this->actual_parameters.iterator();
195    sig_param_iter = this->callee->parameters.iterator();
196    for (i = 0; i < num_parameters; i++) {
197       ir_instruction *const param = (ir_instruction *) param_iter.get();
198       ir_variable *sig_param = (ir_variable *) sig_param_iter.get();
199 
200       if (sig_param->type->base_type == GLSL_TYPE_SAMPLER) {
201 	 ir_dereference *deref = param->as_dereference();
202 
203 	 assert(deref);
204 	 do_sampler_replacement(&new_instructions, sig_param, deref);
205       }
206       param_iter.next();
207       sig_param_iter.next();
208    }
209 
210    /* Now push those new instructions in. */
211    foreach_iter(exec_list_iterator, iter, new_instructions) {
212       ir_instruction *ir = (ir_instruction *)iter.get();
213       next_ir->insert_before(ir);
214    }
215 
216    /* Copy back the value of any 'out' parameters from the function body
217     * variables to our own.
218     */
219    i = 0;
220    param_iter = this->actual_parameters.iterator();
221    sig_param_iter = this->callee->parameters.iterator();
222    for (i = 0; i < num_parameters; i++) {
223       ir_instruction *const param = (ir_instruction *) param_iter.get();
224       const ir_variable *const sig_param = (ir_variable *) sig_param_iter.get();
225 
226       /* Move our param variable into the actual param if it's an 'out' type. */
227       if (parameters[i] && (sig_param->mode == ir_var_out ||
228 			    sig_param->mode == ir_var_inout)) {
229 	 ir_assignment *assign;
230 
231 	 assign = new(ctx) ir_assignment(param->clone(ctx, NULL)->as_rvalue(),
232 					 new(ctx) ir_dereference_variable(parameters[i]),
233 					 NULL);
234 	 next_ir->insert_before(assign);
235       }
236 
237       param_iter.next();
238       sig_param_iter.next();
239    }
240 
241    delete [] parameters;
242 
243    hash_table_dtor(ht);
244 
245    if (retval)
246       return new(ctx) ir_dereference_variable(retval);
247    else
248       return NULL;
249 }
250 
251 
252 ir_visitor_status
visit_enter(ir_expression * ir)253 ir_function_inlining_visitor::visit_enter(ir_expression *ir)
254 {
255    (void) ir;
256    return visit_continue_with_parent;
257 }
258 
259 
260 ir_visitor_status
visit_enter(ir_return * ir)261 ir_function_inlining_visitor::visit_enter(ir_return *ir)
262 {
263    (void) ir;
264    return visit_continue_with_parent;
265 }
266 
267 
268 ir_visitor_status
visit_enter(ir_texture * ir)269 ir_function_inlining_visitor::visit_enter(ir_texture *ir)
270 {
271    (void) ir;
272    return visit_continue_with_parent;
273 }
274 
275 
276 ir_visitor_status
visit_enter(ir_swizzle * ir)277 ir_function_inlining_visitor::visit_enter(ir_swizzle *ir)
278 {
279    (void) ir;
280    return visit_continue_with_parent;
281 }
282 
283 
284 ir_visitor_status
visit_enter(ir_call * ir)285 ir_function_inlining_visitor::visit_enter(ir_call *ir)
286 {
287    if (can_inline(ir)) {
288       /* If the call was part of some tree, then it should have been
289        * flattened out or we shouldn't have seen it because of a
290        * visit_continue_with_parent in this visitor.
291        */
292       assert(ir == base_ir);
293 
294       (void) ir->generate_inline(ir);
295       ir->remove();
296       this->progress = true;
297    }
298 
299    return visit_continue;
300 }
301 
302 
303 ir_visitor_status
visit_enter(ir_assignment * ir)304 ir_function_inlining_visitor::visit_enter(ir_assignment *ir)
305 {
306    ir_call *call = ir->rhs->as_call();
307    if (!call || !can_inline(call))
308       return visit_continue;
309 
310    /* generates the parameter setup, function body, and returns the return
311     * value of the function
312     */
313    ir_rvalue *rhs = call->generate_inline(ir);
314    assert(rhs);
315 
316    ir->rhs = rhs;
317    this->progress = true;
318 
319    return visit_continue;
320 }
321 
322 /**
323  * Replaces references to the "sampler" variable with a clone of "deref."
324  *
325  * From the spec, samplers can appear in the tree as function
326  * (non-out) parameters and as the result of array indexing and
327  * structure field selection.  In our builtin implementation, they
328  * also appear in the sampler field of an ir_tex instruction.
329  */
330 
331 class ir_sampler_replacement_visitor : public ir_hierarchical_visitor {
332 public:
ir_sampler_replacement_visitor(ir_variable * sampler,ir_dereference * deref)333    ir_sampler_replacement_visitor(ir_variable *sampler, ir_dereference *deref)
334    {
335       this->sampler = sampler;
336       this->deref = deref;
337    }
338 
~ir_sampler_replacement_visitor()339    virtual ~ir_sampler_replacement_visitor()
340    {
341    }
342 
343    virtual ir_visitor_status visit_leave(ir_call *);
344    virtual ir_visitor_status visit_leave(ir_dereference_array *);
345    virtual ir_visitor_status visit_leave(ir_dereference_record *);
346    virtual ir_visitor_status visit_leave(ir_texture *);
347 
348    void replace_deref(ir_dereference **deref);
349    void replace_rvalue(ir_rvalue **rvalue);
350 
351    ir_variable *sampler;
352    ir_dereference *deref;
353 };
354 
355 void
replace_deref(ir_dereference ** deref)356 ir_sampler_replacement_visitor::replace_deref(ir_dereference **deref)
357 {
358    ir_dereference_variable *deref_var = (*deref)->as_dereference_variable();
359    if (deref_var && deref_var->var == this->sampler) {
360       *deref = this->deref->clone(hieralloc_parent(*deref), NULL);
361    }
362 }
363 
364 void
replace_rvalue(ir_rvalue ** rvalue)365 ir_sampler_replacement_visitor::replace_rvalue(ir_rvalue **rvalue)
366 {
367    if (!*rvalue)
368       return;
369 
370    ir_dereference *deref = (*rvalue)->as_dereference();
371 
372    if (!deref)
373       return;
374 
375    replace_deref(&deref);
376    *rvalue = deref;
377 }
378 
379 ir_visitor_status
visit_leave(ir_texture * ir)380 ir_sampler_replacement_visitor::visit_leave(ir_texture *ir)
381 {
382    replace_deref(&ir->sampler);
383 
384    return visit_continue;
385 }
386 
387 ir_visitor_status
visit_leave(ir_dereference_array * ir)388 ir_sampler_replacement_visitor::visit_leave(ir_dereference_array *ir)
389 {
390    replace_rvalue(&ir->array);
391    return visit_continue;
392 }
393 
394 ir_visitor_status
visit_leave(ir_dereference_record * ir)395 ir_sampler_replacement_visitor::visit_leave(ir_dereference_record *ir)
396 {
397    replace_rvalue(&ir->record);
398    return visit_continue;
399 }
400 
401 ir_visitor_status
visit_leave(ir_call * ir)402 ir_sampler_replacement_visitor::visit_leave(ir_call *ir)
403 {
404    foreach_iter(exec_list_iterator, iter, *ir) {
405       ir_rvalue *param = (ir_rvalue *)iter.get();
406       ir_rvalue *new_param = param;
407       replace_rvalue(&new_param);
408 
409       if (new_param != param) {
410 	 param->replace_with(new_param);
411       }
412    }
413    return visit_continue;
414 }
415 
416 static void
do_sampler_replacement(exec_list * instructions,ir_variable * sampler,ir_dereference * deref)417 do_sampler_replacement(exec_list *instructions,
418 		       ir_variable *sampler,
419 		       ir_dereference *deref)
420 {
421    ir_sampler_replacement_visitor v(sampler, deref);
422 
423    visit_list_elements(&v, instructions);
424 }
425