101e04c3fSmrg/* 201e04c3fSmrg * Copyright © 2010 Intel Corporation 301e04c3fSmrg * 401e04c3fSmrg * Permission is hereby granted, free of charge, to any person obtaining a 501e04c3fSmrg * constant of this software and associated documentation files (the "Software"), 601e04c3fSmrg * to deal in the Software without restriction, including without limitation 701e04c3fSmrg * the rights to use, constant, modify, merge, publish, distribute, sublicense, 801e04c3fSmrg * and/or sell copies of the Software, and to permit persons to whom the 901e04c3fSmrg * Software is furnished to do so, subject to the following conditions: 1001e04c3fSmrg * 1101e04c3fSmrg * The above constantright notice and this permission notice (including the next 1201e04c3fSmrg * paragraph) shall be included in all copies or substantial portions of the 1301e04c3fSmrg * Software. 1401e04c3fSmrg * 1501e04c3fSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1601e04c3fSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1701e04c3fSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 1801e04c3fSmrg * THE AUTHORS OR CONSTANTRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 1901e04c3fSmrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 2001e04c3fSmrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 2101e04c3fSmrg * DEALINGS IN THE SOFTWARE. 2201e04c3fSmrg */ 2301e04c3fSmrg 2401e04c3fSmrg/** 2501e04c3fSmrg * \file opt_constant_propagation.cpp 2601e04c3fSmrg * 2701e04c3fSmrg * Tracks assignments of constants to channels of variables, and 2801e04c3fSmrg * usage of those constant channels with direct usage of the constants. 2901e04c3fSmrg * 3001e04c3fSmrg * This can lead to constant folding and algebraic optimizations in 3101e04c3fSmrg * those later expressions, while causing no increase in instruction 3201e04c3fSmrg * count (due to constants being generally free to load from a 3301e04c3fSmrg * constant push buffer or as instruction immediate values) and 3401e04c3fSmrg * possibly reducing register pressure. 3501e04c3fSmrg */ 3601e04c3fSmrg 3701e04c3fSmrg#include "ir.h" 3801e04c3fSmrg#include "ir_visitor.h" 3901e04c3fSmrg#include "ir_rvalue_visitor.h" 4001e04c3fSmrg#include "ir_basic_block.h" 4101e04c3fSmrg#include "ir_optimization.h" 4201e04c3fSmrg#include "compiler/glsl_types.h" 4301e04c3fSmrg#include "util/hash_table.h" 4401e04c3fSmrg 4501e04c3fSmrgnamespace { 4601e04c3fSmrg 4701e04c3fSmrgclass acp_entry : public exec_node 4801e04c3fSmrg{ 4901e04c3fSmrgpublic: 5001e04c3fSmrg /* override operator new from exec_node */ 5101e04c3fSmrg DECLARE_LINEAR_ZALLOC_CXX_OPERATORS(acp_entry) 5201e04c3fSmrg 5301e04c3fSmrg acp_entry(ir_variable *var, unsigned write_mask, ir_constant *constant) 5401e04c3fSmrg { 5501e04c3fSmrg assert(var); 5601e04c3fSmrg assert(constant); 5701e04c3fSmrg this->var = var; 5801e04c3fSmrg this->write_mask = write_mask; 5901e04c3fSmrg this->constant = constant; 6001e04c3fSmrg this->initial_values = write_mask; 6101e04c3fSmrg } 6201e04c3fSmrg 6301e04c3fSmrg acp_entry(const acp_entry *src) 6401e04c3fSmrg { 6501e04c3fSmrg this->var = src->var; 6601e04c3fSmrg this->write_mask = src->write_mask; 6701e04c3fSmrg this->constant = src->constant; 6801e04c3fSmrg this->initial_values = src->initial_values; 6901e04c3fSmrg } 7001e04c3fSmrg 7101e04c3fSmrg ir_variable *var; 7201e04c3fSmrg ir_constant *constant; 7301e04c3fSmrg unsigned write_mask; 7401e04c3fSmrg 7501e04c3fSmrg /** Mask of values initially available in the constant. */ 7601e04c3fSmrg unsigned initial_values; 7701e04c3fSmrg}; 7801e04c3fSmrg 7901e04c3fSmrg 8001e04c3fSmrgclass ir_constant_propagation_visitor : public ir_rvalue_visitor { 8101e04c3fSmrgpublic: 8201e04c3fSmrg ir_constant_propagation_visitor() 8301e04c3fSmrg { 8401e04c3fSmrg progress = false; 8501e04c3fSmrg killed_all = false; 8601e04c3fSmrg mem_ctx = ralloc_context(0); 8701e04c3fSmrg this->lin_ctx = linear_alloc_parent(this->mem_ctx, 0); 8801e04c3fSmrg this->acp = new(mem_ctx) exec_list; 897e102996Smaya this->kills = _mesa_pointer_hash_table_create(mem_ctx); 9001e04c3fSmrg } 9101e04c3fSmrg ~ir_constant_propagation_visitor() 9201e04c3fSmrg { 9301e04c3fSmrg ralloc_free(mem_ctx); 9401e04c3fSmrg } 9501e04c3fSmrg 9601e04c3fSmrg virtual ir_visitor_status visit_enter(class ir_loop *); 9701e04c3fSmrg virtual ir_visitor_status visit_enter(class ir_function_signature *); 9801e04c3fSmrg virtual ir_visitor_status visit_enter(class ir_function *); 9901e04c3fSmrg virtual ir_visitor_status visit_leave(class ir_assignment *); 10001e04c3fSmrg virtual ir_visitor_status visit_enter(class ir_call *); 10101e04c3fSmrg virtual ir_visitor_status visit_enter(class ir_if *); 10201e04c3fSmrg 10301e04c3fSmrg void add_constant(ir_assignment *ir); 10401e04c3fSmrg void constant_folding(ir_rvalue **rvalue); 10501e04c3fSmrg void constant_propagation(ir_rvalue **rvalue); 10601e04c3fSmrg void kill(ir_variable *ir, unsigned write_mask); 10701e04c3fSmrg void handle_if_block(exec_list *instructions, hash_table *kills, bool *killed_all); 10801e04c3fSmrg void handle_loop(class ir_loop *, bool keep_acp); 10901e04c3fSmrg void handle_rvalue(ir_rvalue **rvalue); 11001e04c3fSmrg 11101e04c3fSmrg /** List of acp_entry: The available constants to propagate */ 11201e04c3fSmrg exec_list *acp; 11301e04c3fSmrg 11401e04c3fSmrg /** 11501e04c3fSmrg * Hash table of killed entries: maps variables to the mask of killed channels. 11601e04c3fSmrg */ 11701e04c3fSmrg hash_table *kills; 11801e04c3fSmrg 11901e04c3fSmrg bool progress; 12001e04c3fSmrg 12101e04c3fSmrg bool killed_all; 12201e04c3fSmrg 12301e04c3fSmrg void *mem_ctx; 12401e04c3fSmrg void *lin_ctx; 12501e04c3fSmrg}; 12601e04c3fSmrg 12701e04c3fSmrg 12801e04c3fSmrgvoid 12901e04c3fSmrgir_constant_propagation_visitor::constant_folding(ir_rvalue **rvalue) 13001e04c3fSmrg{ 13101e04c3fSmrg if (this->in_assignee || *rvalue == NULL) 13201e04c3fSmrg return; 13301e04c3fSmrg 13401e04c3fSmrg if (ir_constant_fold(rvalue)) 13501e04c3fSmrg this->progress = true; 13601e04c3fSmrg 13701e04c3fSmrg ir_dereference_variable *var_ref = (*rvalue)->as_dereference_variable(); 13801e04c3fSmrg if (var_ref && !var_ref->type->is_array()) { 13901e04c3fSmrg ir_constant *constant = 14001e04c3fSmrg var_ref->constant_expression_value(ralloc_parent(var_ref)); 14101e04c3fSmrg if (constant) { 14201e04c3fSmrg *rvalue = constant; 14301e04c3fSmrg this->progress = true; 14401e04c3fSmrg } 14501e04c3fSmrg } 14601e04c3fSmrg} 14701e04c3fSmrg 14801e04c3fSmrgvoid 14901e04c3fSmrgir_constant_propagation_visitor::constant_propagation(ir_rvalue **rvalue) { 15001e04c3fSmrg 15101e04c3fSmrg if (this->in_assignee || !*rvalue) 15201e04c3fSmrg return; 15301e04c3fSmrg 15401e04c3fSmrg const glsl_type *type = (*rvalue)->type; 15501e04c3fSmrg if (!type->is_scalar() && !type->is_vector()) 15601e04c3fSmrg return; 15701e04c3fSmrg 15801e04c3fSmrg ir_swizzle *swiz = NULL; 15901e04c3fSmrg ir_dereference_variable *deref = (*rvalue)->as_dereference_variable(); 16001e04c3fSmrg if (!deref) { 16101e04c3fSmrg swiz = (*rvalue)->as_swizzle(); 16201e04c3fSmrg if (!swiz) 16301e04c3fSmrg return; 16401e04c3fSmrg 16501e04c3fSmrg deref = swiz->val->as_dereference_variable(); 16601e04c3fSmrg if (!deref) 16701e04c3fSmrg return; 16801e04c3fSmrg } 16901e04c3fSmrg 17001e04c3fSmrg ir_constant_data data; 17101e04c3fSmrg memset(&data, 0, sizeof(data)); 17201e04c3fSmrg 17301e04c3fSmrg for (unsigned int i = 0; i < type->components(); i++) { 17401e04c3fSmrg int channel; 17501e04c3fSmrg acp_entry *found = NULL; 17601e04c3fSmrg 17701e04c3fSmrg if (swiz) { 17801e04c3fSmrg switch (i) { 17901e04c3fSmrg case 0: channel = swiz->mask.x; break; 18001e04c3fSmrg case 1: channel = swiz->mask.y; break; 18101e04c3fSmrg case 2: channel = swiz->mask.z; break; 18201e04c3fSmrg case 3: channel = swiz->mask.w; break; 18301e04c3fSmrg default: assert(!"shouldn't be reached"); channel = 0; break; 18401e04c3fSmrg } 18501e04c3fSmrg } else { 18601e04c3fSmrg channel = i; 18701e04c3fSmrg } 18801e04c3fSmrg 18901e04c3fSmrg foreach_in_list(acp_entry, entry, this->acp) { 19001e04c3fSmrg if (entry->var == deref->var && entry->write_mask & (1 << channel)) { 19101e04c3fSmrg found = entry; 19201e04c3fSmrg break; 19301e04c3fSmrg } 19401e04c3fSmrg } 19501e04c3fSmrg 19601e04c3fSmrg if (!found) 19701e04c3fSmrg return; 19801e04c3fSmrg 19901e04c3fSmrg int rhs_channel = 0; 20001e04c3fSmrg for (int j = 0; j < 4; j++) { 20101e04c3fSmrg if (j == channel) 20201e04c3fSmrg break; 20301e04c3fSmrg if (found->initial_values & (1 << j)) 20401e04c3fSmrg rhs_channel++; 20501e04c3fSmrg } 20601e04c3fSmrg 20701e04c3fSmrg switch (type->base_type) { 20801e04c3fSmrg case GLSL_TYPE_FLOAT: 20901e04c3fSmrg data.f[i] = found->constant->value.f[rhs_channel]; 21001e04c3fSmrg break; 2117ec681f3Smrg case GLSL_TYPE_FLOAT16: 2127ec681f3Smrg data.f16[i] = found->constant->value.f16[rhs_channel]; 2137ec681f3Smrg break; 21401e04c3fSmrg case GLSL_TYPE_DOUBLE: 21501e04c3fSmrg data.d[i] = found->constant->value.d[rhs_channel]; 21601e04c3fSmrg break; 21701e04c3fSmrg case GLSL_TYPE_INT: 21801e04c3fSmrg data.i[i] = found->constant->value.i[rhs_channel]; 21901e04c3fSmrg break; 22001e04c3fSmrg case GLSL_TYPE_UINT: 22101e04c3fSmrg data.u[i] = found->constant->value.u[rhs_channel]; 22201e04c3fSmrg break; 2237ec681f3Smrg case GLSL_TYPE_INT16: 2247ec681f3Smrg data.i16[i] = found->constant->value.i16[rhs_channel]; 2257ec681f3Smrg break; 2267ec681f3Smrg case GLSL_TYPE_UINT16: 2277ec681f3Smrg data.u16[i] = found->constant->value.u16[rhs_channel]; 2287ec681f3Smrg break; 22901e04c3fSmrg case GLSL_TYPE_BOOL: 23001e04c3fSmrg data.b[i] = found->constant->value.b[rhs_channel]; 23101e04c3fSmrg break; 23201e04c3fSmrg case GLSL_TYPE_UINT64: 23301e04c3fSmrg data.u64[i] = found->constant->value.u64[rhs_channel]; 23401e04c3fSmrg break; 23501e04c3fSmrg case GLSL_TYPE_INT64: 23601e04c3fSmrg data.i64[i] = found->constant->value.i64[rhs_channel]; 23701e04c3fSmrg break; 23801e04c3fSmrg default: 23901e04c3fSmrg assert(!"not reached"); 24001e04c3fSmrg break; 24101e04c3fSmrg } 24201e04c3fSmrg } 24301e04c3fSmrg 24401e04c3fSmrg *rvalue = new(ralloc_parent(deref)) ir_constant(type, &data); 24501e04c3fSmrg this->progress = true; 24601e04c3fSmrg} 24701e04c3fSmrg 24801e04c3fSmrgvoid 24901e04c3fSmrgir_constant_propagation_visitor::handle_rvalue(ir_rvalue **rvalue) 25001e04c3fSmrg{ 25101e04c3fSmrg constant_propagation(rvalue); 25201e04c3fSmrg constant_folding(rvalue); 25301e04c3fSmrg} 25401e04c3fSmrg 25501e04c3fSmrgir_visitor_status 25601e04c3fSmrgir_constant_propagation_visitor::visit_enter(ir_function_signature *ir) 25701e04c3fSmrg{ 25801e04c3fSmrg /* Treat entry into a function signature as a completely separate 25901e04c3fSmrg * block. Any instructions at global scope will be shuffled into 26001e04c3fSmrg * main() at link time, so they're irrelevant to us. 26101e04c3fSmrg */ 26201e04c3fSmrg exec_list *orig_acp = this->acp; 26301e04c3fSmrg hash_table *orig_kills = this->kills; 26401e04c3fSmrg bool orig_killed_all = this->killed_all; 26501e04c3fSmrg 26601e04c3fSmrg this->acp = new(mem_ctx) exec_list; 2677e102996Smaya this->kills = _mesa_pointer_hash_table_create(mem_ctx); 26801e04c3fSmrg this->killed_all = false; 26901e04c3fSmrg 27001e04c3fSmrg visit_list_elements(this, &ir->body); 27101e04c3fSmrg 27201e04c3fSmrg this->kills = orig_kills; 27301e04c3fSmrg this->acp = orig_acp; 27401e04c3fSmrg this->killed_all = orig_killed_all; 27501e04c3fSmrg 27601e04c3fSmrg return visit_continue_with_parent; 27701e04c3fSmrg} 27801e04c3fSmrg 27901e04c3fSmrgir_visitor_status 28001e04c3fSmrgir_constant_propagation_visitor::visit_leave(ir_assignment *ir) 28101e04c3fSmrg{ 28201e04c3fSmrg constant_folding(&ir->rhs); 28301e04c3fSmrg 28401e04c3fSmrg if (this->in_assignee) 28501e04c3fSmrg return visit_continue; 28601e04c3fSmrg 28701e04c3fSmrg unsigned kill_mask = ir->write_mask; 28801e04c3fSmrg if (ir->lhs->as_dereference_array()) { 28901e04c3fSmrg /* The LHS of the assignment uses an array indexing operator (e.g. v[i] 29001e04c3fSmrg * = ...;). Since we only try to constant propagate vectors and 29101e04c3fSmrg * scalars, this means that either (a) array indexing is being used to 29201e04c3fSmrg * select a vector component, or (b) the variable in question is neither 29301e04c3fSmrg * a scalar or a vector, so we don't care about it. In the former case, 29401e04c3fSmrg * we want to kill the whole vector, since in general we can't predict 29501e04c3fSmrg * which vector component will be selected by array indexing. In the 29601e04c3fSmrg * latter case, it doesn't matter what we do, so go ahead and kill the 29701e04c3fSmrg * whole variable anyway. 29801e04c3fSmrg * 29901e04c3fSmrg * Note that if the array index is constant (e.g. v[2] = ...;), we could 30001e04c3fSmrg * in principle be smarter, but we don't need to, because a future 30101e04c3fSmrg * optimization pass will convert it to a simple assignment with the 30201e04c3fSmrg * correct mask. 30301e04c3fSmrg */ 30401e04c3fSmrg kill_mask = ~0; 30501e04c3fSmrg } 30601e04c3fSmrg kill(ir->lhs->variable_referenced(), kill_mask); 30701e04c3fSmrg 30801e04c3fSmrg add_constant(ir); 30901e04c3fSmrg 31001e04c3fSmrg return visit_continue; 31101e04c3fSmrg} 31201e04c3fSmrg 31301e04c3fSmrgir_visitor_status 31401e04c3fSmrgir_constant_propagation_visitor::visit_enter(ir_function *ir) 31501e04c3fSmrg{ 31601e04c3fSmrg (void) ir; 31701e04c3fSmrg return visit_continue; 31801e04c3fSmrg} 31901e04c3fSmrg 32001e04c3fSmrgir_visitor_status 32101e04c3fSmrgir_constant_propagation_visitor::visit_enter(ir_call *ir) 32201e04c3fSmrg{ 32301e04c3fSmrg /* Do constant propagation on call parameters, but skip any out params */ 32401e04c3fSmrg foreach_two_lists(formal_node, &ir->callee->parameters, 32501e04c3fSmrg actual_node, &ir->actual_parameters) { 32601e04c3fSmrg ir_variable *sig_param = (ir_variable *) formal_node; 32701e04c3fSmrg ir_rvalue *param = (ir_rvalue *) actual_node; 32801e04c3fSmrg if (sig_param->data.mode != ir_var_function_out 32901e04c3fSmrg && sig_param->data.mode != ir_var_function_inout) { 33001e04c3fSmrg ir_rvalue *new_param = param; 33101e04c3fSmrg handle_rvalue(&new_param); 33201e04c3fSmrg if (new_param != param) 33301e04c3fSmrg param->replace_with(new_param); 33401e04c3fSmrg else 33501e04c3fSmrg param->accept(this); 33601e04c3fSmrg } 33701e04c3fSmrg } 33801e04c3fSmrg 33901e04c3fSmrg /* Since we're unlinked, we don't (necssarily) know the side effects of 34001e04c3fSmrg * this call. So kill all copies. 34101e04c3fSmrg */ 34201e04c3fSmrg acp->make_empty(); 34301e04c3fSmrg this->killed_all = true; 34401e04c3fSmrg 34501e04c3fSmrg return visit_continue_with_parent; 34601e04c3fSmrg} 34701e04c3fSmrg 34801e04c3fSmrgvoid 34901e04c3fSmrgir_constant_propagation_visitor::handle_if_block(exec_list *instructions, hash_table *kills, bool *killed_all) 35001e04c3fSmrg{ 35101e04c3fSmrg exec_list *orig_acp = this->acp; 35201e04c3fSmrg hash_table *orig_kills = this->kills; 35301e04c3fSmrg bool orig_killed_all = this->killed_all; 35401e04c3fSmrg 35501e04c3fSmrg this->acp = new(mem_ctx) exec_list; 35601e04c3fSmrg this->kills = kills; 35701e04c3fSmrg this->killed_all = false; 35801e04c3fSmrg 35901e04c3fSmrg /* Populate the initial acp with a constant of the original */ 36001e04c3fSmrg foreach_in_list(acp_entry, a, orig_acp) { 36101e04c3fSmrg this->acp->push_tail(new(this->lin_ctx) acp_entry(a)); 36201e04c3fSmrg } 36301e04c3fSmrg 36401e04c3fSmrg visit_list_elements(this, instructions); 36501e04c3fSmrg 36601e04c3fSmrg *killed_all = this->killed_all; 36701e04c3fSmrg this->kills = orig_kills; 36801e04c3fSmrg this->acp = orig_acp; 36901e04c3fSmrg this->killed_all = orig_killed_all; 37001e04c3fSmrg} 37101e04c3fSmrg 37201e04c3fSmrgir_visitor_status 37301e04c3fSmrgir_constant_propagation_visitor::visit_enter(ir_if *ir) 37401e04c3fSmrg{ 37501e04c3fSmrg ir->condition->accept(this); 37601e04c3fSmrg handle_rvalue(&ir->condition); 37701e04c3fSmrg 3787e102996Smaya hash_table *new_kills = _mesa_pointer_hash_table_create(mem_ctx); 37901e04c3fSmrg bool then_killed_all = false; 38001e04c3fSmrg bool else_killed_all = false; 38101e04c3fSmrg 38201e04c3fSmrg handle_if_block(&ir->then_instructions, new_kills, &then_killed_all); 38301e04c3fSmrg handle_if_block(&ir->else_instructions, new_kills, &else_killed_all); 38401e04c3fSmrg 38501e04c3fSmrg if (then_killed_all || else_killed_all) { 38601e04c3fSmrg acp->make_empty(); 38701e04c3fSmrg killed_all = true; 38801e04c3fSmrg } else { 38901e04c3fSmrg hash_table_foreach(new_kills, htk) 39001e04c3fSmrg kill((ir_variable *) htk->key, (uintptr_t) htk->data); 39101e04c3fSmrg } 39201e04c3fSmrg 39301e04c3fSmrg _mesa_hash_table_destroy(new_kills, NULL); 39401e04c3fSmrg 39501e04c3fSmrg /* handle_if_block() already descended into the children. */ 39601e04c3fSmrg return visit_continue_with_parent; 39701e04c3fSmrg} 39801e04c3fSmrg 39901e04c3fSmrgvoid 40001e04c3fSmrgir_constant_propagation_visitor::handle_loop(ir_loop *ir, bool keep_acp) 40101e04c3fSmrg{ 40201e04c3fSmrg exec_list *orig_acp = this->acp; 40301e04c3fSmrg hash_table *orig_kills = this->kills; 40401e04c3fSmrg bool orig_killed_all = this->killed_all; 40501e04c3fSmrg 40601e04c3fSmrg this->acp = new(mem_ctx) exec_list; 4077e102996Smaya this->kills = _mesa_pointer_hash_table_create(mem_ctx); 40801e04c3fSmrg this->killed_all = false; 40901e04c3fSmrg 41001e04c3fSmrg if (keep_acp) { 41101e04c3fSmrg foreach_in_list(acp_entry, a, orig_acp) { 41201e04c3fSmrg this->acp->push_tail(new(this->lin_ctx) acp_entry(a)); 41301e04c3fSmrg } 41401e04c3fSmrg } 41501e04c3fSmrg 41601e04c3fSmrg visit_list_elements(this, &ir->body_instructions); 41701e04c3fSmrg 41801e04c3fSmrg if (this->killed_all) { 41901e04c3fSmrg orig_acp->make_empty(); 42001e04c3fSmrg } 42101e04c3fSmrg 42201e04c3fSmrg hash_table *new_kills = this->kills; 42301e04c3fSmrg this->kills = orig_kills; 42401e04c3fSmrg this->acp = orig_acp; 42501e04c3fSmrg this->killed_all = this->killed_all || orig_killed_all; 42601e04c3fSmrg 42701e04c3fSmrg hash_table_foreach(new_kills, htk) { 42801e04c3fSmrg kill((ir_variable *) htk->key, (uintptr_t) htk->data); 42901e04c3fSmrg } 43001e04c3fSmrg} 43101e04c3fSmrg 43201e04c3fSmrgir_visitor_status 43301e04c3fSmrgir_constant_propagation_visitor::visit_enter(ir_loop *ir) 43401e04c3fSmrg{ 43501e04c3fSmrg /* Make a conservative first pass over the loop with an empty ACP set. 43601e04c3fSmrg * This also removes any killed entries from the original ACP set. 43701e04c3fSmrg */ 43801e04c3fSmrg handle_loop(ir, false); 43901e04c3fSmrg 44001e04c3fSmrg /* Then, run it again with the real ACP set, minus any killed entries. 44101e04c3fSmrg * This takes care of propagating values from before the loop into it. 44201e04c3fSmrg */ 44301e04c3fSmrg handle_loop(ir, true); 44401e04c3fSmrg 44501e04c3fSmrg /* already descended into the children. */ 44601e04c3fSmrg return visit_continue_with_parent; 44701e04c3fSmrg} 44801e04c3fSmrg 44901e04c3fSmrgvoid 45001e04c3fSmrgir_constant_propagation_visitor::kill(ir_variable *var, unsigned write_mask) 45101e04c3fSmrg{ 45201e04c3fSmrg assert(var != NULL); 45301e04c3fSmrg 45401e04c3fSmrg /* We don't track non-vectors. */ 45501e04c3fSmrg if (!var->type->is_vector() && !var->type->is_scalar()) 45601e04c3fSmrg return; 45701e04c3fSmrg 45801e04c3fSmrg /* Remove any entries currently in the ACP for this kill. */ 45901e04c3fSmrg foreach_in_list_safe(acp_entry, entry, this->acp) { 46001e04c3fSmrg if (entry->var == var) { 46101e04c3fSmrg entry->write_mask &= ~write_mask; 46201e04c3fSmrg if (entry->write_mask == 0) 46301e04c3fSmrg entry->remove(); 46401e04c3fSmrg } 46501e04c3fSmrg } 46601e04c3fSmrg 46701e04c3fSmrg /* Add this writemask of the variable to the hash table of killed 46801e04c3fSmrg * variables in this block. 46901e04c3fSmrg */ 47001e04c3fSmrg hash_entry *kill_hash_entry = _mesa_hash_table_search(this->kills, var); 47101e04c3fSmrg if (kill_hash_entry) { 47201e04c3fSmrg uintptr_t new_write_mask = ((uintptr_t) kill_hash_entry->data) | write_mask; 47301e04c3fSmrg kill_hash_entry->data = (void *) new_write_mask; 47401e04c3fSmrg return; 47501e04c3fSmrg } 47601e04c3fSmrg /* Not already in the hash table. Make new entry. */ 47701e04c3fSmrg _mesa_hash_table_insert(this->kills, var, (void *) uintptr_t(write_mask)); 47801e04c3fSmrg} 47901e04c3fSmrg 48001e04c3fSmrg/** 48101e04c3fSmrg * Adds an entry to the available constant list if it's a plain assignment 48201e04c3fSmrg * of a variable to a variable. 48301e04c3fSmrg */ 48401e04c3fSmrgvoid 48501e04c3fSmrgir_constant_propagation_visitor::add_constant(ir_assignment *ir) 48601e04c3fSmrg{ 48701e04c3fSmrg acp_entry *entry; 48801e04c3fSmrg 48901e04c3fSmrg if (ir->condition) 49001e04c3fSmrg return; 49101e04c3fSmrg 49201e04c3fSmrg if (!ir->write_mask) 49301e04c3fSmrg return; 49401e04c3fSmrg 49501e04c3fSmrg ir_dereference_variable *deref = ir->lhs->as_dereference_variable(); 49601e04c3fSmrg ir_constant *constant = ir->rhs->as_constant(); 49701e04c3fSmrg 49801e04c3fSmrg if (!deref || !constant) 49901e04c3fSmrg return; 50001e04c3fSmrg 50101e04c3fSmrg /* Only do constant propagation on vectors. Constant matrices, 50201e04c3fSmrg * arrays, or structures would require more work elsewhere. 50301e04c3fSmrg */ 50401e04c3fSmrg if (!deref->var->type->is_vector() && !deref->var->type->is_scalar()) 50501e04c3fSmrg return; 50601e04c3fSmrg 50701e04c3fSmrg /* We can't do copy propagation on buffer variables, since the underlying 50801e04c3fSmrg * memory storage is shared across multiple threads we can't be sure that 50901e04c3fSmrg * the variable value isn't modified between this assignment and the next 51001e04c3fSmrg * instruction where its value is read. 51101e04c3fSmrg */ 51201e04c3fSmrg if (deref->var->data.mode == ir_var_shader_storage || 51301e04c3fSmrg deref->var->data.mode == ir_var_shader_shared) 51401e04c3fSmrg return; 51501e04c3fSmrg 51601e04c3fSmrg entry = new(this->lin_ctx) acp_entry(deref->var, ir->write_mask, constant); 51701e04c3fSmrg this->acp->push_tail(entry); 51801e04c3fSmrg} 51901e04c3fSmrg 52001e04c3fSmrg} /* unnamed namespace */ 52101e04c3fSmrg 52201e04c3fSmrg/** 52301e04c3fSmrg * Does a constant propagation pass on the code present in the instruction stream. 52401e04c3fSmrg */ 52501e04c3fSmrgbool 52601e04c3fSmrgdo_constant_propagation(exec_list *instructions) 52701e04c3fSmrg{ 52801e04c3fSmrg ir_constant_propagation_visitor v; 52901e04c3fSmrg 53001e04c3fSmrg visit_list_elements(&v, instructions); 53101e04c3fSmrg 53201e04c3fSmrg return v.progress; 53301e04c3fSmrg} 534