Merge remote branch 'origin/master' into nvc0-new
[profile/ivi/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 lower_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 }