Tizen 2.0 Release
[profile/ivi/osmesa.git] / src / glsl / lower_vec_index_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_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 = ralloc_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 = ralloc_parent(ir);
171
172    assert(orig_deref->array_index->type->base_type == GLSL_TYPE_INT);
173
174    exec_list list;
175
176    /* Store the index to a temporary to avoid reusing its tree. */
177    index = new(ir) ir_variable(glsl_type::int_type, "vec_index_tmp_i",
178                                ir_var_temporary);
179    list.push_tail(index);
180    deref = new(ir) ir_dereference_variable(index);
181    assign = new(ir) ir_assignment(deref, orig_deref->array_index, NULL);
182    list.push_tail(assign);
183
184    /* Store the RHS to a temporary to avoid reusing its tree. */
185    var = new(ir) ir_variable(ir->rhs->type, "vec_index_tmp_v",
186                              ir_var_temporary);
187    list.push_tail(var);
188    deref = new(ir) ir_dereference_variable(var);
189    assign = new(ir) ir_assignment(deref, ir->rhs, NULL);
190    list.push_tail(assign);
191
192    /* Generate a conditional move of each vector element to the temp. */
193    for (i = 0; i < orig_deref->array->type->vector_elements; i++) {
194       ir_rvalue *condition, *swizzle;
195
196       deref = new(ir) ir_dereference_variable(index);
197       condition = new(ir) ir_expression(ir_binop_equal,
198                                         glsl_type::bool_type,
199                                         deref,
200                                         new(ir) ir_constant(i));
201
202       /* Just clone the rest of the deref chain when trying to get at the
203        * underlying variable.
204        */
205       swizzle = new(ir) ir_swizzle(orig_deref->array->clone(mem_ctx, NULL),
206                                    i, 0, 0, 0, 1);
207
208       deref = new(ir) ir_dereference_variable(var);
209       assign = new(ir) ir_assignment(swizzle, deref, condition);
210       list.push_tail(assign);
211    }
212
213    /* If the original assignment has a condition, respect that original
214     * condition!  This is acomplished by wrapping the new conditional
215     * assignments in an if-statement that uses the original condition.
216     */
217    if (ir->condition != NULL) {
218       /* No need to clone the condition because the IR that it hangs on is
219        * going to be removed from the instruction sequence.
220        */
221       ir_if *if_stmt = new(mem_ctx) ir_if(ir->condition);
222
223       list.move_nodes_to(&if_stmt->then_instructions);
224       ir->insert_before(if_stmt);
225    } else {
226       ir->insert_before(&list);
227    }
228
229    ir->remove();
230
231    this->progress = true;
232
233    return visit_continue;
234 }
235
236 ir_visitor_status
237 ir_vec_index_to_cond_assign_visitor::visit_enter(ir_call *ir)
238 {
239    foreach_iter(exec_list_iterator, iter, *ir) {
240       ir_rvalue *param = (ir_rvalue *)iter.get();
241       ir_rvalue *new_param = convert_vec_index_to_cond_assign(param);
242
243       if (new_param != param) {
244          param->replace_with(new_param);
245       }
246    }
247
248    return visit_continue;
249 }
250
251 ir_visitor_status
252 ir_vec_index_to_cond_assign_visitor::visit_enter(ir_return *ir)
253 {
254    if (ir->value) {
255       ir->value = convert_vec_index_to_cond_assign(ir->value);
256    }
257
258    return visit_continue;
259 }
260
261 ir_visitor_status
262 ir_vec_index_to_cond_assign_visitor::visit_enter(ir_if *ir)
263 {
264    ir->condition = convert_vec_index_to_cond_assign(ir->condition);
265
266    return visit_continue;
267 }
268
269 bool
270 do_vec_index_to_cond_assign(exec_list *instructions)
271 {
272    ir_vec_index_to_cond_assign_visitor v;
273
274    visit_list_elements(&v, instructions);
275
276    return v.progress;
277 }