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 "compiler/glsl_types.h" 43 44 namespace { 45 46 /** 47 * Visitor class for replacing expressions with ir_constant values. 48 */ 49 50 class ir_vec_index_to_cond_assign_visitor : public ir_hierarchical_visitor { 51 public: 52 ir_vec_index_to_cond_assign_visitor() 53 { 54 progress = false; 55 } 56 57 ir_rvalue *convert_vec_index_to_cond_assign(void *mem_ctx, 58 ir_rvalue *orig_vector, 59 ir_rvalue *orig_index, 60 const glsl_type *type); 61 62 ir_rvalue *convert_vector_extract_to_cond_assign(ir_rvalue *ir); 63 64 virtual ir_visitor_status visit_enter(ir_expression *); 65 virtual ir_visitor_status visit_enter(ir_swizzle *); 66 virtual ir_visitor_status visit_leave(ir_assignment *); 67 virtual ir_visitor_status visit_enter(ir_return *); 68 virtual ir_visitor_status visit_enter(ir_call *); 69 virtual ir_visitor_status visit_enter(ir_if *); 70 71 bool progress; 72 }; 73 74 } /* anonymous namespace */ 75 76 ir_rvalue * 77 ir_vec_index_to_cond_assign_visitor::convert_vec_index_to_cond_assign(void *mem_ctx, 78 ir_rvalue *orig_vector, 79 ir_rvalue *orig_index, 80 const glsl_type *type) 81 { 82 ir_assignment *assign, *value_assign; 83 ir_variable *index, *var, *value; 84 ir_dereference *deref, *deref_value; 85 unsigned i; 86 87 88 exec_list list; 89 90 /* Store the index to a temporary to avoid reusing its tree. */ 91 assert(orig_index->type == glsl_type::int_type || 92 orig_index->type == glsl_type::uint_type); 93 index = new(base_ir) ir_variable(orig_index->type, 94 "vec_index_tmp_i", 95 ir_var_temporary); 96 list.push_tail(index); 97 deref = new(base_ir) ir_dereference_variable(index); 98 assign = new(base_ir) ir_assignment(deref, orig_index, NULL); 99 list.push_tail(assign); 100 101 /* Store the value inside a temp, thus avoiding matrixes duplication */ 102 value = new(base_ir) ir_variable(orig_vector->type, "vec_value_tmp", 103 ir_var_temporary); 104 list.push_tail(value); 105 deref_value = new(base_ir) ir_dereference_variable(value); 106 value_assign = new(base_ir) ir_assignment(deref_value, orig_vector); 107 list.push_tail(value_assign); 108 109 /* Temporary where we store whichever value we swizzle out. */ 110 var = new(base_ir) ir_variable(type, "vec_index_tmp_v", 111 ir_var_temporary); 112 list.push_tail(var); 113 114 /* Generate a single comparison condition "mask" for all of the components 115 * in the vector. 116 */ 117 ir_rvalue *const cond_deref = 118 compare_index_block(&list, index, 0, 119 orig_vector->type->vector_elements, 120 mem_ctx); 121 122 /* Generate a conditional move of each vector element to the temp. */ 123 for (i = 0; i < orig_vector->type->vector_elements; i++) { 124 ir_rvalue *condition_swizzle = 125 new(base_ir) ir_swizzle(cond_deref->clone(mem_ctx, NULL), 126 i, 0, 0, 0, 1); 127 128 /* Just clone the rest of the deref chain when trying to get at the 129 * underlying variable. 130 */ 131 ir_rvalue *swizzle = 132 new(base_ir) ir_swizzle(deref_value->clone(mem_ctx, NULL), 133 i, 0, 0, 0, 1); 134 135 deref = new(base_ir) ir_dereference_variable(var); 136 assign = new(base_ir) ir_assignment(deref, swizzle, condition_swizzle); 137 list.push_tail(assign); 138 } 139 140 /* Put all of the new instructions in the IR stream before the old 141 * instruction. 142 */ 143 base_ir->insert_before(&list); 144 145 this->progress = true; 146 return new(base_ir) ir_dereference_variable(var); 147 } 148 149 ir_rvalue * 150 ir_vec_index_to_cond_assign_visitor::convert_vector_extract_to_cond_assign(ir_rvalue *ir) 151 { 152 ir_expression *const expr = ir->as_expression(); 153 154 if (expr == NULL || expr->operation != ir_binop_vector_extract) 155 return ir; 156 157 return convert_vec_index_to_cond_assign(ralloc_parent(ir), 158 expr->operands[0], 159 expr->operands[1], 160 ir->type); 161 } 162 163 ir_visitor_status 164 ir_vec_index_to_cond_assign_visitor::visit_enter(ir_expression *ir) 165 { 166 unsigned int i; 167 168 for (i = 0; i < ir->get_num_operands(); i++) { 169 ir->operands[i] = convert_vector_extract_to_cond_assign(ir->operands[i]); 170 } 171 172 return visit_continue; 173 } 174 175 ir_visitor_status 176 ir_vec_index_to_cond_assign_visitor::visit_enter(ir_swizzle *ir) 177 { 178 /* Can't be hit from normal GLSL, since you can't swizzle a scalar (which 179 * the result of indexing a vector is. But maybe at some point we'll end up 180 * using swizzling of scalars for vector construction. 181 */ 182 ir->val = convert_vector_extract_to_cond_assign(ir->val); 183 184 return visit_continue; 185 } 186 187 ir_visitor_status 188 ir_vec_index_to_cond_assign_visitor::visit_leave(ir_assignment *ir) 189 { 190 ir->rhs = convert_vector_extract_to_cond_assign(ir->rhs); 191 192 if (ir->condition) { 193 ir->condition = convert_vector_extract_to_cond_assign(ir->condition); 194 } 195 196 return visit_continue; 197 } 198 199 ir_visitor_status 200 ir_vec_index_to_cond_assign_visitor::visit_enter(ir_call *ir) 201 { 202 foreach_in_list_safe(ir_rvalue, param, &ir->actual_parameters) { 203 ir_rvalue *new_param = convert_vector_extract_to_cond_assign(param); 204 205 if (new_param != param) { 206 param->replace_with(new_param); 207 } 208 } 209 210 return visit_continue; 211 } 212 213 ir_visitor_status 214 ir_vec_index_to_cond_assign_visitor::visit_enter(ir_return *ir) 215 { 216 if (ir->value) { 217 ir->value = convert_vector_extract_to_cond_assign(ir->value); 218 } 219 220 return visit_continue; 221 } 222 223 ir_visitor_status 224 ir_vec_index_to_cond_assign_visitor::visit_enter(ir_if *ir) 225 { 226 ir->condition = convert_vector_extract_to_cond_assign(ir->condition); 227 228 return visit_continue; 229 } 230 231 bool 232 do_vec_index_to_cond_assign(exec_list *instructions) 233 { 234 ir_vec_index_to_cond_assign_visitor v; 235 236 visit_list_elements(&v, instructions); 237 238 return v.progress; 239 } 240