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_if_to_cond_assign.cpp 26 * 27 * This attempts to flatten if-statements to conditional assignments for 28 * GPUs with limited or no flow control support. 29 * 30 * It can't handle other control flow being inside of its block, such 31 * as calls or loops. Hopefully loop unrolling and inlining will take 32 * care of those. 33 * 34 * Drivers for GPUs with no control flow support should simply call 35 * 36 * lower_if_to_cond_assign(instructions) 37 * 38 * to attempt to flatten all if-statements. 39 * 40 * Some GPUs (such as i965 prior to gen6) do support control flow, but have a 41 * maximum nesting depth N. Drivers for such hardware can call 42 * 43 * lower_if_to_cond_assign(instructions, N) 44 * 45 * to attempt to flatten any if-statements appearing at depth > N. 46 */ 47 48 #include "glsl_types.h" 49 #include "ir.h" 50 #include "program/hash_table.h" 51 52 class ir_if_to_cond_assign_visitor : public ir_hierarchical_visitor { 53 public: 54 ir_if_to_cond_assign_visitor(unsigned max_depth) 55 { 56 this->progress = false; 57 this->max_depth = max_depth; 58 this->depth = 0; 59 60 this->condition_variables = hash_table_ctor(0, hash_table_pointer_hash, 61 hash_table_pointer_compare); 62 } 63 64 ~ir_if_to_cond_assign_visitor() 65 { 66 hash_table_dtor(this->condition_variables); 67 } 68 69 ir_visitor_status visit_enter(ir_if *); 70 ir_visitor_status visit_leave(ir_if *); 71 72 bool progress; 73 unsigned max_depth; 74 unsigned depth; 75 76 struct hash_table *condition_variables; 77 }; 78 79 bool 80 lower_if_to_cond_assign(exec_list *instructions, unsigned max_depth) 81 { 82 if (max_depth == UINT_MAX) 83 return false; 84 85 ir_if_to_cond_assign_visitor v(max_depth); 86 87 visit_list_elements(&v, instructions); 88 89 return v.progress; 90 } 91 92 void 93 check_control_flow(ir_instruction *ir, void *data) 94 { 95 bool *found_control_flow = (bool *)data; 96 switch (ir->ir_type) { 97 case ir_type_call: 98 case ir_type_discard: 99 case ir_type_loop: 100 case ir_type_loop_jump: 101 case ir_type_return: 102 *found_control_flow = true; 103 break; 104 default: 105 break; 106 } 107 } 108 109 void 110 move_block_to_cond_assign(void *mem_ctx, 111 ir_if *if_ir, ir_rvalue *cond_expr, 112 exec_list *instructions, 113 struct hash_table *ht) 114 { 115 foreach_list_safe(node, instructions) { 116 ir_instruction *ir = (ir_instruction *) node; 117 118 if (ir->ir_type == ir_type_assignment) { 119 ir_assignment *assign = (ir_assignment *)ir; 120 121 if (hash_table_find(ht, assign) == NULL) { 122 hash_table_insert(ht, assign, assign); 123 124 /* If the LHS of the assignment is a condition variable that was 125 * previously added, insert an additional assignment of false to 126 * the variable. 127 */ 128 const bool assign_to_cv = 129 hash_table_find(ht, assign->lhs->variable_referenced()) != NULL; 130 131 if (!assign->condition) { 132 if (assign_to_cv) { 133 assign->rhs = 134 new(mem_ctx) ir_expression(ir_binop_logic_and, 135 glsl_type::bool_type, 136 cond_expr->clone(mem_ctx, NULL), 137 assign->rhs); 138 } else { 139 assign->condition = cond_expr->clone(mem_ctx, NULL); 140 } 141 } else { 142 assign->condition = 143 new(mem_ctx) ir_expression(ir_binop_logic_and, 144 glsl_type::bool_type, 145 cond_expr->clone(mem_ctx, NULL), 146 assign->condition); 147 } 148 } 149 } 150 151 /* Now, move from the if block to the block surrounding it. */ 152 ir->remove(); 153 if_ir->insert_before(ir); 154 } 155 } 156 157 ir_visitor_status 158 ir_if_to_cond_assign_visitor::visit_enter(ir_if *ir) 159 { 160 (void) ir; 161 this->depth++; 162 163 return visit_continue; 164 } 165 166 ir_visitor_status 167 ir_if_to_cond_assign_visitor::visit_leave(ir_if *ir) 168 { 169 /* Only flatten when beyond the GPU's maximum supported nesting depth. */ 170 if (this->depth-- <= this->max_depth) 171 return visit_continue; 172 173 bool found_control_flow = false; 174 ir_assignment *assign; 175 176 /* Check that both blocks don't contain anything we can't support. */ 177 foreach_iter(exec_list_iterator, then_iter, ir->then_instructions) { 178 ir_instruction *then_ir = (ir_instruction *)then_iter.get(); 179 visit_tree(then_ir, check_control_flow, &found_control_flow); 180 } 181 foreach_iter(exec_list_iterator, else_iter, ir->else_instructions) { 182 ir_instruction *else_ir = (ir_instruction *)else_iter.get(); 183 visit_tree(else_ir, check_control_flow, &found_control_flow); 184 } 185 if (found_control_flow) 186 return visit_continue; 187 188 void *mem_ctx = ralloc_parent(ir); 189 190 /* Store the condition to a variable. Move all of the instructions from 191 * the then-clause of the if-statement. Use the condition variable as a 192 * condition for all assignments. 193 */ 194 ir_variable *const then_var = 195 new(mem_ctx) ir_variable(glsl_type::bool_type, 196 "if_to_cond_assign_then", 197 ir_var_temporary); 198 ir->insert_before(then_var); 199 200 ir_dereference_variable *then_cond = 201 new(mem_ctx) ir_dereference_variable(then_var); 202 203 assign = new(mem_ctx) ir_assignment(then_cond, ir->condition); 204 ir->insert_before(assign); 205 206 move_block_to_cond_assign(mem_ctx, ir, then_cond, 207 &ir->then_instructions, 208 this->condition_variables); 209 210 /* Add the new condition variable to the hash table. This allows us to 211 * find this variable when lowering other (enclosing) if-statements. 212 */ 213 hash_table_insert(this->condition_variables, then_var, then_var); 214 215 /* If there are instructions in the else-clause, store the inverse of the 216 * condition to a variable. Move all of the instructions from the 217 * else-clause if the if-statement. Use the (inverse) condition variable 218 * as a condition for all assignments. 219 */ 220 if (!ir->else_instructions.is_empty()) { 221 ir_variable *const else_var = 222 new(mem_ctx) ir_variable(glsl_type::bool_type, 223 "if_to_cond_assign_else", 224 ir_var_temporary); 225 ir->insert_before(else_var); 226 227 ir_dereference_variable *else_cond = 228 new(mem_ctx) ir_dereference_variable(else_var); 229 230 ir_rvalue *inverse = 231 new(mem_ctx) ir_expression(ir_unop_logic_not, 232 then_cond->clone(mem_ctx, NULL)); 233 234 assign = new(mem_ctx) ir_assignment(else_cond, inverse); 235 ir->insert_before(assign); 236 237 move_block_to_cond_assign(mem_ctx, ir, else_cond, 238 &ir->else_instructions, 239 this->condition_variables); 240 241 /* Add the new condition variable to the hash table. This allows us to 242 * find this variable when lowering other (enclosing) if-statements. 243 */ 244 hash_table_insert(this->condition_variables, else_var, else_var); 245 } 246 247 ir->remove(); 248 249 this->progress = true; 250 251 return visit_continue; 252 } 253