ir_array_refcount.cpp revision b8e80941
1/*
2 * Copyright © 2016 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_array_refcount.cpp
26 *
27 * Provides a visitor which produces a list of variables referenced.
28 */
29
30#include "ir.h"
31#include "ir_visitor.h"
32#include "ir_array_refcount.h"
33#include "compiler/glsl_types.h"
34#include "util/hash_table.h"
35
36ir_array_refcount_visitor::ir_array_refcount_visitor()
37   : last_array_deref(0), derefs(0), num_derefs(0), derefs_size(0)
38{
39   this->mem_ctx = ralloc_context(NULL);
40   this->ht = _mesa_pointer_hash_table_create(NULL);
41}
42
43static void
44free_entry(struct hash_entry *entry)
45{
46   ir_array_refcount_entry *ivre = (ir_array_refcount_entry *) entry->data;
47   delete ivre;
48}
49
50ir_array_refcount_visitor::~ir_array_refcount_visitor()
51{
52   ralloc_free(this->mem_ctx);
53   _mesa_hash_table_destroy(this->ht, free_entry);
54}
55
56ir_array_refcount_entry::ir_array_refcount_entry(ir_variable *var)
57   : var(var), is_referenced(false)
58{
59   num_bits = MAX2(1, var->type->arrays_of_arrays_size());
60   bits = new BITSET_WORD[BITSET_WORDS(num_bits)];
61   memset(bits, 0, BITSET_WORDS(num_bits) * sizeof(bits[0]));
62
63   /* Count the "depth" of the arrays-of-arrays. */
64   array_depth = 0;
65   for (const glsl_type *type = var->type;
66        type->is_array();
67        type = type->fields.array) {
68      array_depth++;
69   }
70}
71
72
73ir_array_refcount_entry::~ir_array_refcount_entry()
74{
75   delete [] bits;
76}
77
78
79void
80ir_array_refcount_entry::mark_array_elements_referenced(const array_deref_range *dr,
81                                                        unsigned count)
82{
83   if (count != array_depth)
84      return;
85
86   mark_array_elements_referenced(dr, count, 1, 0);
87}
88
89void
90ir_array_refcount_entry::mark_array_elements_referenced(const array_deref_range *dr,
91                                                        unsigned count,
92                                                        unsigned scale,
93                                                        unsigned linearized_index)
94{
95   /* Walk through the list of array dereferences in least- to
96    * most-significant order.  Along the way, accumulate the current
97    * linearized offset and the scale factor for each array-of-.
98    */
99   for (unsigned i = 0; i < count; i++) {
100      if (dr[i].index < dr[i].size) {
101         linearized_index += dr[i].index * scale;
102         scale *= dr[i].size;
103      } else {
104         /* For each element in the current array, update the count and
105          * offset, then recurse to process the remaining arrays.
106          *
107          * There is some inefficency here if the last element in the
108          * array_deref_range list specifies the entire array.  In that case,
109          * the loop will make recursive calls with count == 0.  In the call,
110          * all that will happen is the bit will be set.
111          */
112         for (unsigned j = 0; j < dr[i].size; j++) {
113            mark_array_elements_referenced(&dr[i + 1],
114                                           count - (i + 1),
115                                           scale * dr[i].size,
116                                           linearized_index + (j * scale));
117         }
118
119         return;
120      }
121   }
122
123   BITSET_SET(bits, linearized_index);
124}
125
126ir_array_refcount_entry *
127ir_array_refcount_visitor::get_variable_entry(ir_variable *var)
128{
129   assert(var);
130
131   struct hash_entry *e = _mesa_hash_table_search(this->ht, var);
132   if (e)
133      return (ir_array_refcount_entry *)e->data;
134
135   ir_array_refcount_entry *entry = new ir_array_refcount_entry(var);
136   _mesa_hash_table_insert(this->ht, var, entry);
137
138   return entry;
139}
140
141
142array_deref_range *
143ir_array_refcount_visitor::get_array_deref()
144{
145   if ((num_derefs + 1) * sizeof(array_deref_range) > derefs_size) {
146      void *ptr = reralloc_size(mem_ctx, derefs, derefs_size + 4096);
147
148      if (ptr == NULL)
149         return NULL;
150
151      derefs_size += 4096;
152      derefs = (array_deref_range *)ptr;
153   }
154
155   array_deref_range *d = &derefs[num_derefs];
156   num_derefs++;
157
158   return d;
159}
160
161ir_visitor_status
162ir_array_refcount_visitor::visit_enter(ir_dereference_array *ir)
163{
164   /* It could also be a vector or a matrix.  Individual elements of vectors
165    * are natrices are not tracked, so bail.
166    */
167   if (!ir->array->type->is_array())
168      return visit_continue;
169
170   /* If this array dereference is a child of an array dereference that was
171    * already visited, just continue on.  Otherwise, for an arrays-of-arrays
172    * dereference like x[1][2][3][4], we'd process the [1][2][3][4] sequence,
173    * the [1][2][3] sequence, the [1][2] sequence, and the [1] sequence.  This
174    * ensures that we only process the full sequence.
175    */
176   if (last_array_deref && last_array_deref->array == ir) {
177      last_array_deref = ir;
178      return visit_continue;
179   }
180
181   last_array_deref = ir;
182
183   num_derefs = 0;
184
185   ir_rvalue *rv = ir;
186   while (rv->ir_type == ir_type_dereference_array) {
187      ir_dereference_array *const deref = rv->as_dereference_array();
188
189      assert(deref != NULL);
190      assert(deref->array->type->is_array());
191
192      ir_rvalue *const array = deref->array;
193      const ir_constant *const idx = deref->array_index->as_constant();
194      array_deref_range *const dr = get_array_deref();
195
196      dr->size = array->type->array_size();
197
198      if (idx != NULL) {
199         dr->index = idx->get_int_component(0);
200      } else {
201         /* An unsized array can occur at the end of an SSBO.  We can't track
202          * accesses to such an array, so bail.
203          */
204         if (array->type->array_size() == 0)
205            return visit_continue;
206
207         dr->index = dr->size;
208      }
209
210      rv = array;
211   }
212
213   ir_dereference_variable *const var_deref = rv->as_dereference_variable();
214
215   /* If the array being dereferenced is not a variable, bail.  At the very
216    * least, ir_constant and ir_dereference_record are possible.
217    */
218   if (var_deref == NULL)
219      return visit_continue;
220
221   ir_array_refcount_entry *const entry =
222      this->get_variable_entry(var_deref->var);
223
224   if (entry == NULL)
225      return visit_stop;
226
227   entry->mark_array_elements_referenced(derefs, num_derefs);
228
229   return visit_continue;
230}
231
232
233ir_visitor_status
234ir_array_refcount_visitor::visit(ir_dereference_variable *ir)
235{
236   ir_variable *const var = ir->variable_referenced();
237   ir_array_refcount_entry *entry = this->get_variable_entry(var);
238
239   entry->is_referenced = true;
240
241   return visit_continue;
242}
243
244
245ir_visitor_status
246ir_array_refcount_visitor::visit_enter(ir_function_signature *ir)
247{
248   /* We don't want to descend into the function parameters and
249    * dead-code eliminate them, so just accept the body here.
250    */
251   visit_list_elements(this, &ir->body);
252   return visit_continue_with_parent;
253}
254