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