• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  
42  static bool debug = false;
43  
44  namespace opt_array_splitting {
45  
46  class variable_entry : public exec_node
47  {
48  public:
variable_entry(ir_variable * var)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 */
81  using 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   */
88  class ir_array_reference_visitor : public ir_hierarchical_visitor {
89  public:
ir_array_reference_visitor(void)90     ir_array_reference_visitor(void)
91     {
92        this->mem_ctx = ralloc_context(NULL);
93        this->variable_list.make_empty();
94     }
95  
~ir_array_reference_visitor(void)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  
116  variable_entry *
get_variable_entry(ir_variable * var)117  ir_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  
146  ir_visitor_status
visit(ir_variable * ir)147  ir_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  
157  ir_visitor_status
visit(ir_dereference_variable * ir)158  ir_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  
173  ir_visitor_status
visit_enter(ir_dereference_array * ir)174  ir_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  
191  ir_visitor_status
visit_enter(ir_function_signature * ir)192  ir_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  
202  bool
get_split_list(exec_list * instructions,bool linked)203  ir_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   */
244  class ir_array_splitting_visitor : public ir_rvalue_visitor {
245  public:
ir_array_splitting_visitor(exec_list * vars)246     ir_array_splitting_visitor(exec_list *vars)
247     {
248        this->variable_list = vars;
249     }
250  
~ir_array_splitting_visitor()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  
264  variable_entry *
get_splitting_entry(ir_variable * var)265  ir_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  
279  void
split_deref(ir_dereference ** deref)280  ir_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  
316  void
handle_rvalue(ir_rvalue ** rvalue)317  ir_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  
331  ir_visitor_status
visit_leave(ir_assignment * ir)332  ir_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  
355  bool
optimize_split_arrays(exec_list * instructions,bool linked)356  optimize_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