glsl: Rename various ir_* files to lower_* and opt_*.
[mesa.git] / src / glsl / lower_if_to_cond_assign.cpp
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 ir_if_to_cond_assign.cpp
26 *
27 * This attempts to flatten all if statements to conditional
28 * assignments for GPUs that don't do control flow.
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
35 #include "glsl_types.h"
36 #include "ir.h"
37
38 class ir_if_to_cond_assign_visitor : public ir_hierarchical_visitor {
39 public:
40 ir_if_to_cond_assign_visitor()
41 {
42 this->progress = false;
43 }
44
45 ir_visitor_status visit_leave(ir_if *);
46
47 bool progress;
48 };
49
50 bool
51 do_if_to_cond_assign(exec_list *instructions)
52 {
53 ir_if_to_cond_assign_visitor v;
54
55 visit_list_elements(&v, instructions);
56
57 return v.progress;
58 }
59
60 void
61 check_control_flow(ir_instruction *ir, void *data)
62 {
63 bool *found_control_flow = (bool *)data;
64 switch (ir->ir_type) {
65 case ir_type_call:
66 case ir_type_discard:
67 case ir_type_loop:
68 case ir_type_loop_jump:
69 case ir_type_return:
70 *found_control_flow = true;
71 break;
72 default:
73 break;
74 }
75 }
76
77 void
78 move_block_to_cond_assign(void *mem_ctx,
79 ir_if *if_ir, ir_variable *cond_var, bool then)
80 {
81 exec_list *instructions;
82
83 if (then) {
84 instructions = &if_ir->then_instructions;
85 } else {
86 instructions = &if_ir->else_instructions;
87 }
88
89 foreach_iter(exec_list_iterator, iter, *instructions) {
90 ir_instruction *ir = (ir_instruction *)iter.get();
91
92 if (ir->ir_type == ir_type_assignment) {
93 ir_assignment *assign = (ir_assignment *)ir;
94 ir_rvalue *cond_expr;
95 ir_dereference *deref = new(mem_ctx) ir_dereference_variable(cond_var);
96
97 if (then) {
98 cond_expr = deref;
99 } else {
100 cond_expr = new(mem_ctx) ir_expression(ir_unop_logic_not,
101 glsl_type::bool_type,
102 deref,
103 NULL);
104 }
105
106 if (!assign->condition) {
107 assign->condition = cond_expr;
108 } else {
109 assign->condition = new(mem_ctx) ir_expression(ir_binop_logic_and,
110 glsl_type::bool_type,
111 cond_expr,
112 assign->condition);
113 }
114 }
115
116 /* Now, move from the if block to the block surrounding it. */
117 ir->remove();
118 if_ir->insert_before(ir);
119 }
120 }
121
122 ir_visitor_status
123 ir_if_to_cond_assign_visitor::visit_leave(ir_if *ir)
124 {
125 bool found_control_flow = false;
126 ir_variable *cond_var;
127 ir_assignment *assign;
128 ir_dereference_variable *deref;
129
130 /* Check that both blocks don't contain anything we can't support. */
131 foreach_iter(exec_list_iterator, then_iter, ir->then_instructions) {
132 ir_instruction *then_ir = (ir_instruction *)then_iter.get();
133 visit_tree(then_ir, check_control_flow, &found_control_flow);
134 }
135 foreach_iter(exec_list_iterator, else_iter, ir->else_instructions) {
136 ir_instruction *else_ir = (ir_instruction *)else_iter.get();
137 visit_tree(else_ir, check_control_flow, &found_control_flow);
138 }
139 if (found_control_flow)
140 return visit_continue;
141
142 void *mem_ctx = talloc_parent(ir);
143
144 /* Store the condition to a variable so the assignment conditions are
145 * simpler.
146 */
147 cond_var = new(mem_ctx) ir_variable(glsl_type::bool_type,
148 "if_to_cond_assign_condition",
149 ir_var_temporary);
150 ir->insert_before(cond_var);
151
152 deref = new(mem_ctx) ir_dereference_variable(cond_var);
153 assign = new(mem_ctx) ir_assignment(deref,
154 ir->condition, NULL);
155 ir->insert_before(assign);
156
157 /* Now, move all of the instructions out of the if blocks, putting
158 * conditions on assignments.
159 */
160 move_block_to_cond_assign(mem_ctx, ir, cond_var, true);
161 move_block_to_cond_assign(mem_ctx, ir, cond_var, false);
162
163 ir->remove();
164
165 this->progress = true;
166
167 return visit_continue;
168 }