Subversion Repositories Kolibri OS

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
5564 serge 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 "glsl_types.h"
40
 
41
static bool debug = false;
42
 
43
namespace {
44
 
45
namespace opt_array_splitting {
46
 
47
class variable_entry : public exec_node
48
{
49
public:
50
   variable_entry(ir_variable *var)
51
   {
52
      this->var = var;
53
      this->split = true;
54
      this->declaration = false;
55
      this->components = NULL;
56
      this->mem_ctx = NULL;
57
      if (var->type->is_array())
58
	 this->size = var->type->length;
59
      else
60
	 this->size = var->type->matrix_columns;
61
   }
62
 
63
   ir_variable *var; /* The key: the variable's pointer. */
64
   unsigned size; /* array length or matrix columns */
65
 
66
   /** Whether this array should be split or not. */
67
   bool split;
68
 
69
   /* If the variable had a decl we can work with in the instruction
70
    * stream.  We can't do splitting on function arguments, which
71
    * don't get this variable set.
72
    */
73
   bool declaration;
74
 
75
   ir_variable **components;
76
 
77
   /** ralloc_parent(this->var) -- the shader's talloc context. */
78
   void *mem_ctx;
79
};
80
 
81
} /* namespace */
82
 
83
using namespace opt_array_splitting;
84
 
85
/**
86
 * This class does a walk over the tree, coming up with the set of
87
 * variables that could be split by looking to see if they are arrays
88
 * that are only ever constant-index dereferenced.
89
 */
90
class ir_array_reference_visitor : public ir_hierarchical_visitor {
91
public:
92
   ir_array_reference_visitor(void)
93
   {
94
      this->mem_ctx = ralloc_context(NULL);
95
      this->variable_list.make_empty();
96
   }
97
 
98
   ~ir_array_reference_visitor(void)
99
   {
100
      ralloc_free(mem_ctx);
101
   }
102
 
103
   bool get_split_list(exec_list *instructions, bool linked);
104
 
105
   virtual ir_visitor_status visit(ir_variable *);
106
   virtual ir_visitor_status visit(ir_dereference_variable *);
107
   virtual ir_visitor_status visit_enter(ir_dereference_array *);
108
   virtual ir_visitor_status visit_enter(ir_function_signature *);
109
 
110
   variable_entry *get_variable_entry(ir_variable *var);
111
 
112
   /* List of variable_entry */
113
   exec_list variable_list;
114
 
115
   void *mem_ctx;
116
};
117
 
118
} /* namespace */
119
 
120
variable_entry *
121
ir_array_reference_visitor::get_variable_entry(ir_variable *var)
122
{
123
   assert(var);
124
 
125
   if (var->data.mode != ir_var_auto &&
126
       var->data.mode != ir_var_temporary)
127
      return NULL;
128
 
129
   if (!(var->type->is_array() || var->type->is_matrix()))
130
      return NULL;
131
 
132
   /* If the array hasn't been sized yet, we can't split it.  After
133
    * linking, this should be resolved.
134
    */
135
   if (var->type->is_unsized_array())
136
      return NULL;
137
 
138
   foreach_in_list(variable_entry, entry, &this->variable_list) {
139
      if (entry->var == var)
140
	 return entry;
141
   }
142
 
143
   variable_entry *entry = new(mem_ctx) variable_entry(var);
144
   this->variable_list.push_tail(entry);
145
   return entry;
146
}
147
 
148
 
149
ir_visitor_status
150
ir_array_reference_visitor::visit(ir_variable *ir)
151
{
152
   variable_entry *entry = this->get_variable_entry(ir);
153
 
154
   if (entry)
155
      entry->declaration = true;
156
 
157
   return visit_continue;
158
}
159
 
160
ir_visitor_status
161
ir_array_reference_visitor::visit(ir_dereference_variable *ir)
162
{
163
   variable_entry *entry = this->get_variable_entry(ir->var);
164
 
165
   /* If we made it to here without seeing an ir_dereference_array,
166
    * then the dereference of this array didn't have a constant index
167
    * (see the visit_continue_with_parent below), so we can't split
168
    * the variable.
169
    */
170
   if (entry)
171
      entry->split = false;
172
 
173
   return visit_continue;
174
}
175
 
