/* * 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_hash_table_create(NULL, _mesa_hash_pointer, _mesa_key_pointer_equal); } 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; }