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 lower_vec_index_to_cond_assign.cpp 26 * 27 * Turns indexing into vector types to a series of conditional moves 28 * of each channel's swizzle into a temporary. 29 * 30 * Most GPUs don't have a native way to do this operation, and this 31 * works around that. For drivers using both this pass and 32 * ir_vec_index_to_swizzle, there's a risk that this pass will happen 33 * before sufficient constant folding to find that the array index is 34 * constant. However, we hope that other optimization passes, 35 * particularly constant folding of assignment conditions and copy 36 * propagation, will result in the same code in the end. 37 */ 38 39#include "ir.h" 40#include "ir_visitor.h" 41#include "ir_optimization.h" 42#include "glsl_types.h" 43 44/** 45 * Visitor class for replacing expressions with ir_constant values. 46 */ 47 48class ir_vec_index_to_cond_assign_visitor : public ir_hierarchical_visitor { 49public: 50 ir_vec_index_to_cond_assign_visitor() 51 { 52 progress = false; 53 } 54 55 ir_rvalue *convert_vec_index_to_cond_assign(ir_rvalue *val); 56 57 virtual ir_visitor_status visit_enter(ir_expression *); 58 virtual ir_visitor_status visit_enter(ir_swizzle *); 59 virtual ir_visitor_status visit_leave(ir_assignment *); 60 virtual ir_visitor_status visit_enter(ir_return *); 61 virtual ir_visitor_status visit_enter(ir_call *); 62 virtual ir_visitor_status visit_enter(ir_if *); 63 64 bool progress; 65}; 66 67ir_rvalue * 68ir_vec_index_to_cond_assign_visitor::convert_vec_index_to_cond_assign(ir_rvalue *ir) 69{ 70 ir_dereference_array *orig_deref = ir->as_dereference_array(); 71 ir_assignment *assign; 72 ir_variable *index, *var; 73 ir_dereference *deref; 74 int i; 75 76 if (!orig_deref) 77 return ir; 78 79 if (orig_deref->array->type->is_matrix() || 80 orig_deref->array->type->is_array()) 81 return ir; 82 83 void *mem_ctx = ralloc_parent(ir); 84 85 assert(orig_deref->array_index->type->base_type == GLSL_TYPE_INT); 86 87 exec_list list; 88 89 /* Store the index to a temporary to avoid reusing its tree. */ 90 index = new(base_ir) ir_variable(glsl_type::int_type, 91 "vec_index_tmp_i", 92 ir_var_temporary); 93 list.push_tail(index); 94 deref = new(base_ir) ir_dereference_variable(index); 95 assign = new(base_ir) ir_assignment(deref, orig_deref->array_index, NULL); 96 list.push_tail(assign); 97 98 /* Temporary where we store whichever value we swizzle out. */ 99 var = new(base_ir) ir_variable(ir->type, "vec_index_tmp_v", 100 ir_var_temporary); 101 list.push_tail(var); 102 103 /* Generate a single comparison condition "mask" for all of the components 104 * in the vector. 105 */ 106 ir_rvalue *const cond_deref = 107 compare_index_block(&list, index, 0, 108 orig_deref->array->type->vector_elements, 109 mem_ctx); 110 111 /* Generate a conditional move of each vector element to the temp. */ 112 for (i = 0; i < orig_deref->array->type->vector_elements; i++) { 113 ir_rvalue *condition_swizzle = 114 new(base_ir) ir_swizzle(cond_deref->clone(ir, NULL), i, 0, 0, 0, 1); 115 116 /* Just clone the rest of the deref chain when trying to get at the 117 * underlying variable. 118 */ 119 ir_rvalue *swizzle = 120 new(base_ir) ir_swizzle(orig_deref->array->clone(mem_ctx, NULL), 121 i, 0, 0, 0, 1); 122 123 deref = new(base_ir) ir_dereference_variable(var); 124 assign = new(base_ir) ir_assignment(deref, swizzle, condition_swizzle); 125 list.push_tail(assign); 126 } 127 128 /* Put all of the new instructions in the IR stream before the old 129 * instruction. 130 */ 131 base_ir->insert_before(&list); 132 133 this->progress = true; 134 return new(base_ir) ir_dereference_variable(var); 135} 136 137ir_visitor_status 138ir_vec_index_to_cond_assign_visitor::visit_enter(ir_expression *ir) 139{ 140 unsigned int i; 141 142 for (i = 0; i < ir->get_num_operands(); i++) { 143 ir->operands[i] = convert_vec_index_to_cond_assign(ir->operands[i]); 144 } 145 146 return visit_continue; 147} 148 149ir_visitor_status 150ir_vec_index_to_cond_assign_visitor::visit_enter(ir_swizzle *ir) 151{ 152 /* Can't be hit from normal GLSL, since you can't swizzle a scalar (which 153 * the result of indexing a vector is. But maybe at some point we'll end up 154 * using swizzling of scalars for vector construction. 155 */ 156 ir->val = convert_vec_index_to_cond_assign(ir->val); 157 158 return visit_continue; 159} 160 161ir_visitor_status 162ir_vec_index_to_cond_assign_visitor::visit_leave(ir_assignment *ir) 163{ 164 ir_variable *index, *var; 165 ir_dereference_variable *deref; 166 ir_assignment *assign; 167 int i; 168 169 ir->rhs = convert_vec_index_to_cond_assign(ir->rhs); 170 if (ir->condition) 171 ir->condition = convert_vec_index_to_cond_assign(ir->condition); 172 173 /* Last, handle the LHS */ 174 ir_dereference_array *orig_deref = ir->lhs->as_dereference_array(); 175 176 if (!orig_deref || 177 orig_deref->array->type->is_matrix() || 178 orig_deref->array->type->is_array()) 179 return visit_continue; 180 181 void *mem_ctx = ralloc_parent(ir); 182 183 assert(orig_deref->array_index->type->base_type == GLSL_TYPE_INT); 184 185 exec_list list; 186 187 /* Store the index to a temporary to avoid reusing its tree. */ 188 index = new(ir) ir_variable(glsl_type::int_type, "vec_index_tmp_i", 189 ir_var_temporary); 190 list.push_tail(index); 191 deref = new(ir) ir_dereference_variable(index); 192 assign = new(ir) ir_assignment(deref, orig_deref->array_index, NULL); 193 list.push_tail(assign); 194 195 /* Store the RHS to a temporary to avoid reusing its tree. */ 196 var = new(ir) ir_variable(ir->rhs->type, "vec_index_tmp_v", 197 ir_var_temporary); 198 list.push_tail(var); 199 deref = new(ir) ir_dereference_variable(var); 200 assign = new(ir) ir_assignment(deref, ir->rhs, NULL); 201 list.push_tail(assign); 202 203 /* Generate a single comparison condition "mask" for all of the components 204 * in the vector. 205 */ 206 ir_rvalue *const cond_deref = 207 compare_index_block(&list, index, 0, 208 orig_deref->array->type->vector_elements, 209 mem_ctx); 210 211 /* Generate a conditional move of each vector element to the temp. */ 212 for (i = 0; i < orig_deref->array->type->vector_elements; i++) { 213 ir_rvalue *condition_swizzle = 214 new(ir) ir_swizzle(cond_deref->clone(ir, NULL), i, 0, 0, 0, 1); 215 216 217 /* Just clone the rest of the deref chain when trying to get at the 218 * underlying variable. 219 */ 220 ir_rvalue *swizzle = 221 new(ir) ir_swizzle(orig_deref->array->clone(mem_ctx, NULL), 222 i, 0, 0, 0, 1); 223 224 deref = new(ir) ir_dereference_variable(var); 225 assign = new(ir) ir_assignment(swizzle, deref, condition_swizzle); 226 list.push_tail(assign); 227 } 228 229 /* If the original assignment has a condition, respect that original 230 * condition! This is acomplished by wrapping the new conditional 231 * assignments in an if-statement that uses the original condition. 232 */ 233 if (ir->condition != NULL) { 234 /* No need to clone the condition because the IR that it hangs on is 235 * going to be removed from the instruction sequence. 236 */ 237 ir_if *if_stmt = new(mem_ctx) ir_if(ir->condition); 238 239 list.move_nodes_to(&if_stmt->then_instructions); 240 ir->insert_before(if_stmt); 241 } else { 242 ir->insert_before(&list); 243 } 244 245 ir->remove(); 246 247 this->progress = true; 248 249 return visit_continue; 250} 251 252ir_visitor_status 253ir_vec_index_to_cond_assign_visitor::visit_enter(ir_call *ir) 254{ 255 foreach_iter(exec_list_iterator, iter, *ir) { 256 ir_rvalue *param = (ir_rvalue *)iter.get(); 257 ir_rvalue *new_param = convert_vec_index_to_cond_assign(param); 258 259 if (new_param != param) { 260 param->replace_with(new_param); 261 } 262 } 263 264 return visit_continue; 265} 266 267ir_visitor_status 268ir_vec_index_to_cond_assign_visitor::visit_enter(ir_return *ir) 269{ 270 if (ir->value) { 271 ir->value = convert_vec_index_to_cond_assign(ir->value); 272 } 273 274 return visit_continue; 275} 276 277ir_visitor_status 278ir_vec_index_to_cond_assign_visitor::visit_enter(ir_if *ir) 279{ 280 ir->condition = convert_vec_index_to_cond_assign(ir->condition); 281 282 return visit_continue; 283} 284 285bool 286do_vec_index_to_cond_assign(exec_list *instructions) 287{ 288 ir_vec_index_to_cond_assign_visitor v; 289 290 visit_list_elements(&v, instructions); 291 292 return v.progress; 293} 294