2 * Copyright © 2010 Intel Corporation
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:
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
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.
25 * \file ir_function_inlining.cpp
27 * Replaces calls to functions with the body of the function.
32 #include "ir_visitor.h"
33 #include "ir_function_inlining.h"
34 #include "ir_expression_flattening.h"
35 #include "glsl_types.h"
37 class ir_function_inlining_visitor
: public ir_hierarchical_visitor
{
39 ir_function_inlining_visitor()
44 virtual ~ir_function_inlining_visitor()
49 virtual ir_visitor_status
visit_enter(ir_expression
*);
50 virtual ir_visitor_status
visit_enter(ir_call
*);
51 virtual ir_visitor_status
visit_enter(ir_assignment
*);
52 virtual ir_visitor_status
visit_enter(ir_return
*);
53 virtual ir_visitor_status
visit_enter(ir_texture
*);
54 virtual ir_visitor_status
visit_enter(ir_swizzle
*);
59 class variable_remap
: public exec_node
{
61 variable_remap(const ir_variable
*old_var
, ir_variable
*new_var
)
62 : old_var(old_var
), new_var(new_var
)
66 const ir_variable
*old_var
;
70 class ir_function_cloning_visitor
: public ir_visitor
{
72 ir_function_cloning_visitor(ir_variable
*retval
)
78 virtual ~ir_function_cloning_visitor()
83 void remap_variable(const ir_variable
*old_var
, ir_variable
*new_var
) {
84 variable_remap
*remap
= new variable_remap(old_var
, new_var
);
85 this->remap_list
.push_tail(remap
);
88 ir_variable
*get_remapped_variable(ir_variable
*var
) {
89 foreach_iter(exec_list_iterator
, iter
, this->remap_list
) {
90 variable_remap
*remap
= (variable_remap
*)iter
.get();
92 if (var
== remap
->old_var
)
93 return remap
->new_var
;
96 /* Not a reapped variable, so a global scoped reference, for example. */
100 /* List of variable_remap for mapping from original function body variables
101 * to inlined function body variables.
103 exec_list remap_list
;
105 /* Return value for the inlined function. */
109 * \name Visit methods
111 * As typical for the visitor pattern, there must be one \c visit method for
112 * each concrete subclass of \c ir_instruction. Virtual base classes within
113 * the hierarchy should not have \c visit methods.
116 virtual void visit(ir_variable
*);
117 virtual void visit(ir_loop
*);
118 virtual void visit(ir_loop_jump
*);
119 virtual void visit(ir_function_signature
*);
120 virtual void visit(ir_function
*);
121 virtual void visit(ir_expression
*);
122 virtual void visit(ir_texture
*);
123 virtual void visit(ir_swizzle
*);
124 virtual void visit(ir_dereference_variable
*);
125 virtual void visit(ir_dereference_array
*);
126 virtual void visit(ir_dereference_record
*);
127 virtual void visit(ir_assignment
*);
128 virtual void visit(ir_constant
*);
129 virtual void visit(ir_call
*);
130 virtual void visit(ir_return
*);
131 virtual void visit(ir_if
*);
134 ir_instruction
*result
;
138 ir_function_cloning_visitor::visit(ir_variable
*ir
)
140 ir_variable
*new_var
= ir
->clone();
142 this->result
= new_var
;
144 this->remap_variable(ir
, new_var
);
148 ir_function_cloning_visitor::visit(ir_loop
*ir
)
150 /* FINISHME: Implement loop cloning. */
158 ir_function_cloning_visitor::visit(ir_loop_jump
*ir
)
160 /* FINISHME: Implement loop cloning. */
169 ir_function_cloning_visitor::visit(ir_function_signature
*ir
)
178 ir_function_cloning_visitor::visit(ir_function
*ir
)
186 ir_function_cloning_visitor::visit(ir_expression
*ir
)
188 unsigned int operand
;
189 ir_rvalue
*op
[2] = {NULL
, NULL
};
191 for (operand
= 0; operand
< ir
->get_num_operands(); operand
++) {
192 ir
->operands
[operand
]->accept(this);
193 op
[operand
] = this->result
->as_rvalue();
197 this->result
= new ir_expression(ir
->operation
, ir
->type
, op
[0], op
[1]);
202 ir_function_cloning_visitor::visit(ir_texture
*ir
)
204 ir_texture
*tex
= new ir_texture(ir
->op
);
206 ir
->sampler
->accept(this);
207 tex
->set_sampler(this->result
->as_dereference());
209 ir
->coordinate
->accept(this);
210 tex
->coordinate
= this->result
->as_rvalue();
212 if (ir
->projector
!= NULL
) {
213 ir
->projector
->accept(this);
214 tex
->projector
= this->result
->as_rvalue();
217 if (ir
->shadow_comparitor
!= NULL
) {
218 ir
->shadow_comparitor
->accept(this);
219 tex
->shadow_comparitor
= this->result
->as_rvalue();
222 for (int i
= 0; i
< 3; i
++)
223 tex
->offsets
[i
] = ir
->offsets
[i
];
225 tex
->lod_info
= ir
->lod_info
;
230 ir_function_cloning_visitor::visit(ir_swizzle
*ir
)
232 ir
->val
->accept(this);
234 this->result
= new ir_swizzle(this->result
->as_rvalue(), ir
->mask
);
238 ir_function_cloning_visitor::visit(ir_dereference_variable
*ir
)
240 ir_variable
*var
= this->get_remapped_variable(ir
->variable_referenced());
241 this->result
= new ir_dereference_variable(var
);
245 ir_function_cloning_visitor::visit(ir_dereference_array
*ir
)
247 ir
->array
->accept(this);
249 ir_rvalue
*var
= this->result
->as_rvalue();
251 ir
->array_index
->accept(this);
253 ir_rvalue
*index
= this->result
->as_rvalue();
255 this->result
= new ir_dereference_array(var
, index
);
259 ir_function_cloning_visitor::visit(ir_dereference_record
*ir
)
261 ir
->record
->accept(this);
263 ir_rvalue
*var
= this->result
->as_rvalue();
265 this->result
= new ir_dereference_record(var
, strdup(ir
->field
));
269 ir_function_cloning_visitor::visit(ir_assignment
*ir
)
271 ir_rvalue
*lhs
, *rhs
, *condition
= NULL
;
273 ir
->lhs
->accept(this);
274 lhs
= this->result
->as_rvalue();
276 ir
->rhs
->accept(this);
277 rhs
= this->result
->as_rvalue();
280 ir
->condition
->accept(this);
281 condition
= this->result
->as_rvalue();
284 this->result
= new ir_assignment(lhs
, rhs
, condition
);
289 ir_function_cloning_visitor::visit(ir_constant
*ir
)
291 this->result
= ir
->clone();
296 ir_function_cloning_visitor::visit(ir_call
*ir
)
298 exec_list parameters
;
300 foreach_iter(exec_list_iterator
, iter
, *ir
) {
301 ir_rvalue
*param
= (ir_rvalue
*)iter
.get();
304 parameters
.push_tail(this->result
);
307 this->result
= new ir_call(ir
->get_callee(), ¶meters
);
312 ir_function_cloning_visitor::visit(ir_return
*ir
)
316 assert(this->retval
);
318 rval
= ir
->get_value();
320 rval
= this->result
->as_rvalue();
323 result
= new ir_assignment(new ir_dereference_variable(this->retval
), rval
,
329 ir_function_cloning_visitor::visit(ir_if
*ir
)
331 /* FINISHME: Implement if cloning. */
339 automatic_inlining_predicate(ir_instruction
*ir
)
341 ir_call
*call
= ir
->as_call();
343 if (call
&& can_inline(call
))
350 do_function_inlining(exec_list
*instructions
)
352 ir_function_inlining_visitor v
;
354 do_expression_flattening(instructions
, automatic_inlining_predicate
);
362 ir_call::generate_inline(ir_instruction
*next_ir
)
364 ir_variable
**parameters
;
367 ir_variable
*retval
= NULL
;
370 foreach_iter(exec_list_iterator
, iter_sig
, this->callee
->parameters
)
373 parameters
= new ir_variable
*[num_parameters
];
375 /* Generate storage for the return value. */
376 if (this->callee
->return_type
) {
377 retval
= new ir_variable(this->callee
->return_type
, "__retval");
378 next_ir
->insert_before(retval
);
381 ir_function_cloning_visitor v
= ir_function_cloning_visitor(retval
);
383 /* Generate the declarations for the parameters to our inlined code,
384 * and set up the mapping of real function body variables to ours.
387 exec_list_iterator sig_param_iter
= this->callee
->parameters
.iterator();
388 exec_list_iterator param_iter
= this->actual_parameters
.iterator();
389 for (i
= 0; i
< num_parameters
; i
++) {
390 const ir_variable
*const sig_param
= (ir_variable
*) sig_param_iter
.get();
391 ir_rvalue
*param
= (ir_rvalue
*) param_iter
.get();
393 /* Generate a new variable for the parameter. */
394 parameters
[i
] = sig_param
->clone();
395 next_ir
->insert_before(parameters
[i
]);
397 v
.remap_variable(sig_param
, parameters
[i
]);
399 /* Move the actual param into our param variable if it's an 'in' type. */
400 if (parameters
[i
]->mode
== ir_var_in
||
401 parameters
[i
]->mode
== ir_var_inout
) {
402 ir_assignment
*assign
;
404 assign
= new ir_assignment(new ir_dereference_variable(parameters
[i
]),
406 next_ir
->insert_before(assign
);
409 sig_param_iter
.next();
413 /* Generate the inlined body of the function. */
414 foreach_iter(exec_list_iterator
, iter
, callee
->body
) {
415 ir_instruction
*ir
= (ir_instruction
*)iter
.get();
419 next_ir
->insert_before(v
.result
);
422 /* Copy back the value of any 'out' parameters from the function body
423 * variables to our own.
426 param_iter
= this->actual_parameters
.iterator();
427 for (i
= 0; i
< num_parameters
; i
++) {
428 ir_instruction
*const param
= (ir_instruction
*) param_iter
.get();
430 /* Move our param variable into the actual param if it's an 'out' type. */
431 if (parameters
[i
]->mode
== ir_var_out
||
432 parameters
[i
]->mode
== ir_var_inout
) {
433 ir_assignment
*assign
;
435 assign
= new ir_assignment(param
->as_rvalue(),
436 new ir_dereference_variable(parameters
[i
]),
438 next_ir
->insert_before(assign
);
444 delete [] parameters
;
447 return new ir_dereference_variable(retval
);
454 ir_function_inlining_visitor::visit_enter(ir_expression
*ir
)
457 return visit_continue_with_parent
;
462 ir_function_inlining_visitor::visit_enter(ir_return
*ir
)
465 return visit_continue_with_parent
;
470 ir_function_inlining_visitor::visit_enter(ir_texture
*ir
)
473 return visit_continue_with_parent
;
478 ir_function_inlining_visitor::visit_enter(ir_swizzle
*ir
)
481 return visit_continue_with_parent
;
486 ir_function_inlining_visitor::visit_enter(ir_call
*ir
)
488 if (can_inline(ir
)) {
489 (void) ir
->generate_inline(ir
);
491 this->progress
= true;
494 return visit_continue
;
499 ir_function_inlining_visitor::visit_enter(ir_assignment
*ir
)
501 ir_call
*call
= ir
->rhs
->as_call();
502 if (!call
|| !can_inline(call
))
503 return visit_continue
;
505 /* generates the parameter setup, function body, and returns the return
506 * value of the function
508 ir_rvalue
*rhs
= call
->generate_inline(ir
);
512 this->progress
= true;
514 return visit_continue
;