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 48 class ir_vec_index_to_cond_assign_visitor : public ir_hierarchical_visitor { 49 public: 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 67 ir_rvalue * 68 ir_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 ir_expression *condition; 75 ir_swizzle *swizzle; 76 int i; 77 78 if (!orig_deref) 79 return ir; 80 81 if (orig_deref->array->type->is_matrix() || 82 orig_deref->array->type->is_array()) 83 return ir; 84 85 void *mem_ctx = hieralloc_parent(ir); 86 87 assert(orig_deref->array_index->type->base_type == GLSL_TYPE_INT); 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 base_ir->insert_before(index); 94 deref = new(base_ir) ir_dereference_variable(index); 95 assign = new(base_ir) ir_assignment(deref, orig_deref->array_index, NULL); 96 base_ir->insert_before(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 base_ir->insert_before(var); 102 103 /* Generate a conditional move of each vector element to the temp. */ 104 for (i = 0; i < orig_deref->array->type->vector_elements; i++) { 105 deref = new(base_ir) ir_dereference_variable(index); 106 condition = new(base_ir) ir_expression(ir_binop_equal, 107 glsl_type::bool_type, 108 deref, 109 new(base_ir) ir_constant(i)); 110 111 /* Just clone the rest of the deref chain when trying to get at the 112 * underlying variable. 113 */ 114 swizzle = new(base_ir) ir_swizzle(orig_deref->array->clone(mem_ctx, NULL), 115 i, 0, 0, 0, 1); 116 117 deref = new(base_ir) ir_dereference_variable(var); 118 assign = new(base_ir) ir_assignment(deref, swizzle, condition); 119 base_ir->insert_before(assign); 120 } 121 122 this->progress = true; 123 return new(base_ir) ir_dereference_variable(var); 124 } 125 126 ir_visitor_status 127 ir_vec_index_to_cond_assign_visitor::visit_enter(ir_expression *ir) 128 { 129 unsigned int i; 130 131 for (i = 0; i < ir->get_num_operands(); i++) { 132 ir->operands[i] = convert_vec_index_to_cond_assign(ir->operands[i]); 133 } 134 135 return visit_continue; 136 } 137 138 ir_visitor_status 139 ir_vec_index_to_cond_assign_visitor::visit_enter(ir_swizzle *ir) 140 { 141 /* Can't be hit from normal GLSL, since you can't swizzle a scalar (which 142 * the result of indexing a vector is. But maybe at some point we'll end up 143 * using swizzling of scalars for vector construction. 144 */ 145 ir->val = convert_vec_index_to_cond_assign(ir->val); 146 147 return visit_continue; 148 } 149 150 ir_visitor_status 151 ir_vec_index_to_cond_assign_visitor::visit_leave(ir_assignment *ir) 152 { 153 ir_variable *index, *var; 154 ir_dereference_variable *deref; 155 ir_assignment *assign; 156 int i; 157 158 ir->rhs = convert_vec_index_to_cond_assign(ir->rhs); 159 if (ir->condition) 160 ir->condition = convert_vec_index_to_cond_assign(ir->condition); 161 162 /* Last, handle the LHS */ 163 ir_dereference_array *orig_deref = ir->lhs->as_dereference_array(); 164 165 if (!orig_deref || 166 orig_deref->array->type->is_matrix() || 167 orig_deref->array->type->is_array()) 168 return visit_continue; 169 170 void *mem_ctx = hieralloc_parent(ir); 171 172 assert(orig_deref->array_index->type->base_type == GLSL_TYPE_INT); 173 174 /* Store the index to a temporary to avoid reusing its tree. */ 175 index = new(ir) ir_variable(glsl_type::int_type, "vec_index_tmp_i", 176 ir_var_temporary); 177 ir->insert_before(index); 178 deref = new(ir) ir_dereference_variable(index); 179 assign = new(ir) ir_assignment(deref, orig_deref->array_index, NULL); 180 ir->insert_before(assign); 181 182 /* Store the RHS to a temporary to avoid reusing its tree. */ 183 var = new(ir) ir_variable(ir->rhs->type, "vec_index_tmp_v", 184 ir_var_temporary); 185 ir->insert_before(var); 186 deref = new(ir) ir_dereference_variable(var); 187 assign = new(ir) ir_assignment(deref, ir->rhs, NULL); 188 ir->insert_before(assign); 189 190 /* Generate a conditional move of each vector element to the temp. */ 191 for (i = 0; i < orig_deref->array->type->vector_elements; i++) { 192 ir_rvalue *condition, *swizzle; 193 194 deref = new(ir) ir_dereference_variable(index); 195 condition = new(ir) ir_expression(ir_binop_equal, 196 glsl_type::bool_type, 197 deref, 198 new(ir) ir_constant(i)); 199 200 /* Just clone the rest of the deref chain when trying to get at the 201 * underlying variable. 202 */ 203 swizzle = new(ir) ir_swizzle(orig_deref->array->clone(mem_ctx, NULL), 204 i, 0, 0, 0, 1); 205 206 deref = new(ir) ir_dereference_variable(var); 207 assign = new(ir) ir_assignment(swizzle, deref, condition); 208 ir->insert_before(assign); 209 } 210 ir->remove(); 211 212 this->progress = true; 213 214 return visit_continue; 215 } 216 217 ir_visitor_status 218 ir_vec_index_to_cond_assign_visitor::visit_enter(ir_call *ir) 219 { 220 foreach_iter(exec_list_iterator, iter, *ir) { 221 ir_rvalue *param = (ir_rvalue *)iter.get(); 222 ir_rvalue *new_param = convert_vec_index_to_cond_assign(param); 223 224 if (new_param != param) { 225 param->replace_with(new_param); 226 } 227 } 228 229 return visit_continue; 230 } 231 232 ir_visitor_status 233 ir_vec_index_to_cond_assign_visitor::visit_enter(ir_return *ir) 234 { 235 if (ir->value) { 236 ir->value = convert_vec_index_to_cond_assign(ir->value); 237 } 238 239 return visit_continue; 240 } 241 242 ir_visitor_status 243 ir_vec_index_to_cond_assign_visitor::visit_enter(ir_if *ir) 244 { 245 ir->condition = convert_vec_index_to_cond_assign(ir->condition); 246 247 return visit_continue; 248 } 249 250 bool 251 do_vec_index_to_cond_assign(exec_list *instructions) 252 { 253 ir_vec_index_to_cond_assign_visitor v; 254 255 visit_list_elements(&v, instructions); 256 257 return v.progress; 258 } 259