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