blob: 40f82513095b2c8f856394c140e6a7e286e7b99c [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"
34#include "glsl_types.h"
35
36class variable_remap : public exec_node {
37public:
38 variable_remap(const ir_variable *old_var, ir_variable *new_var)
39 : old_var(old_var), new_var(new_var)
40 {
41 /* empty */
42 }
43 const ir_variable *old_var;
44 ir_variable *new_var;
45};
46
47class ir_function_cloning_visitor : public ir_visitor {
48public:
49 ir_function_cloning_visitor(ir_variable *retval)
50 : retval(retval)
51 {
52 /* empty */
53 }
54
55 virtual ~ir_function_cloning_visitor()
56 {
57 /* empty */
58 }
59
60 void remap_variable(const ir_variable *old_var, ir_variable *new_var) {
61 variable_remap *remap = new variable_remap(old_var, new_var);
62 this->remap_list.push_tail(remap);
63 }
64
65 ir_variable *get_remapped_variable(ir_variable *var) {
66 foreach_iter(exec_list_iterator, iter, this->remap_list) {
67 variable_remap *remap = (variable_remap *)iter.get();
68
69 if (var == remap->old_var)
70 return remap->new_var;
71 }
72
73 /* Not a reapped variable, so a global scoped reference, for example. */
74 return var;
75 }
76
77 /* List of variable_remap for mapping from original function body variables
78 * to inlined function body variables.
79 */
80 exec_list remap_list;
81
82 /* Return value for the inlined function. */
83 ir_variable *retval;
84
85 /**
86 * \name Visit methods
87 *
88 * As typical for the visitor pattern, there must be one \c visit method for
89 * each concrete subclass of \c ir_instruction. Virtual base classes within
90 * the hierarchy should not have \c visit methods.
91 */
92 /*@{*/
93 virtual void visit(ir_variable *);
94 virtual void visit(ir_label *);
95 virtual void visit(ir_loop *);
96 virtual void visit(ir_loop_jump *);
97 virtual void visit(ir_function_signature *);
98 virtual void visit(ir_function *);
99 virtual void visit(ir_expression *);
100 virtual void visit(ir_swizzle *);
101 virtual void visit(ir_dereference *);
102 virtual void visit(ir_assignment *);
103 virtual void visit(ir_constant *);
104 virtual void visit(ir_call *);
105 virtual void visit(ir_return *);
106 virtual void visit(ir_if *);
107 /*@}*/
108
109 ir_instruction *result;
110};
111
112void
113ir_function_cloning_visitor::visit(ir_variable *ir)
114{
115 ir_variable *new_var = ir->clone();
116
117 this->result = new_var;
118
119 this->remap_variable(ir, new_var);
120}
121
122void
123ir_function_cloning_visitor::visit(ir_label *ir)
124{
125 (void)ir;
126 this->result = NULL;
127}
128
129void
130ir_function_cloning_visitor::visit(ir_loop *ir)
131{
132 (void)ir;
133 this->result = NULL;
134}
135
136void
137ir_function_cloning_visitor::visit(ir_loop_jump *ir)
138{
139 (void) ir;
140 this->result = NULL;
141}
142
143
144void
145ir_function_cloning_visitor::visit(ir_function_signature *ir)
146{
147 (void)ir;
148 this->result = NULL;
149}
150
151
152void
153ir_function_cloning_visitor::visit(ir_function *ir)
154{
155 (void) ir;
156 this->result = NULL;
157}
158
159void
160ir_function_cloning_visitor::visit(ir_expression *ir)
161{
162 unsigned int operand;
163 ir_rvalue *op[2] = {NULL, NULL};
164
165 for (operand = 0; operand < ir->get_num_operands(); operand++) {
166 ir->operands[operand]->accept(this);
167 op[operand] = this->result->as_rvalue();
168 assert(op[operand]);
169 }
170
171 this->result = new ir_expression(ir->operation, ir->type, op[0], op[1]);
172}
173
174
175void
176ir_function_cloning_visitor::visit(ir_swizzle *ir)
177{
178 ir->val->accept(this);
179
180 this->result = new ir_swizzle(this->result->as_rvalue(), ir->mask);
181}
182
183void
184ir_function_cloning_visitor::visit(ir_dereference *ir)
185{
186 if (ir->mode == ir_dereference::ir_reference_variable) {
187 ir_variable *old_var = ir->var->as_variable();
188
189 /* If it's a deref of a real variable, then we need to remap it if
190 * it was local to the function.
191 */
192 if (old_var) {
193 ir_variable *new_var;
194
195 new_var = this->get_remapped_variable(old_var);
196
197 this->result = new ir_dereference(new_var);
198 } else {
199 ir->var->accept(this);
200
201 this->result = new ir_dereference(this->result);
202 }
Eric Anholt61924342010-04-08 13:40:52 -0700203 } else if (ir->mode == ir_dereference::ir_reference_array) {
204 ir_instruction *variable;
205 ir_rvalue *index;
206
207 ir->var->accept(this);
208 variable = this->result;
209
210 ir->selector.array_index->accept(this);
211 index = this->result->as_rvalue();
212
213 this->result = new ir_dereference(variable, index);
Eric Anholtcad97662010-04-07 11:46:26 -0700214 } else {
Eric Anholt61924342010-04-08 13:40:52 -0700215 assert(ir->mode == ir_dereference::ir_reference_record);
216 /* FINISHME: inlining of structure references */
217 assert(0);
Eric Anholtcad97662010-04-07 11:46:26 -0700218 }
219}
220
221void
222ir_function_cloning_visitor::visit(ir_assignment *ir)
223{
224 ir_rvalue *lhs, *rhs, *condition;
225
226 ir->lhs->accept(this);
227 lhs = this->result->as_rvalue();
228
229 ir->rhs->accept(this);
230 rhs = this->result->as_rvalue();
231
232 ir->condition->accept(this);
233 condition = this->result->as_rvalue();
234
235 this->result = new ir_assignment(lhs, rhs, condition);
236}
237
238
239void
240ir_function_cloning_visitor::visit(ir_constant *ir)
241{
242 this->result = ir->clone();
243}
244
245
246void
247ir_function_cloning_visitor::visit(ir_call *ir)
248{
249 exec_list parameters;
250
251 foreach_iter(exec_list_iterator, iter, *ir) {
252 ir_rvalue *param = (ir_rvalue *)iter.get();
253
254 param->accept(this);
255 parameters.push_tail(this->result);
256 }
257
258 this->result = new ir_call(ir->get_callee(), &parameters);
259}
260
261
262void
263ir_function_cloning_visitor::visit(ir_return *ir)
264{
265 ir_rvalue *rval;
266
267 assert(this->retval);
268
269 rval = ir->get_value();
270 rval->accept(this);
271 rval = this->result->as_rvalue();
272 assert(rval);
273
274 result = new ir_assignment(new ir_dereference(this->retval),
275 ir->get_value(), NULL);
276}
277
278
279void
280ir_function_cloning_visitor::visit(ir_if *ir)
281{
282 (void) ir;
283 result = NULL;
284}
285
286bool
287can_inline(ir_call *call)
288{
289 bool found_return = false;
290
291 /* FINISHME: Right now we only allow a single statement that is a return.
292 */
293 foreach_iter(exec_list_iterator, iter, call->get_callee()->body) {
294 ir_instruction *ir = (ir_instruction *)iter.get();
295 if (ir->get_next()->get_next() != NULL)
296 return false;
297
298 if (!ir->as_return())
299 return false;
300
301 found_return = true;
302 }
303
304 return found_return;
305}
306
307bool
308do_function_inlining(exec_list *instructions)
309{
310 bool progress;
311
312 foreach_iter(exec_list_iterator, iter, *instructions) {
313 ir_instruction *ir = (ir_instruction *)iter.get();
314 ir_assignment *assign = ir->as_assignment();
315 ir_call *call;
316
317 if (assign) {
318 call = assign->rhs->as_call();
319 if (!call || !can_inline(call))
320 continue;
321
322 /* generates the parameter setup, function body, and returns the return
323 * value of the function
324 */
325 ir_rvalue *rhs = call->generate_inline(ir);
326 assert(rhs);
327
328 assign->rhs = rhs;
329 progress = true;
330 } else if ((call = ir->as_call()) && can_inline(call)) {
331 (void)call->generate_inline(ir);
332 ir->remove();
333 progress = true;
334 } else {
335 ir_function_inlining_visitor v;
336 ir->accept(&v);
337 }
338 }
339
340 return progress;
341}
342
343ir_rvalue *
344ir_call::generate_inline(ir_instruction *next_ir)
345{
346 ir_variable **parameters;
347 int num_parameters;
348 int i;
349 ir_variable *retval = NULL;
350
351 num_parameters = 0;
352 foreach_iter(exec_list_iterator, iter_sig, this->callee->parameters)
353 num_parameters++;
354
355 parameters = new ir_variable *[num_parameters];
356
357 /* Generate storage for the return value. */
358 if (this->callee->return_type) {
359 retval = new ir_variable(this->callee->return_type, "__retval");
360 next_ir->insert_before(retval);
361 }
362
363 ir_function_cloning_visitor v = ir_function_cloning_visitor(retval);
364
365 /* Generate the declarations for the parameters to our inlined code,
366 * and set up the mapping of real function body variables to ours.
367 */
368 i = 0;
369 exec_list_iterator sig_param_iter = this->callee->parameters.iterator();
370 exec_list_iterator param_iter = this->actual_parameters.iterator();
371 for (i = 0; i < num_parameters; i++) {
372 const ir_variable *const sig_param = (ir_variable *) sig_param_iter.get();
373 ir_rvalue *param = (ir_rvalue *) param_iter.get();
374
375 /* Generate a new variable for the parameter. */
376 parameters[i] = sig_param->clone();
377 next_ir->insert_before(parameters[i]);
378
379 v.remap_variable(sig_param, parameters[i]);
380
381 /* Move the actual param into our param variable if it's an 'in' type. */
382 if (parameters[i]->mode == ir_var_in ||
383 parameters[i]->mode == ir_var_inout) {
384 ir_assignment *assign;
385
386 assign = new ir_assignment(new ir_dereference(parameters[i]),
387 param, NULL);
388 next_ir->insert_before(assign);
389 }
390
391 sig_param_iter.next();
392 param_iter.next();
393 }
394
395 /* Generate the inlined body of the function. */
396 foreach_iter(exec_list_iterator, iter, callee->body) {
397 ir_instruction *ir = (ir_instruction *)iter.get();
398
399 ir->accept(&v);
400 assert(v.result);
401 next_ir->insert_before(v.result);
402 }
403
404 /* Generate the declarations for the parameters to our inlined code,
405 * and set up the mapping of real function body variables to ours.
406 */
407 i = 0;
408 param_iter = this->actual_parameters.iterator();
409 for (i = 0; i < num_parameters; i++) {
410 ir_instruction *const param = (ir_instruction *) param_iter.get();
411
412 /* Move the actual param into our param variable if it's an 'in' type. */
413 if (parameters[i]->mode == ir_var_out ||
414 parameters[i]->mode == ir_var_inout) {
415 ir_assignment *assign;
416
417 assign = new ir_assignment(param->as_rvalue(),
418 new ir_dereference(parameters[i]),
419 NULL);
420 next_ir->insert_before(assign);
421 }
422
423 param_iter.next();
424 }
425
426 delete(parameters);
427
428 if (retval)
429 return new ir_dereference(retval);
430 else
431 return NULL;
432}
433
434void
435ir_function_inlining_visitor::visit(ir_variable *ir)
436{
437 (void) ir;
438}
439
440
441void
442ir_function_inlining_visitor::visit(ir_label *ir)
443{
444 ir->signature->accept(this);
445}
446
447void
448ir_function_inlining_visitor::visit(ir_loop *ir)
449{
450 do_function_inlining(&ir->body_instructions);
451}
452
453void
454ir_function_inlining_visitor::visit(ir_loop_jump *ir)
455{
456 (void) ir;
457}
458
459
460void
461ir_function_inlining_visitor::visit(ir_function_signature *ir)
462{
463 do_function_inlining(&ir->body);
464}
465
466
467void
468ir_function_inlining_visitor::visit(ir_function *ir)
469{
470 (void) ir;
471}
472
473void
474ir_function_inlining_visitor::visit(ir_expression *ir)
475{
476 unsigned int operand;
477
478 for (operand = 0; operand < ir->get_num_operands(); operand++) {
479 ir->operands[operand]->accept(this);
480 }
481}
482
483
484void
485ir_function_inlining_visitor::visit(ir_swizzle *ir)
486{
487 ir->val->accept(this);
488}
489
490
491void
492ir_function_inlining_visitor::visit(ir_dereference *ir)
493{
494 if (ir->mode == ir_dereference::ir_reference_array) {
495 ir->selector.array_index->accept(this);
496 }
497 ir->var->accept(this);
498}
499
500void
501ir_function_inlining_visitor::visit(ir_assignment *ir)
502{
503 ir->rhs->accept(this);
504}
505
506
507void
508ir_function_inlining_visitor::visit(ir_constant *ir)
509{
510 (void) ir;
511}
512
513
514void
515ir_function_inlining_visitor::visit(ir_call *ir)
516{
517 (void) ir;
518}
519
520
521void
522ir_function_inlining_visitor::visit(ir_return *ir)
523{
524 (void) ir;
525}
526
527
528void
529ir_function_inlining_visitor::visit(ir_if *ir)
530{
531 ir->condition->accept(this);
532
533 do_function_inlining(&ir->then_instructions);
534 do_function_inlining(&ir->else_instructions);
535}