176
ir_visitor_status
177
ir_array_reference_visitor::visit_enter(ir_dereference_array *ir)
178
{
179
   ir_dereference_variable *deref = ir->array->as_dereference_variable();
180
   if (!deref)
181
      return visit_continue;
182
 
183
   variable_entry *entry = this->get_variable_entry(deref->var);
184
 
185
   /* If the access to the array has a variable index, we wouldn't
186
    * know which split variable this dereference should go to.
187
    */
188
   if (entry && !ir->array_index->as_constant())
189
      entry->split = false;
190
 
191
   return visit_continue_with_parent;
192
}
193
 
194
ir_visitor_status
195
ir_array_reference_visitor::visit_enter(ir_function_signature *ir)
196
{
197
   /* We don't have logic for array-splitting function arguments,
198
    * so just look at the body instructions and not the parameter
199
    * declarations.
200
    */
201
   visit_list_elements(this, &ir->body);
202
   return visit_continue_with_parent;
203
}
204
 
205
bool
206
ir_array_reference_visitor::get_split_list(exec_list *instructions,
207
					   bool linked)
208
{
209
   visit_list_elements(this, instructions);
210
 
211
   /* If the shaders aren't linked yet, we can't mess with global
212
    * declarations, which need to be matched by name across shaders.
213
    */
214
   if (!linked) {
215
      foreach_in_list(ir_instruction, node, instructions) {
216
	 ir_variable *var = node->as_variable();
217
	 if (var) {
218
	    variable_entry *entry = get_variable_entry(var);
219
	    if (entry)
220
	       entry->remove();
221
	 }
222
      }
223
   }
224
 
225
   /* Trim out variables we found that we can't split. */
226
   foreach_in_list_safe(variable_entry, entry, &variable_list) {
227
      if (debug) {
228
	 printf("array %s@%p: decl %d, split %d\n",
229
		entry->var->name, (void *) entry->var, entry->declaration,
230
		entry->split);
231
      }
232
 
233
      if (!(entry->declaration && entry->split)) {
234
	 entry->remove();
235
      }
236
   }
237
 
238
   return !variable_list.is_empty();
239
}
240
 
241
/**
242
 * This class rewrites the dereferences of arrays that have been split
243
 * to use the newly created ir_variables for each component.
244
 */
245
class ir_array_splitting_visitor : public ir_rvalue_visitor {
246
public:
247
   ir_array_splitting_visitor(exec_list *vars)
248
   {
249
      this->variable_list = vars;
250
   }
251
 
252
   virtual ~ir_array_splitting_visitor()
253
   {
254
   }
255
 
256
   virtual ir_visitor_status visit_leave(ir_assignment *);
257
 
258
   void split_deref(ir_dereference **deref);
259
   void handle_rvalue(ir_rvalue **rvalue);
260
   variable_entry *get_splitting_entry(ir_variable *var);
261
 
262
   exec_list *variable_list;
263
};
264
 
265
variable_entry *
266
ir_array_splitting_visitor::get_splitting_entry(ir_variable *var)
267
{
268
   assert(var);
269
 
270
   foreach_in_list(variable_entry, entry, this->variable_list) {
271
      if (entry->var == var) {
272
	 return entry;
273
      }
274
   }
275
 
276
   return NULL;
277
}
278
 
279
void
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] >= 0 && 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
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
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
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_in_list(variable_entry, entry, &refs.variable_list) {
368
      const struct glsl_type *type = entry->var->type;
369
      const struct glsl_type *subtype;
370
 
371
      if (type->is_matrix())
372
	 subtype = type->column_type();
373
      else
374
	 subtype = type->fields.array;
375
 
376
      entry->mem_ctx = ralloc_parent(entry->var);
377
 
378
      entry->components = ralloc_array(mem_ctx,
379
				       ir_variable *,
380
				       entry->size);
381
 
382
      for (unsigned int i = 0; i < entry->size; i++) {
383
	 const char *name = ralloc_asprintf(mem_ctx, "%s_%d",
384
					    entry->var->name, i);
385
 
386
	 entry->components[i] =
387
	    new(entry->mem_ctx) ir_variable(subtype, name, ir_var_temporary);
388
	 entry->var->insert_before(entry->components[i]);
389
      }
390
 
391
      entry->var->remove();
392
   }
393
 
394
   ir_array_splitting_visitor split(&refs.variable_list);
395
   visit_list_elements(&split, instructions);
396
 
397
   if (debug)
398
      _mesa_print_ir(stdout, instructions, NULL);
399
 
400
   ralloc_free(mem_ctx);
401
 
402
   return true;
403
 
404
}