| /* |
| * Copyright © 2016 Intel Corporation |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice (including the next |
| * paragraph) shall be included in all copies or substantial portions of the |
| * Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
| * DEALINGS IN THE SOFTWARE. |
| */ |
| |
| /** |
| * \file ir_array_refcount.cpp |
| * |
| * Provides a visitor which produces a list of variables referenced. |
| */ |
| |
| #include "ir.h" |
| #include "ir_visitor.h" |
| #include "ir_array_refcount.h" |
| #include "compiler/glsl_types.h" |
| #include "util/hash_table.h" |
| |
| ir_array_refcount_visitor::ir_array_refcount_visitor() |
| : last_array_deref(0), derefs(0), num_derefs(0), derefs_size(0) |
| { |
| this->mem_ctx = ralloc_context(NULL); |
| this->ht = _mesa_pointer_hash_table_create(NULL); |
| } |
| |
| static void |
| free_entry(struct hash_entry *entry) |
| { |
| ir_array_refcount_entry *ivre = (ir_array_refcount_entry *) entry->data; |
| delete ivre; |
| } |
| |
| ir_array_refcount_visitor::~ir_array_refcount_visitor() |
| { |
| ralloc_free(this->mem_ctx); |
| _mesa_hash_table_destroy(this->ht, free_entry); |
| } |
| |
| ir_array_refcount_entry::ir_array_refcount_entry(ir_variable *var) |
| : var(var), is_referenced(false) |
| { |
| num_bits = MAX2(1, var->type->arrays_of_arrays_size()); |
| bits = new BITSET_WORD[BITSET_WORDS(num_bits)]; |
| memset(bits, 0, BITSET_WORDS(num_bits) * sizeof(bits[0])); |
| |
| /* Count the "depth" of the arrays-of-arrays. */ |
| array_depth = 0; |
| for (const glsl_type *type = var->type; |
| type->is_array(); |
| type = type->fields.array) { |
| array_depth++; |
| } |
| } |
| |
| |
| ir_array_refcount_entry::~ir_array_refcount_entry() |
| { |
| delete [] bits; |
| } |
| |
| |
| void |
| ir_array_refcount_entry::mark_array_elements_referenced(const array_deref_range *dr, |
| unsigned count) |
| { |
| if (count != array_depth) |
| return; |
| |
| mark_array_elements_referenced(dr, count, 1, 0); |
| } |
| |
| void |
| ir_array_refcount_entry::mark_array_elements_referenced(const array_deref_range *dr, |
| unsigned count, |
| unsigned scale, |
| unsigned linearized_index) |
| { |
| /* Walk through the list of array dereferences in least- to |
| * most-significant order. Along the way, accumulate the current |
| * linearized offset and the scale factor for each array-of-. |
| */ |
| for (unsigned i = 0; i < count; i++) { |
| if (dr[i].index < dr[i].size) { |
| linearized_index += dr[i].index * scale; |
| scale *= dr[i].size; |
| } else { |
| /* For each element in the current array, update the count and |
| * offset, then recurse to process the remaining arrays. |
| * |
| * There is some inefficency here if the last element in the |
| * array_deref_range list specifies the entire array. In that case, |
| * the loop will make recursive calls with count == 0. In the call, |
| * all that will happen is the bit will be set. |
| */ |
| for (unsigned j = 0; j < dr[i].size; j++) { |
| mark_array_elements_referenced(&dr[i + 1], |
| count - (i + 1), |
| scale * dr[i].size, |
| linearized_index + (j * scale)); |
| } |
| |
| return; |
| } |
| } |
| |
| BITSET_SET(bits, linearized_index); |
| } |
| |
| ir_array_refcount_entry * |
| ir_array_refcount_visitor::get_variable_entry(ir_variable *var) |
| { |
| assert(var); |
| |
| struct hash_entry *e = _mesa_hash_table_search(this->ht, var); |
| if (e) |
| return (ir_array_refcount_entry *)e->data; |
| |
| ir_array_refcount_entry *entry = new ir_array_refcount_entry(var); |
| _mesa_hash_table_insert(this->ht, var, entry); |
| |
| return entry; |
| } |
| |
| |
| array_deref_range * |
| ir_array_refcount_visitor::get_array_deref() |
| { |
| if ((num_derefs + 1) * sizeof(array_deref_range) > derefs_size) { |
| void *ptr = reralloc_size(mem_ctx, derefs, derefs_size + 4096); |
| |
| if (ptr == NULL) |
| return NULL; |
| |
| derefs_size += 4096; |
| derefs = (array_deref_range *)ptr; |
| } |
| |
| array_deref_range *d = &derefs[num_derefs]; |
| num_derefs++; |
| |
| return d; |
| } |
| |
| ir_visitor_status |
| ir_array_refcount_visitor::visit_enter(ir_dereference_array *ir) |
| { |
| /* It could also be a vector or a matrix. Individual elements of vectors |
| * are natrices are not tracked, so bail. |
| */ |
| if (!ir->array->type->is_array()) |
| return visit_continue; |
| |
| /* If this array dereference is a child of an array dereference that was |
| * already visited, just continue on. Otherwise, for an arrays-of-arrays |
| * dereference like x[1][2][3][4], we'd process the [1][2][3][4] sequence, |
| * the [1][2][3] sequence, the [1][2] sequence, and the [1] sequence. This |
| * ensures that we only process the full sequence. |
| */ |
| if (last_array_deref && last_array_deref->array == ir) { |
| last_array_deref = ir; |
| return visit_continue; |
| } |
| |
| last_array_deref = ir; |
| |
| num_derefs = 0; |
| |
| ir_rvalue *rv = ir; |
| while (rv->ir_type == ir_type_dereference_array) { |
| ir_dereference_array *const deref = rv->as_dereference_array(); |
| |
| assert(deref != NULL); |
| assert(deref->array->type->is_array()); |
| |
| ir_rvalue *const array = deref->array; |
| const ir_constant *const idx = deref->array_index->as_constant(); |
| array_deref_range *const dr = get_array_deref(); |
| |
| dr->size = array->type->array_size(); |
| |
| if (idx != NULL) { |
| dr->index = idx->get_int_component(0); |
| } else { |
| /* An unsized array can occur at the end of an SSBO. We can't track |
| * accesses to such an array, so bail. |
| */ |
| if (array->type->array_size() == 0) |
| return visit_continue; |
| |
| dr->index = dr->size; |
| } |
| |
| rv = array; |
| } |
| |
| ir_dereference_variable *const var_deref = rv->as_dereference_variable(); |
| |
| /* If the array being dereferenced is not a variable, bail. At the very |
| * least, ir_constant and ir_dereference_record are possible. |
| */ |
| if (var_deref == NULL) |
| return visit_continue; |
| |
| ir_array_refcount_entry *const entry = |
| this->get_variable_entry(var_deref->var); |
| |
| if (entry == NULL) |
| return visit_stop; |
| |
| entry->mark_array_elements_referenced(derefs, num_derefs); |
| |
| return visit_continue; |
| } |
| |
| |
| ir_visitor_status |
| ir_array_refcount_visitor::visit(ir_dereference_variable *ir) |
| { |
| ir_variable *const var = ir->variable_referenced(); |
| ir_array_refcount_entry *entry = this->get_variable_entry(var); |
| |
| entry->is_referenced = true; |
| |
| return visit_continue; |
| } |
| |
| |
| ir_visitor_status |
| ir_array_refcount_visitor::visit_enter(ir_function_signature *ir) |
| { |
| /* We don't want to descend into the function parameters and |
| * dead-code eliminate them, so just accept the body here. |
| */ |
| visit_list_elements(this, &ir->body); |
| return visit_continue_with_parent; |
| } |