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_constant_variable.cpp 26 * 27 * Marks variables assigned a single constant value over the course 28 * of the program as constant. 29 * 30 * The goal here is to trigger further constant folding and then dead 31 * code elimination. This is common with vector/matrix constructors 32 * and calls to builtin functions. 33 */ 34 35#include "ir.h" 36#include "ir_visitor.h" 37#include "ir_optimization.h" 38#include "compiler/glsl_types.h" 39#include "util/hash_table.h" 40 41namespace { 42 43struct assignment_entry { 44 int assignment_count; 45 ir_variable *var; 46 ir_constant *constval; 47 bool our_scope; 48}; 49 50class ir_constant_variable_visitor : public ir_hierarchical_visitor { 51public: 52 using ir_hierarchical_visitor::visit; 53 using ir_hierarchical_visitor::visit_enter; 54 55 virtual ir_visitor_status visit_enter(ir_dereference_variable *); 56 virtual ir_visitor_status visit(ir_variable *); 57 virtual ir_visitor_status visit_enter(ir_assignment *); 58 virtual ir_visitor_status visit_enter(ir_call *); 59 60 struct hash_table *ht; 61}; 62 63} /* unnamed namespace */ 64 65static struct assignment_entry * 66get_assignment_entry(ir_variable *var, struct hash_table *ht) 67{ 68 struct hash_entry *hte = _mesa_hash_table_search(ht, var); 69 struct assignment_entry *entry; 70 71 if (hte) { 72 entry = (struct assignment_entry *) hte->data; 73 } else { 74 entry = (struct assignment_entry *) calloc(1, sizeof(*entry)); 75 entry->var = var; 76 _mesa_hash_table_insert(ht, var, entry); 77 } 78 79 return entry; 80} 81 82ir_visitor_status 83ir_constant_variable_visitor::visit(ir_variable *ir) 84{ 85 struct assignment_entry *entry = get_assignment_entry(ir, this->ht); 86 entry->our_scope = true; 87 return visit_continue; 88} 89 90/* Skip derefs of variables so that we can detect declarations. */ 91ir_visitor_status 92ir_constant_variable_visitor::visit_enter(ir_dereference_variable *ir) 93{ 94 (void)ir; 95 return visit_continue_with_parent; 96} 97 98ir_visitor_status 99ir_constant_variable_visitor::visit_enter(ir_assignment *ir) 100{ 101 ir_constant *constval; 102 struct assignment_entry *entry; 103 104 entry = get_assignment_entry(ir->lhs->variable_referenced(), this->ht); 105 assert(entry); 106 entry->assignment_count++; 107 108 /* If there's more than one assignment, don't bother - we won't do anything 109 * with this variable anyway, and continuing just wastes memory cloning 110 * constant expressions. 111 */ 112 if (entry->assignment_count > 1) 113 return visit_continue; 114 115 /* If it's already constant, don't do the work. */ 116 if (entry->var->constant_value) 117 return visit_continue; 118 119 /* OK, now find if we actually have all the right conditions for 120 * this to be a constant value assigned to the var. 121 */ 122 if (ir->condition) 123 return visit_continue; 124 125 ir_variable *var = ir->whole_variable_written(); 126 if (!var) 127 return visit_continue; 128 129 /* Ignore buffer variables, since the underlying storage is shared 130 * and we can't be sure that this variable won't be written by another 131 * thread. 132 */ 133 if (var->data.mode == ir_var_shader_storage || 134 var->data.mode == ir_var_shader_shared) 135 return visit_continue; 136 137 constval = ir->rhs->constant_expression_value(ralloc_parent(ir)); 138 if (!constval) 139 return visit_continue; 140 141 /* Mark this entry as having a constant assignment (if the 142 * assignment count doesn't go >1). do_constant_variable will fix 143 * up the variable with the constant value later. 144 */ 145 entry->constval = constval; 146 147 return visit_continue; 148} 149 150ir_visitor_status 151ir_constant_variable_visitor::visit_enter(ir_call *ir) 152{ 153 /* Mark any out parameters as assigned to */ 154 foreach_two_lists(formal_node, &ir->callee->parameters, 155 actual_node, &ir->actual_parameters) { 156 ir_rvalue *param_rval = (ir_rvalue *) actual_node; 157 ir_variable *param = (ir_variable *) formal_node; 158 159 if (param->data.mode == ir_var_function_out || 160 param->data.mode == ir_var_function_inout) { 161 ir_variable *var = param_rval->variable_referenced(); 162 struct assignment_entry *entry; 163 164 assert(var); 165 entry = get_assignment_entry(var, this->ht); 166 entry->assignment_count++; 167 } 168 169 /* We don't know if the variable passed to this function has been 170 * assigned a value or if it is undefined, so for now we always assume 171 * it has been assigned a value. Once functions have been inlined any 172 * further potential optimisations will be taken care of. 173 */ 174 struct assignment_entry *entry; 175 entry = get_assignment_entry(param, this->ht); 176 entry->assignment_count++; 177 } 178 179 /* Mark the return storage as having been assigned to */ 180 if (ir->return_deref != NULL) { 181 ir_variable *var = ir->return_deref->variable_referenced(); 182 struct assignment_entry *entry; 183 184 assert(var); 185 entry = get_assignment_entry(var, this->ht); 186 entry->assignment_count++; 187 } 188 189 return visit_continue; 190} 191 192/** 193 * Does a copy propagation pass on the code present in the instruction stream. 194 */ 195bool 196do_constant_variable(exec_list *instructions) 197{ 198 bool progress = false; 199 ir_constant_variable_visitor v; 200 201 v.ht = _mesa_pointer_hash_table_create(NULL); 202 v.run(instructions); 203 204 hash_table_foreach(v.ht, hte) { 205 struct assignment_entry *entry = (struct assignment_entry *) hte->data; 206 207 if (entry->assignment_count == 1 && entry->constval && entry->our_scope) { 208 entry->var->constant_value = entry->constval; 209 progress = true; 210 } 211 hte->data = NULL; 212 free(entry); 213 } 214 _mesa_hash_table_destroy(v.ht, NULL); 215 216 return progress; 217} 218 219bool 220do_constant_variable_unlinked(exec_list *instructions) 221{ 222 bool progress = false; 223 224 foreach_in_list(ir_instruction, ir, instructions) { 225 ir_function *f = ir->as_function(); 226 if (f) { 227 foreach_in_list(ir_function_signature, sig, &f->signatures) { 228 if (do_constant_variable(&sig->body)) 229 progress = true; 230 } 231 } 232 } 233 234 return progress; 235} 236