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_rvalue_visitor.h" 38#include "compiler/glsl_types.h" 39 40namespace { 41 42static bool debug = false; 43 44class variable_entry : public exec_node 45{ 46public: 47 variable_entry(ir_variable *var) 48 { 49 this->var = var; 50 this->whole_structure_access = 0; 51 this->declaration = false; 52 this->components = NULL; 53 this->mem_ctx = NULL; 54 } 55 56 ir_variable *var; /* The key: the variable's pointer. */ 57 58 /** Number of times the variable is referenced, including assignments. */ 59 unsigned whole_structure_access; 60 61 /* If the variable had a decl we can work with in the instruction 62 * stream. We can't do splitting on function arguments, which 63 * don't get this variable set. 64 */ 65 bool declaration; 66 67 ir_variable **components; 68 69 /** ralloc_parent(this->var) -- the shader's ralloc context. */ 70 void *mem_ctx; 71}; 72 73 74class ir_structure_reference_visitor : public ir_hierarchical_visitor { 75public: 76 ir_structure_reference_visitor(void) 77 { 78 this->mem_ctx = ralloc_context(NULL); 79 this->variable_list.make_empty(); 80 } 81 82 ~ir_structure_reference_visitor(void) 83 { 84 ralloc_free(mem_ctx); 85 } 86 87 virtual ir_visitor_status visit(ir_variable *); 88 virtual ir_visitor_status visit(ir_dereference_variable *); 89 virtual ir_visitor_status visit_enter(ir_dereference_record *); 90 virtual ir_visitor_status visit_enter(ir_assignment *); 91 virtual ir_visitor_status visit_enter(ir_function_signature *); 92 93 variable_entry *get_variable_entry(ir_variable *var); 94 95 /* List of variable_entry */ 96 exec_list variable_list; 97 98 void *mem_ctx; 99}; 100 101variable_entry * 102ir_structure_reference_visitor::get_variable_entry(ir_variable *var) 103{ 104 assert(var); 105 106 if (!var->type->is_struct() || 107 var->data.mode == ir_var_uniform || var->data.mode == ir_var_shader_storage || 108 var->data.mode == ir_var_shader_in || var->data.mode == ir_var_shader_out) 109 return NULL; 110 111 foreach_in_list(variable_entry, entry, &this->variable_list) { 112 if (entry->var == var) 113 return entry; 114 } 115 116 variable_entry *entry = new(mem_ctx) variable_entry(var); 117 this->variable_list.push_tail(entry); 118 return entry; 119} 120 121 122ir_visitor_status 123ir_structure_reference_visitor::visit(ir_variable *ir) 124{ 125 variable_entry *entry = this->get_variable_entry(ir); 126 127 if (entry) 128 entry->declaration = true; 129 130 return visit_continue; 131} 132 133ir_visitor_status 134ir_structure_reference_visitor::visit(ir_dereference_variable *ir) 135{ 136 ir_variable *const var = ir->variable_referenced(); 137 variable_entry *entry = this->get_variable_entry(var); 138 139 if (entry) 140 entry->whole_structure_access++; 141 142 return visit_continue; 143} 144 145ir_visitor_status 146ir_structure_reference_visitor::visit_enter(ir_dereference_record *ir) 147{ 148 (void) ir; 149 /* Don't descend into the ir_dereference_variable below. */ 150 return visit_continue_with_parent; 151} 152 153ir_visitor_status 154ir_structure_reference_visitor::visit_enter(ir_assignment *ir) 155{ 156 /* If there are no structure references yet, no need to bother with 157 * processing the expression tree. 158 */ 159 if (this->variable_list.is_empty()) 160 return visit_continue_with_parent; 161 162 if (ir->lhs->as_dereference_variable() && 163 ir->rhs->as_dereference_variable()) { 164 /* We'll split copies of a structure to copies of components, so don't 165 * descend to the ir_dereference_variables. 166 */ 167 return visit_continue_with_parent; 168 } 169 return visit_continue; 170} 171 172ir_visitor_status 173ir_structure_reference_visitor::visit_enter(ir_function_signature *ir) 174{ 175 /* We don't have logic for structure-splitting function arguments, 176 * so just look at the body instructions and not the parameter 177 * declarations. 178 */ 179 visit_list_elements(this, &ir->body); 180 return visit_continue_with_parent; 181} 182 183class ir_structure_splitting_visitor : public ir_rvalue_visitor { 184public: 185 ir_structure_splitting_visitor(exec_list *vars) 186 { 187 this->variable_list = vars; 188 } 189 190 virtual ~ir_structure_splitting_visitor() 191 { 192 } 193 194 virtual ir_visitor_status visit_leave(ir_assignment *); 195 196 void split_deref(ir_dereference **deref); 197 void handle_rvalue(ir_rvalue **rvalue); 198 variable_entry *get_splitting_entry(ir_variable *var); 199 200 exec_list *variable_list; 201}; 202 203variable_entry * 204ir_structure_splitting_visitor::get_splitting_entry(ir_variable *var) 205{ 206 assert(var); 207 208 if (!var->type->is_struct()) 209 return NULL; 210 211 foreach_in_list(variable_entry, entry, this->variable_list) { 212 if (entry->var == var) { 213 return entry; 214 } 215 } 216 217 return NULL; 218} 219 220void 221ir_structure_splitting_visitor::split_deref(ir_dereference **deref) 222{ 223 if ((*deref)->ir_type != ir_type_dereference_record) 224 return; 225 226 ir_dereference_record *deref_record = (ir_dereference_record *)*deref; 227 ir_dereference_variable *deref_var = deref_record->record->as_dereference_variable(); 228 if (!deref_var) 229 return; 230 231 variable_entry *entry = get_splitting_entry(deref_var->var); 232 if (!entry) 233 return; 234 235 int i = deref_record->field_idx; 236 assert(i >= 0); 237 assert((unsigned) i < entry->var->type->length); 238 239 *deref = new(entry->mem_ctx) ir_dereference_variable(entry->components[i]); 240} 241 242void 243ir_structure_splitting_visitor::handle_rvalue(ir_rvalue **rvalue) 244{ 245 if (!*rvalue) 246 return; 247 248 ir_dereference *deref = (*rvalue)->as_dereference(); 249 250 if (!deref) 251 return; 252 253 split_deref(&deref); 254 *rvalue = deref; 255} 256 257ir_visitor_status 258ir_structure_splitting_visitor::visit_leave(ir_assignment *ir) 259{ 260 ir_dereference_variable *lhs_deref = ir->lhs->as_dereference_variable(); 261 ir_dereference_variable *rhs_deref = ir->rhs->as_dereference_variable(); 262 variable_entry *lhs_entry = lhs_deref ? get_splitting_entry(lhs_deref->var) : NULL; 263 variable_entry *rhs_entry = rhs_deref ? get_splitting_entry(rhs_deref->var) : NULL; 264 const glsl_type *type = ir->rhs->type; 265 266 if (lhs_entry || rhs_entry) { 267 for (unsigned int i = 0; i < type->length; i++) { 268 ir_dereference *new_lhs, *new_rhs; 269 void *mem_ctx = lhs_entry ? lhs_entry->mem_ctx : rhs_entry->mem_ctx; 270 271 if (lhs_entry) { 272 new_lhs = new(mem_ctx) ir_dereference_variable(lhs_entry->components[i]); 273 } else { 274 new_lhs = new(mem_ctx) 275 ir_dereference_record(ir->lhs->clone(mem_ctx, NULL), 276 type->fields.structure[i].name); 277 } 278 279 if (rhs_entry) { 280 new_rhs = new(mem_ctx) ir_dereference_variable(rhs_entry->components[i]); 281 } else { 282 new_rhs = new(mem_ctx) 283 ir_dereference_record(ir->rhs->clone(mem_ctx, NULL), 284 type->fields.structure[i].name); 285 } 286 287 ir->insert_before(new(mem_ctx) ir_assignment(new_lhs, new_rhs)); 288 } 289 ir->remove(); 290 } else { 291 handle_rvalue(&ir->rhs); 292 split_deref(&ir->lhs); 293 } 294 295 return visit_continue; 296} 297 298} /* unnamed namespace */ 299 300bool 301do_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_in_list_safe(variable_entry, entry, &refs.variable_list) { 309 if (debug) { 310 printf("structure %s@%p: decl %d, whole_access %d\n", 311 entry->var->name, (void *) entry->var, entry->declaration, 312 entry->whole_structure_access); 313 } 314 315 if (!entry->declaration || entry->whole_structure_access) { 316 entry->remove(); 317 } 318 } 319 320 if (refs.variable_list.is_empty()) 321 return false; 322 323 void *mem_ctx = ralloc_context(NULL); 324 325 /* Replace the decls of the structures to be split with their split 326 * components. 327 */ 328 foreach_in_list_safe(variable_entry, entry, &refs.variable_list) { 329 const struct glsl_type *type = entry->var->type; 330 331 entry->mem_ctx = ralloc_parent(entry->var); 332 333 entry->components = ralloc_array(mem_ctx, ir_variable *, type->length); 334 335 for (unsigned int i = 0; i < entry->var->type->length; i++) { 336 const char *name = ralloc_asprintf(mem_ctx, "%s_%s", entry->var->name, 337 type->fields.structure[i].name); 338 ir_variable *new_var = 339 new(entry->mem_ctx) ir_variable(type->fields.structure[i].type, 340 name, 341 (ir_variable_mode) entry->var->data.mode); 342 343 if (type->fields.structure[i].type->without_array()->is_image()) { 344 /* Do not lose memory/format qualifiers for images declared inside 345 * structures as allowed by ARB_bindless_texture. 346 */ 347 new_var->data.memory_read_only = 348 type->fields.structure[i].memory_read_only; 349 new_var->data.memory_write_only = 350 type->fields.structure[i].memory_write_only; 351 new_var->data.memory_coherent = 352 type->fields.structure[i].memory_coherent; 353 new_var->data.memory_volatile = 354 type->fields.structure[i].memory_volatile; 355 new_var->data.memory_restrict = 356 type->fields.structure[i].memory_restrict; 357 new_var->data.image_format = 358 type->fields.structure[i].image_format; 359 } 360 361 entry->components[i] = new_var; 362 entry->var->insert_before(entry->components[i]); 363 } 364 365 entry->var->remove(); 366 } 367 368 ir_structure_splitting_visitor split(&refs.variable_list); 369 visit_list_elements(&split, instructions); 370 371 ralloc_free(mem_ctx); 372 373 return true; 374} 375