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_array_splitting.cpp
26 *
27 * If an array is always dereferenced with a constant index, then
28 * split it apart into its elements, making it more amenable to other
29 * optimization passes.
30 *
31 * This skips uniform/varying arrays, which would need careful
32 * handling due to their ir->location fields tying them to the GL API
33 * and other shader stages.
34 */
35
36#include "ir.h"
37#include "ir_visitor.h"
38#include "ir_rvalue_visitor.h"
39#include "ir_print_visitor.h"
40#include "glsl_types.h"
41
42static bool debug = false;
43
44namespace opt_array_splitting {
45
46class variable_entry : public exec_node
47{
48public:
49   variable_entry(ir_variable *var)
50   {
51      this->var = var;
52      this->split = true;
53      this->declaration = false;
54      this->components = NULL;
55      this->mem_ctx = NULL;
56      if (var->type->is_array())
57	 this->size = var->type->length;
58      else
59	 this->size = var->type->matrix_columns;
60   }
61
62   ir_variable *var; /* The key: the variable's pointer. */
63   unsigned size; /* array length or matrix columns */
64
65   /** Whether this array should be split or not. */
66   bool split;
67
68   /* If the variable had a decl we can work with in the instruction
69    * stream.  We can't do splitting on function arguments, which
70    * don't get this variable set.
71    */
72   bool declaration;
73
74   ir_variable **components;
75
76   /** ralloc_parent(this->var) -- the shader's talloc context. */
77   void *mem_ctx;
78};
79
80} /* namespace */
81using namespace opt_array_splitting;
82
83/**
84 * This class does a walk over the tree, coming up with the set of
85 * variables that could be split by looking to see if they are arrays
86 * that are only ever constant-index dereferenced.
87 */
88class ir_array_reference_visitor : public ir_hierarchical_visitor {
89public:
90   ir_array_reference_visitor(void)
91   {
92      this->mem_ctx = ralloc_context(NULL);
93      this->variable_list.make_empty();
94   }
95
96   ~ir_array_reference_visitor(void)
97   {
98      ralloc_free(mem_ctx);
99   }
100
101   bool get_split_list(exec_list *instructions, bool linked);
102
103   virtual ir_visitor_status visit(ir_variable *);
104   virtual ir_visitor_status visit(ir_dereference_variable *);
105   virtual ir_visitor_status visit_enter(ir_dereference_array *);
106   virtual ir_visitor_status visit_enter(ir_function_signature *);
107
108   variable_entry *get_variable_entry(ir_variable *var);
109
110   /* List of variable_entry */
111   exec_list variable_list;
112
113   void *mem_ctx;
114};
115
116variable_entry *
117ir_array_reference_visitor::get_variable_entry(ir_variable *var)
118{
119   assert(var);
120
121   if (var->mode != ir_var_auto &&
122       var->mode != ir_var_temporary)
123      return NULL;
124
125   if (!(var->type->is_array() || var->type->is_matrix()))
126      return NULL;
127
128   /* If the array hasn't been sized yet, we can't split it.  After
129    * linking, this should be resolved.
130    */
131   if (var->type->is_array() && var->type->length == 0)
132      return NULL;
133
134   foreach_iter(exec_list_iterator, iter, this->variable_list) {
135      variable_entry *entry = (variable_entry *)iter.get();
136      if (entry->var == var)
137	 return entry;
138   }
139
140   variable_entry *entry = new(mem_ctx) variable_entry(var);
141   this->variable_list.push_tail(entry);
142   return entry;
143}
144
145
146ir_visitor_status
147ir_array_reference_visitor::visit(ir_variable *ir)
148{
149   variable_entry *entry = this->get_variable_entry(ir);
150
151   if (entry)
152      entry->declaration = true;
153
154   return visit_continue;
155}
156
157ir_visitor_status
158ir_array_reference_visitor::visit(ir_dereference_variable *ir)
159{
160   variable_entry *entry = this->get_variable_entry(ir->var);
161
162   /* If we made it to here without seeing an ir_dereference_array,
163    * then the dereference of this array didn't have a constant index
164    * (see the visit_continue_with_parent below), so we can't split
165    * the variable.
166    */
167   if (entry)
168      entry->split = false;
169
170   return visit_continue;
171}
172
173ir_visitor_status
174ir_array_reference_visitor::visit_enter(ir_dereference_array *ir)
175{
176   ir_dereference_variable *deref = ir->array->as_dereference_variable();
177   if (!deref)
178      return visit_continue;
179
180   variable_entry *entry = this->get_variable_entry(deref->var);
181
182   /* If the access to the array has a variable index, we wouldn't
183    * know which split variable this dereference should go to.
184    */
185   if (entry && !ir->array_index->as_constant())
186      entry->split = false;
187
188   return visit_continue_with_parent;
189}
190
191ir_visitor_status
192ir_array_reference_visitor::visit_enter(ir_function_signature *ir)
193{
194   /* We don't have logic for array-splitting function arguments,
195    * so just look at the body instructions and not the parameter
196    * declarations.
197    */
198   visit_list_elements(this, &ir->body);
199   return visit_continue_with_parent;
200}
201
202bool
203ir_array_reference_visitor::get_split_list(exec_list *instructions,
204					   bool linked)
205{
206   visit_list_elements(this, instructions);
207
208   /* If the shaders aren't linked yet, we can't mess with global
209    * declarations, which need to be matched by name across shaders.
210    */
211   if (!linked) {
212      foreach_list(node, instructions) {
213	 ir_variable *var = ((ir_instruction *)node)->as_variable();
214	 if (var) {
215	    variable_entry *entry = get_variable_entry(var);
216	    if (entry)
217	       entry->remove();
218	 }
219      }
220   }
221
222   /* Trim out variables we found that we can't split. */
223   foreach_iter(exec_list_iterator, iter, variable_list) {
224      variable_entry *entry = (variable_entry *)iter.get();
225
226      if (debug) {
227	 printf("array %s@%p: decl %d, split %d\n",
228		entry->var->name, (void *) entry->var, entry->declaration,
229		entry->split);
230      }
231
232      if (!(entry->declaration && entry->split)) {
233	 entry->remove();
234      }
235   }
236
237   return !variable_list.is_empty();
238}
239
240/**
241 * This class rewrites the dereferences of arrays that have been split
242 * to use the newly created ir_variables for each component.
243 */
244class ir_array_splitting_visitor : public ir_rvalue_visitor {
245public:
246   ir_array_splitting_visitor(exec_list *vars)
247   {
248      this->variable_list = vars;
249   }
250
251   virtual ~ir_array_splitting_visitor()
252   {
253   }
254
255   virtual ir_visitor_status visit_leave(ir_assignment *);
256
257   void split_deref(ir_dereference **deref);
258   void handle_rvalue(ir_rvalue **rvalue);
259   variable_entry *get_splitting_entry(ir_variable *var);
260
261   exec_list *variable_list;
262};
263
264variable_entry *
265ir_array_splitting_visitor::get_splitting_entry(ir_variable *var)
266{
267   assert(var);
268
269   foreach_iter(exec_list_iterator, iter, *this->variable_list) {
270      variable_entry *entry = (variable_entry *)iter.get();
271      if (entry->var == var) {
272	 return entry;
273      }
274   }
275
276   return NULL;
277}
278
279void
280ir_array_splitting_visitor::split_deref(ir_dereference **deref)
281{
282   ir_dereference_array *deref_array = (*deref)->as_dereference_array();
283   if (!deref_array)
284      return;
285
286   ir_dereference_variable *deref_var = deref_array->array->as_dereference_variable();
287   if (!deref_var)
288      return;
289   ir_variable *var = deref_var->var;
290
291   variable_entry *entry = get_splitting_entry(var);
292   if (!entry)
293      return;
294
295   ir_constant *constant = deref_array->array_index->as_constant();
296   assert(constant);
297
298   if (constant->value.i[0] < (int)entry->size) {
299      *deref = new(entry->mem_ctx)
300	 ir_dereference_variable(entry->components[constant->value.i[0]]);
301   } else {
302      /* There was a constant array access beyond the end of the
303       * array.  This might have happened due to constant folding
304       * after the initial parse.  This produces an undefined value,
305       * but shouldn't crash.  Just give them an uninitialized
306       * variable.
307       */
308      ir_variable *temp = new(entry->mem_ctx) ir_variable(deref_array->type,
309							  "undef",
310							  ir_var_temporary);
311      entry->components[0]->insert_before(temp);
312      *deref = new(entry->mem_ctx) ir_dereference_variable(temp);
313   }
314}
315
316void
317ir_array_splitting_visitor::handle_rvalue(ir_rvalue **rvalue)
318{
319   if (!*rvalue)
320      return;
321
322   ir_dereference *deref = (*rvalue)->as_dereference();
323
324   if (!deref)
325      return;
326
327   split_deref(&deref);
328   *rvalue = deref;
329}
330
331ir_visitor_status
332ir_array_splitting_visitor::visit_leave(ir_assignment *ir)
333{
334   /* The normal rvalue visitor skips the LHS of assignments, but we
335    * need to process those just the same.
336    */
337   ir_rvalue *lhs = ir->lhs;
338
339   handle_rvalue(&lhs);
340   ir->lhs = lhs->as_dereference();
341
342   ir->lhs->accept(this);
343
344   handle_rvalue(&ir->rhs);
345   ir->rhs->accept(this);
346
347   if (ir->condition) {
348      handle_rvalue(&ir->condition);
349      ir->condition->accept(this);
350   }
351
352   return visit_continue;
353}
354
355bool
356optimize_split_arrays(exec_list *instructions, bool linked)
357{
358   ir_array_reference_visitor refs;
359   if (!refs.get_split_list(instructions, linked))
360      return false;
361
362   void *mem_ctx = ralloc_context(NULL);
363
364   /* Replace the decls of the arrays to be split with their split
365    * components.
366    */
367   foreach_iter(exec_list_iterator, iter, refs.variable_list) {
368      variable_entry *entry = (variable_entry *)iter.get();
369      const struct glsl_type *type = entry->var->type;
370      const struct glsl_type *subtype;
371
372      if (type->is_matrix())
373	 subtype = type->column_type();
374      else
375	 subtype = type->fields.array;
376
377      entry->mem_ctx = ralloc_parent(entry->var);
378
379      entry->components = ralloc_array(mem_ctx,
380				       ir_variable *,
381				       entry->size);
382
383      for (unsigned int i = 0; i < entry->size; i++) {
384	 const char *name = ralloc_asprintf(mem_ctx, "%s_%d",
385					    entry->var->name, i);
386
387	 entry->components[i] =
388	    new(entry->mem_ctx) ir_variable(subtype, name, ir_var_temporary);
389	 entry->var->insert_before(entry->components[i]);
390      }
391
392      entry->var->remove();
393   }
394
395   ir_array_splitting_visitor split(&refs.variable_list);
396   visit_list_elements(&split, instructions);
397
398   if (debug)
399      _mesa_print_ir(instructions, NULL);
400
401   ralloc_free(mem_ctx);
402
403   return true;
404
405}
406