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_structure_splitting.cpp 26 * 27 * If a structure is only ever referenced by its components, then 28 * split those components out to individual variables so they can be 29 * handled normally by other optimization passes. 30 * 31 * This skips structures like uniforms, which need to be accessible as 32 * structures for their access by the GL. 33 */ 34 35 #include "ir.h" 36 #include "ir_visitor.h" 37 #include "ir_print_visitor.h" 38 #include "ir_rvalue_visitor.h" 39 #include "glsl_types.h" 40 41 static bool debug = false; 42 43 // XXX using variable_entry2 here to avoid collision (MSVC multiply-defined 44 // function) with the variable_entry class seen in ir_variable_refcount.h 45 // Perhaps we can use the one in ir_variable_refcount.h and make this class 46 // here go away? 47 class variable_entry2 : public exec_node 48 { 49 public: 50 variable_entry2(ir_variable *var) 51 { 52 this->var = var; 53 this->whole_structure_access = 0; 54 this->declaration = false; 55 this->components = NULL; 56 this->mem_ctx = NULL; 57 } 58 59 ir_variable *var; /* The key: the variable's pointer. */ 60 61 /** Number of times the variable is referenced, including assignments. */ 62 unsigned whole_structure_access; 63 64 bool declaration; /* If the variable had a decl in the instruction stream */ 65 66 ir_variable **components; 67 68 /** hieralloc_parent(this->var) -- the shader's hieralloc context. */ 69 void *mem_ctx; 70 }; 71 72 73 class ir_structure_reference_visitor : public ir_hierarchical_visitor { 74 public: 75 ir_structure_reference_visitor(void) 76 { 77 this->mem_ctx = hieralloc_new(NULL); 78 this->variable_list.make_empty(); 79 } 80 81 ~ir_structure_reference_visitor(void) 82 { 83 hieralloc_free(mem_ctx); 84 } 85 86 virtual ir_visitor_status visit(ir_variable *); 87 virtual ir_visitor_status visit(ir_dereference_variable *); 88 virtual ir_visitor_status visit_enter(ir_dereference_record *); 89 virtual ir_visitor_status visit_enter(ir_assignment *); 90 virtual ir_visitor_status visit_enter(ir_function_signature *); 91 92 variable_entry2 *get_variable_entry2(ir_variable *var); 93 94 /* List of variable_entry */ 95 exec_list variable_list; 96 97 void *mem_ctx; 98 }; 99 100 variable_entry2 * 101 ir_structure_reference_visitor::get_variable_entry2(ir_variable *var) 102 { 103 assert(var); 104 105 if (!var->type->is_record() || var->mode == ir_var_uniform) 106 return NULL; 107 108 foreach_iter(exec_list_iterator, iter, this->variable_list) { 109 variable_entry2 *entry = (variable_entry2 *)iter.get(); 110 if (entry->var == var) 111 return entry; 112 } 113 114 variable_entry2 *entry = new(mem_ctx) variable_entry2(var); 115 this->variable_list.push_tail(entry); 116 return entry; 117 } 118 119 120 ir_visitor_status 121 ir_structure_reference_visitor::visit(ir_variable *ir) 122 { 123 variable_entry2 *entry = this->get_variable_entry2(ir); 124 125 if (entry) 126 entry->declaration = true; 127 128 return visit_continue; 129 } 130 131 ir_visitor_status 132 ir_structure_reference_visitor::visit(ir_dereference_variable *ir) 133 { 134 ir_variable *const var = ir->variable_referenced(); 135 variable_entry2 *entry = this->get_variable_entry2(var); 136 137 if (entry) 138 entry->whole_structure_access++; 139 140 return visit_continue; 141 } 142 143 ir_visitor_status 144 ir_structure_reference_visitor::visit_enter(ir_dereference_record *ir) 145 { 146 (void) ir; 147 /* Don't descend into the ir_dereference_variable below. */ 148 return visit_continue_with_parent; 149 } 150 151 ir_visitor_status 152 ir_structure_reference_visitor::visit_enter(ir_assignment *ir) 153 { 154 if (ir->lhs->as_dereference_variable() && 155 ir->rhs->as_dereference_variable() && 156 !ir->condition) { 157 /* We'll split copies of a structure to copies of components, so don't 158 * descend to the ir_dereference_variables. 159 */ 160 return visit_continue_with_parent; 161 } 162 return visit_continue; 163 } 164 165 ir_visitor_status 166 ir_structure_reference_visitor::visit_enter(ir_function_signature *ir) 167 { 168 /* We don't want to descend into the function parameters and 169 * dead-code eliminate them, so just accept the body here. 170 */ 171 visit_list_elements(this, &ir->body); 172 return visit_continue_with_parent; 173 } 174 175 class ir_structure_splitting_visitor : public ir_rvalue_visitor { 176 public: 177 ir_structure_splitting_visitor(exec_list *vars) 178 { 179 this->variable_list = vars; 180 } 181 182 virtual ~ir_structure_splitting_visitor() 183 { 184 } 185 186 virtual ir_visitor_status visit_leave(ir_assignment *); 187 188 void split_deref(ir_dereference **deref); 189 void handle_rvalue(ir_rvalue **rvalue); 190 variable_entry2 *get_splitting_entry(ir_variable *var); 191 192 exec_list *variable_list; 193 void *mem_ctx; 194 }; 195 196 variable_entry2 * 197 ir_structure_splitting_visitor::get_splitting_entry(ir_variable *var) 198 { 199 assert(var); 200 201 if (!var->type->is_record()) 202 return NULL; 203 204 foreach_iter(exec_list_iterator, iter, *this->variable_list) { 205 variable_entry2 *entry = (variable_entry2 *)iter.get(); 206 if (entry->var == var) { 207 return entry; 208 } 209 } 210 211 return NULL; 212 } 213 214 void 215 ir_structure_splitting_visitor::split_deref(ir_dereference **deref) 216 { 217 if ((*deref)->ir_type != ir_type_dereference_record) 218 return; 219 220 ir_dereference_record *deref_record = (ir_dereference_record *)*deref; 221 ir_dereference_variable *deref_var = deref_record->record->as_dereference_variable(); 222 if (!deref_var) 223 return; 224 225 variable_entry2 *entry = get_splitting_entry(deref_var->var); 226 if (!entry) 227 return; 228 229 unsigned int i; 230 for (i = 0; i < entry->var->type->length; i++) { 231 if (strcmp(deref_record->field, 232 entry->var->type->fields.structure[i].name) == 0) 233 break; 234 } 235 assert(i != entry->var->type->length); 236 237 *deref = new(entry->mem_ctx) ir_dereference_variable(entry->components[i]); 238 } 239 240 void 241 ir_structure_splitting_visitor::handle_rvalue(ir_rvalue **rvalue) 242 { 243 if (!*rvalue) 244 return; 245 246 ir_dereference *deref = (*rvalue)->as_dereference(); 247 248 if (!deref) 249 return; 250 251 split_deref(&deref); 252 *rvalue = deref; 253 } 254 255 ir_visitor_status 256 ir_structure_splitting_visitor::visit_leave(ir_assignment *ir) 257 { 258 ir_dereference_variable *lhs_deref = ir->lhs->as_dereference_variable(); 259 ir_dereference_variable *rhs_deref = ir->rhs->as_dereference_variable(); 260 variable_entry2 *lhs_entry = lhs_deref ? get_splitting_entry(lhs_deref->var) : NULL; 261 variable_entry2 *rhs_entry = rhs_deref ? get_splitting_entry(rhs_deref->var) : NULL; 262 const glsl_type *type = ir->rhs->type; 263 264 if ((lhs_entry || rhs_entry) && !ir->condition) { 265 for (unsigned int i = 0; i < type->length; i++) { 266 ir_dereference *new_lhs, *new_rhs; 267 void *mem_ctx = lhs_entry ? lhs_entry->mem_ctx : rhs_entry->mem_ctx; 268 269 if (lhs_entry) { 270 new_lhs = new(mem_ctx) ir_dereference_variable(lhs_entry->components[i]); 271 } else { 272 new_lhs = new(mem_ctx) 273 ir_dereference_record(ir->lhs->clone(mem_ctx, NULL), 274 type->fields.structure[i].name); 275 } 276 277 if (rhs_entry) { 278 new_rhs = new(mem_ctx) ir_dereference_variable(rhs_entry->components[i]); 279 } else { 280 new_rhs = new(mem_ctx) 281 ir_dereference_record(ir->rhs->clone(mem_ctx, NULL), 282 type->fields.structure[i].name); 283 } 284 285 ir->insert_before(new(mem_ctx) ir_assignment(new_lhs, 286 new_rhs, 287 NULL)); 288 } 289 ir->remove(); 290 } else { 291 handle_rvalue(&ir->rhs); 292 split_deref(&ir->lhs); 293 } 294 295 handle_rvalue(&ir->condition); 296 297 return visit_continue; 298 } 299 300 bool 301 do_structure_splitting(exec_list *instructions) 302 { 303 ir_structure_reference_visitor refs; 304 305 visit_list_elements(&refs, instructions); 306 307 /* Trim out variables we can't split. */ 308 foreach_iter(exec_list_iterator, iter, refs.variable_list) { 309 variable_entry2 *entry = (variable_entry2 *)iter.get(); 310 311 if (debug) { 312 printf("structure %s@%p: decl %d, whole_access %d\n", 313 entry->var->name, (void *) entry->var, entry->declaration, 314 entry->whole_structure_access); 315 } 316 317 if (!entry->declaration || entry->whole_structure_access) { 318 entry->remove(); 319 } 320 } 321 322 if (refs.variable_list.is_empty()) 323 return false; 324 325 void *mem_ctx = hieralloc_new(NULL); 326 327 /* Replace the decls of the structures to be split with their split 328 * components. 329 */ 330 foreach_iter(exec_list_iterator, iter, refs.variable_list) { 331 variable_entry2 *entry = (variable_entry2 *)iter.get(); 332 const struct glsl_type *type = entry->var->type; 333 334 entry->mem_ctx = hieralloc_parent(entry->var); 335 336 entry->components = hieralloc_array(mem_ctx, 337 ir_variable *, 338 type->length); 339 340 for (unsigned int i = 0; i < entry->var->type->length; i++) { 341 const char *name = hieralloc_asprintf(mem_ctx, "%s_%s", 342 entry->var->name, 343 type->fields.structure[i].name); 344 345 entry->components[i] = 346 new(entry->mem_ctx) ir_variable(type->fields.structure[i].type, 347 name, 348 ir_var_temporary); 349 entry->var->insert_before(entry->components[i]); 350 } 351 352 entry->var->remove(); 353 } 354 355 ir_structure_splitting_visitor split(&refs.variable_list); 356 visit_list_elements(&split, instructions); 357 358 hieralloc_free(mem_ctx); 359 360 return true; 361 } 362