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 opt_constant_folding.cpp
26  * Replace constant-valued expressions with references to constant values.
27  */
28 
29 #include "ir.h"
30 #include "ir_visitor.h"
31 #include "ir_rvalue_visitor.h"
32 #include "ir_optimization.h"
33 #include "compiler/glsl_types.h"
34 
35 namespace {
36 
37 /**
38  * Visitor class for replacing expressions with ir_constant values.
39  */
40 
41 class ir_constant_folding_visitor : public ir_rvalue_visitor {
42 public:
ir_constant_folding_visitor()43    ir_constant_folding_visitor()
44    {
45       this->progress = false;
46    }
47 
~ir_constant_folding_visitor()48    virtual ~ir_constant_folding_visitor()
49    {
50       /* empty */
51    }
52 
53    virtual ir_visitor_status visit_enter(ir_discard *ir);
54    virtual ir_visitor_status visit_enter(ir_assignment *ir);
55    virtual ir_visitor_status visit_enter(ir_call *ir);
56 
57    virtual void handle_rvalue(ir_rvalue **rvalue);
58 
59    bool progress;
60 };
61 
62 } /* unnamed namespace */
63 
64 bool
ir_constant_fold(ir_rvalue ** rvalue)65 ir_constant_fold(ir_rvalue **rvalue)
66 {
67    if (*rvalue == NULL || (*rvalue)->ir_type == ir_type_constant)
68       return false;
69 
70    /* Note that we do rvalue visitoring on leaving.  So if an
71     * expression has a non-constant operand, no need to go looking
72     * down it to find if it's constant.  This cuts the time of this
73     * pass down drastically.
74     */
75    ir_expression *expr = (*rvalue)->as_expression();
76    if (expr) {
77       for (unsigned int i = 0; i < expr->num_operands; i++) {
78 	 if (!expr->operands[i]->as_constant())
79 	    return false;
80       }
81    }
82 
83    /* Ditto for swizzles. */
84    ir_swizzle *swiz = (*rvalue)->as_swizzle();
85    if (swiz && !swiz->val->as_constant())
86       return false;
87 
88    /* Ditto for array dereferences */
89    ir_dereference_array *array_ref = (*rvalue)->as_dereference_array();
90    if (array_ref && (!array_ref->array->as_constant() ||
91                      !array_ref->array_index->as_constant()))
92       return false;
93 
94    /* No constant folding can be performed on variable dereferences.  We need
95     * to explicitly avoid them, as calling constant_expression_value() on a
96     * variable dereference will return a clone of var->constant_value.  This
97     * would make us propagate the value into the tree, which isn't our job.
98     */
99    ir_dereference_variable *var_ref = (*rvalue)->as_dereference_variable();
100    if (var_ref)
101       return false;
102 
103    ir_constant *constant =
104       (*rvalue)->constant_expression_value(ralloc_parent(*rvalue));
105    if (constant) {
106       *rvalue = constant;
107       return true;
108    }
109    return false;
110 }
111 
112 void
handle_rvalue(ir_rvalue ** rvalue)113 ir_constant_folding_visitor::handle_rvalue(ir_rvalue **rvalue)
114 {
115    if (ir_constant_fold(rvalue))
116       this->progress = true;
117 }
118 
119 ir_visitor_status
visit_enter(ir_discard * ir)120 ir_constant_folding_visitor::visit_enter(ir_discard *ir)
121 {
122    if (ir->condition) {
123       ir->condition->accept(this);
124       handle_rvalue(&ir->condition);
125 
126       ir_constant *const_val = ir->condition->as_constant();
127       /* If the condition is constant, either remove the condition or
128        * remove the never-executed assignment.
129        */
130       if (const_val) {
131          if (const_val->value.b[0])
132             ir->condition = NULL;
133          else
134             ir->remove();
135          this->progress = true;
136       }
137    }
138 
139    return visit_continue_with_parent;
140 }
141 
142 ir_visitor_status
visit_enter(ir_assignment * ir)143 ir_constant_folding_visitor::visit_enter(ir_assignment *ir)
144 {
145    ir->rhs->accept(this);
146    handle_rvalue(&ir->rhs);
147 
148    if (ir->condition) {
149       ir->condition->accept(this);
150       handle_rvalue(&ir->condition);
151 
152       ir_constant *const_val = ir->condition->as_constant();
153       /* If the condition is constant, either remove the condition or
154        * remove the never-executed assignment.
155        */
156       if (const_val) {
157 	 if (const_val->value.b[0])
158 	    ir->condition = NULL;
159 	 else
160 	    ir->remove();
161 	 this->progress = true;
162       }
163    }
164 
165    /* Don't descend into the LHS because we want it to stay as a
166     * variable dereference.  FINISHME: We probably should to get array
167     * indices though.
168     */
169    return visit_continue_with_parent;
170 }
171 
172 ir_visitor_status
visit_enter(ir_call * ir)173 ir_constant_folding_visitor::visit_enter(ir_call *ir)
174 {
175    /* Attempt to constant fold parameters */
176    foreach_two_lists(formal_node, &ir->callee->parameters,
177                      actual_node, &ir->actual_parameters) {
178       ir_rvalue *param_rval = (ir_rvalue *) actual_node;
179       ir_variable *sig_param = (ir_variable *) formal_node;
180 
181       if (sig_param->data.mode == ir_var_function_in
182           || sig_param->data.mode == ir_var_const_in) {
183 	 ir_rvalue *new_param = param_rval;
184 
185 	 handle_rvalue(&new_param);
186 	 if (new_param != param_rval) {
187 	    param_rval->replace_with(new_param);
188 	 }
189       }
190    }
191 
192    /* Next, see if the call can be replaced with an assignment of a constant */
193    ir_constant *const_val = ir->constant_expression_value(ralloc_parent(ir));
194 
195    if (const_val != NULL) {
196       ir_assignment *assignment =
197 	 new(ralloc_parent(ir)) ir_assignment(ir->return_deref, const_val);
198       ir->replace_with(assignment);
199    }
200 
201    return visit_continue_with_parent;
202 }
203 
204 bool
do_constant_folding(exec_list * instructions)205 do_constant_folding(exec_list *instructions)
206 {
207    ir_constant_folding_visitor constant_folding;
208 
209    visit_list_elements(&constant_folding, instructions);
210 
211    return constant_folding.progress;
212 }
213