blob: 09604c04df9e71f5ba0d42ccc3a9e909896d2421 [file] [log] [blame]
Eric Anholtcad97662010-04-07 11:46:26 -07001/*
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 ir_function_inlining.cpp
26 *
27 * Replaces calls to functions with the body of the function.
28 */
29
30#define NULL 0
31#include "ir.h"
32#include "ir_visitor.h"
33#include "ir_function_inlining.h"
Eric Anholt0d423212010-04-16 12:53:46 -070034#include "ir_expression_flattening.h"
Eric Anholtcad97662010-04-07 11:46:26 -070035#include "glsl_types.h"
36
Eric Anholtbdd9b1f2010-05-05 11:45:30 -070037class ir_function_inlining_visitor : public ir_visitor {
38public:
39 ir_function_inlining_visitor()
40 {
41 /* empty */
42 }
43
44 virtual ~ir_function_inlining_visitor()
45 {
46 /* empty */
47 }
48
49 /**
50 * \name Visit methods
51 *
52 * As typical for the visitor pattern, there must be one \c visit method for
53 * each concrete subclass of \c ir_instruction. Virtual base classes within
54 * the hierarchy should not have \c visit methods.
55 */
56 /*@{*/
57 virtual void visit(ir_variable *);
58 virtual void visit(ir_loop *);
59 virtual void visit(ir_loop_jump *);
60 virtual void visit(ir_function_signature *);
61 virtual void visit(ir_function *);
62 virtual void visit(ir_expression *);
63 virtual void visit(ir_swizzle *);
64 virtual void visit(ir_dereference *);
65 virtual void visit(ir_assignment *);
66 virtual void visit(ir_constant *);
67 virtual void visit(ir_call *);
68 virtual void visit(ir_return *);
69 virtual void visit(ir_if *);
70 /*@}*/
71};
72
Eric Anholtcad97662010-04-07 11:46:26 -070073class variable_remap : public exec_node {
74public:
75 variable_remap(const ir_variable *old_var, ir_variable *new_var)
76 : old_var(old_var), new_var(new_var)
77 {
78 /* empty */
79 }
80 const ir_variable *old_var;
81 ir_variable *new_var;
82};
83
84class ir_function_cloning_visitor : public ir_visitor {
85public:
86 ir_function_cloning_visitor(ir_variable *retval)
87 : retval(retval)
88 {
89 /* empty */
90 }
91
92 virtual ~ir_function_cloning_visitor()
93 {
94 /* empty */
95 }
96
97 void remap_variable(const ir_variable *old_var, ir_variable *new_var) {
98 variable_remap *remap = new variable_remap(old_var, new_var);
99 this->remap_list.push_tail(remap);
100 }
101
102 ir_variable *get_remapped_variable(ir_variable *var) {
103 foreach_iter(exec_list_iterator, iter, this->remap_list) {
104 variable_remap *remap = (variable_remap *)iter.get();
105
106 if (var == remap->old_var)
107 return remap->new_var;
108 }
109
110 /* Not a reapped variable, so a global scoped reference, for example. */
111 return var;
112 }
113
114 /* List of variable_remap for mapping from original function body variables
115 * to inlined function body variables.
116 */
117 exec_list remap_list;
118
119 /* Return value for the inlined function. */
120 ir_variable *retval;
121
122 /**
123 * \name Visit methods
124 *
125 * As typical for the visitor pattern, there must be one \c visit method for
126 * each concrete subclass of \c ir_instruction. Virtual base classes within
127 * the hierarchy should not have \c visit methods.
128 */
129 /*@{*/
130 virtual void visit(ir_variable *);
Eric Anholtcad97662010-04-07 11:46:26 -0700131 virtual void visit(ir_loop *);
132 virtual void visit(ir_loop_jump *);
133 virtual void visit(ir_function_signature *);
134 virtual void visit(ir_function *);
135 virtual void visit(ir_expression *);
136 virtual void visit(ir_swizzle *);
137 virtual void visit(ir_dereference *);
138 virtual void visit(ir_assignment *);
139 virtual void visit(ir_constant *);
140 virtual void visit(ir_call *);
141 virtual void visit(ir_return *);
142 virtual void visit(ir_if *);
143 /*@}*/
144
145 ir_instruction *result;
146};
147
148void
149ir_function_cloning_visitor::visit(ir_variable *ir)
150{
151 ir_variable *new_var = ir->clone();
152
153 this->result = new_var;
154
155 this->remap_variable(ir, new_var);
156}
157
158void
Eric Anholtcad97662010-04-07 11:46:26 -0700159ir_function_cloning_visitor::visit(ir_loop *ir)
160{
Eric Anholte8e97482010-04-22 17:52:59 -0700161 /* FINISHME: Implement loop cloning. */
162 assert(0);
163
Eric Anholtcad97662010-04-07 11:46:26 -0700164 (void)ir;
165 this->result = NULL;
166}
167
168void
169ir_function_cloning_visitor::visit(ir_loop_jump *ir)
170{
Eric Anholte8e97482010-04-22 17:52:59 -0700171 /* FINISHME: Implement loop cloning. */
172 assert(0);
173
Eric Anholtcad97662010-04-07 11:46:26 -0700174 (void) ir;
175 this->result = NULL;
176}
177
178
179void
180ir_function_cloning_visitor::visit(ir_function_signature *ir)
181{
Eric Anholte8e97482010-04-22 17:52:59 -0700182 assert(0);
Eric Anholtcad97662010-04-07 11:46:26 -0700183 (void)ir;
184 this->result = NULL;
185}
186
187
188void
189ir_function_cloning_visitor::visit(ir_function *ir)
190{
Eric Anholte8e97482010-04-22 17:52:59 -0700191 assert(0);
Eric Anholtcad97662010-04-07 11:46:26 -0700192 (void) ir;
193 this->result = NULL;
194}
195
196void
197ir_function_cloning_visitor::visit(ir_expression *ir)
198{
199 unsigned int operand;
200 ir_rvalue *op[2] = {NULL, NULL};
201
202 for (operand = 0; operand < ir->get_num_operands(); operand++) {
203 ir->operands[operand]->accept(this);
204 op[operand] = this->result->as_rvalue();
205 assert(op[operand]);
206 }
207
208 this->result = new ir_expression(ir->operation, ir->type, op[0], op[1]);
209}
210
211
212void
213ir_function_cloning_visitor::visit(ir_swizzle *ir)
214{
215 ir->val->accept(this);
216
217 this->result = new ir_swizzle(this->result->as_rvalue(), ir->mask);
218}
219
220void
221ir_function_cloning_visitor::visit(ir_dereference *ir)
222{
Eric Anholtc0bfe872010-04-26 15:01:50 -0700223 ir_variable *old_var = ir->var->as_variable();
224 ir_instruction *var;
Eric Anholtcad97662010-04-07 11:46:26 -0700225
Eric Anholtc0bfe872010-04-26 15:01:50 -0700226 if (old_var)
227 var = this->get_remapped_variable(old_var);
228 else {
Eric Anholt61924342010-04-08 13:40:52 -0700229 ir->var->accept(this);
Eric Anholtc0bfe872010-04-26 15:01:50 -0700230 var = this->result;
231 }
232
233 if (ir->mode == ir_dereference::ir_reference_variable) {
234 this->result = new ir_dereference(var);
235 } else if (ir->mode == ir_dereference::ir_reference_array) {
236 ir_rvalue *index;
Eric Anholt61924342010-04-08 13:40:52 -0700237
238 ir->selector.array_index->accept(this);
239 index = this->result->as_rvalue();
240
Eric Anholtc0bfe872010-04-26 15:01:50 -0700241 this->result = new ir_dereference(var, index);
Eric Anholtcad97662010-04-07 11:46:26 -0700242 } else {
Eric Anholt61924342010-04-08 13:40:52 -0700243 assert(ir->mode == ir_dereference::ir_reference_record);
Eric Anholt35e8e462010-04-26 15:02:40 -0700244 this->result = new ir_dereference(var, strdup(ir->selector.field));
Eric Anholtcad97662010-04-07 11:46:26 -0700245 }
246}
247
248void
249ir_function_cloning_visitor::visit(ir_assignment *ir)
250{
Eric Anholt22147be2010-04-22 18:41:32 -0700251 ir_rvalue *lhs, *rhs, *condition = NULL;
Eric Anholtcad97662010-04-07 11:46:26 -0700252
253 ir->lhs->accept(this);
254 lhs = this->result->as_rvalue();
255
256 ir->rhs->accept(this);
257 rhs = this->result->as_rvalue();
258
Eric Anholt22147be2010-04-22 18:41:32 -0700259 if (ir->condition) {
260 ir->condition->accept(this);
261 condition = this->result->as_rvalue();
262 }
Eric Anholtcad97662010-04-07 11:46:26 -0700263
264 this->result = new ir_assignment(lhs, rhs, condition);
265}
266
267
268void
269ir_function_cloning_visitor::visit(ir_constant *ir)
270{
271 this->result = ir->clone();
272}
273
274
275void
276ir_function_cloning_visitor::visit(ir_call *ir)
277{
278 exec_list parameters;
279
280 foreach_iter(exec_list_iterator, iter, *ir) {
281 ir_rvalue *param = (ir_rvalue *)iter.get();
282
283 param->accept(this);
284 parameters.push_tail(this->result);
285 }
286
287 this->result = new ir_call(ir->get_callee(), &parameters);
288}
289
290
291void
292ir_function_cloning_visitor::visit(ir_return *ir)
293{
294 ir_rvalue *rval;
295
296 assert(this->retval);
297
298 rval = ir->get_value();
299 rval->accept(this);
300 rval = this->result->as_rvalue();
301 assert(rval);
302
Kenneth Graunke05ddeba2010-05-01 00:31:35 -0700303 result = new ir_assignment(new ir_dereference(this->retval), rval, NULL);
Eric Anholtcad97662010-04-07 11:46:26 -0700304}
305
306
307void
308ir_function_cloning_visitor::visit(ir_if *ir)
309{
Eric Anholte8e97482010-04-22 17:52:59 -0700310 /* FINISHME: Implement if cloning. */
311 assert(0);
312
Eric Anholtcad97662010-04-07 11:46:26 -0700313 (void) ir;
314 result = NULL;
315}
316
317bool
Eric Anholt0d423212010-04-16 12:53:46 -0700318automatic_inlining_predicate(ir_instruction *ir)
319{
320 ir_call *call = ir->as_call();
321
322 if (call && can_inline(call))
323 return true;
324
325 return false;
326}
327
328bool
Eric Anholtcad97662010-04-07 11:46:26 -0700329do_function_inlining(exec_list *instructions)
330{
Eric Anholt2a7b2b22010-04-08 13:42:48 -0700331 bool progress = false;
Eric Anholtcad97662010-04-07 11:46:26 -0700332
Eric Anholt0d423212010-04-16 12:53:46 -0700333 do_expression_flattening(instructions, automatic_inlining_predicate);
334
Eric Anholtcad97662010-04-07 11:46:26 -0700335 foreach_iter(exec_list_iterator, iter, *instructions) {
336 ir_instruction *ir = (ir_instruction *)iter.get();
337 ir_assignment *assign = ir->as_assignment();
338 ir_call *call;
339
340 if (assign) {
341 call = assign->rhs->as_call();
342 if (!call || !can_inline(call))
343 continue;
344
345 /* generates the parameter setup, function body, and returns the return
346 * value of the function
347 */
348 ir_rvalue *rhs = call->generate_inline(ir);
349 assert(rhs);
350
351 assign->rhs = rhs;
352 progress = true;
353 } else if ((call = ir->as_call()) && can_inline(call)) {
354 (void)call->generate_inline(ir);
355 ir->remove();
356 progress = true;
357 } else {
358 ir_function_inlining_visitor v;
359 ir->accept(&v);
360 }
361 }
362
363 return progress;
364}
365
366ir_rvalue *
367ir_call::generate_inline(ir_instruction *next_ir)
368{
369 ir_variable **parameters;
370 int num_parameters;
371 int i;
372 ir_variable *retval = NULL;
373
374 num_parameters = 0;
375 foreach_iter(exec_list_iterator, iter_sig, this->callee->parameters)
376 num_parameters++;
377
378 parameters = new ir_variable *[num_parameters];
379
380 /* Generate storage for the return value. */
381 if (this->callee->return_type) {
382 retval = new ir_variable(this->callee->return_type, "__retval");
383 next_ir->insert_before(retval);
384 }
385
386 ir_function_cloning_visitor v = ir_function_cloning_visitor(retval);
387
388 /* Generate the declarations for the parameters to our inlined code,
389 * and set up the mapping of real function body variables to ours.
390 */
391 i = 0;
392 exec_list_iterator sig_param_iter = this->callee->parameters.iterator();
393 exec_list_iterator param_iter = this->actual_parameters.iterator();
394 for (i = 0; i < num_parameters; i++) {
395 const ir_variable *const sig_param = (ir_variable *) sig_param_iter.get();
396 ir_rvalue *param = (ir_rvalue *) param_iter.get();
397
398 /* Generate a new variable for the parameter. */
399 parameters[i] = sig_param->clone();
400 next_ir->insert_before(parameters[i]);
401
402 v.remap_variable(sig_param, parameters[i]);
403
404 /* Move the actual param into our param variable if it's an 'in' type. */
405 if (parameters[i]->mode == ir_var_in ||
406 parameters[i]->mode == ir_var_inout) {
407 ir_assignment *assign;
408
409 assign = new ir_assignment(new ir_dereference(parameters[i]),
410 param, NULL);
411 next_ir->insert_before(assign);
412 }
413
414 sig_param_iter.next();
415 param_iter.next();
416 }
417
418 /* Generate the inlined body of the function. */
419 foreach_iter(exec_list_iterator, iter, callee->body) {
420 ir_instruction *ir = (ir_instruction *)iter.get();
421
422 ir->accept(&v);
423 assert(v.result);
424 next_ir->insert_before(v.result);
425 }
426
Kenneth Graunkec07fdae2010-04-30 23:38:50 -0700427 /* Copy back the value of any 'out' parameters from the function body
428 * variables to our own.
Eric Anholtcad97662010-04-07 11:46:26 -0700429 */
430 i = 0;
431 param_iter = this->actual_parameters.iterator();
432 for (i = 0; i < num_parameters; i++) {
433 ir_instruction *const param = (ir_instruction *) param_iter.get();
434
Kenneth Graunkec07fdae2010-04-30 23:38:50 -0700435 /* Move our param variable into the actual param if it's an 'out' type. */
Eric Anholtcad97662010-04-07 11:46:26 -0700436 if (parameters[i]->mode == ir_var_out ||
437 parameters[i]->mode == ir_var_inout) {
438 ir_assignment *assign;
439
440 assign = new ir_assignment(param->as_rvalue(),
441 new ir_dereference(parameters[i]),
442 NULL);
443 next_ir->insert_before(assign);
444 }
445
446 param_iter.next();
447 }
448
449 delete(parameters);
450
451 if (retval)
452 return new ir_dereference(retval);
453 else
454 return NULL;
455}
456
457void
458ir_function_inlining_visitor::visit(ir_variable *ir)
459{
460 (void) ir;
461}
462
463
464void
Eric Anholtcad97662010-04-07 11:46:26 -0700465ir_function_inlining_visitor::visit(ir_loop *ir)
466{
467 do_function_inlining(&ir->body_instructions);
468}
469
470void
471ir_function_inlining_visitor::visit(ir_loop_jump *ir)
472{
473 (void) ir;
474}
475
476
477void
478ir_function_inlining_visitor::visit(ir_function_signature *ir)
479{
480 do_function_inlining(&ir->body);
481}
482
483
484void
485ir_function_inlining_visitor::visit(ir_function *ir)
486{
Kenneth Graunke9fa99f32010-04-21 12:30:22 -0700487 foreach_iter(exec_list_iterator, iter, *ir) {
488 ir_function_signature *const sig = (ir_function_signature *) iter.get();
489 sig->accept(this);
490 }
Eric Anholtcad97662010-04-07 11:46:26 -0700491}
492
493void
494ir_function_inlining_visitor::visit(ir_expression *ir)
495{
496 unsigned int operand;
497
498 for (operand = 0; operand < ir->get_num_operands(); operand++) {
499 ir->operands[operand]->accept(this);
500 }
501}
502
503
504void
505ir_function_inlining_visitor::visit(ir_swizzle *ir)
506{
507 ir->val->accept(this);
508}
509
510
511void
512ir_function_inlining_visitor::visit(ir_dereference *ir)
513{
514 if (ir->mode == ir_dereference::ir_reference_array) {
515 ir->selector.array_index->accept(this);
516 }
517 ir->var->accept(this);
518}
519
520void
521ir_function_inlining_visitor::visit(ir_assignment *ir)
522{
523 ir->rhs->accept(this);
524}
525
526
527void
528ir_function_inlining_visitor::visit(ir_constant *ir)
529{
530 (void) ir;
531}
532
533
534void
535ir_function_inlining_visitor::visit(ir_call *ir)
536{
537 (void) ir;
538}
539
540
541void
542ir_function_inlining_visitor::visit(ir_return *ir)
543{
544 (void) ir;
545}
546
547
548void
549ir_function_inlining_visitor::visit(ir_if *ir)
550{
551 ir->condition->accept(this);
552
553 do_function_inlining(&ir->then_instructions);
554 do_function_inlining(&ir->else_instructions);
555}