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_variable.cpp
26  *
27  * Marks variables assigned a single constant value over the course
28  * of the program as constant.
29  *
30  * The goal here is to trigger further constant folding and then dead
31  * code elimination.  This is common with vector/matrix constructors
32  * and calls to builtin functions.
33  */
34 
35 #include "ir.h"
36 #include "ir_visitor.h"
37 #include "ir_optimization.h"
38 #include "glsl_types.h"
39 
40 namespace {
41 
42 struct assignment_entry {
43    exec_node link;
44    int assignment_count;
45    ir_variable *var;
46    ir_constant *constval;
47    bool our_scope;
48 };
49 
50 class ir_constant_variable_visitor : public ir_hierarchical_visitor {
51 public:
52    virtual ir_visitor_status visit_enter(ir_dereference_variable *);
53    virtual ir_visitor_status visit(ir_variable *);
54    virtual ir_visitor_status visit_enter(ir_assignment *);
55    virtual ir_visitor_status visit_enter(ir_call *);
56 
57    exec_list list;
58 };
59 
60 } /* unnamed namespace */
61 
62 static struct assignment_entry *
get_assignment_entry(ir_variable * var,exec_list * list)63 get_assignment_entry(ir_variable *var, exec_list *list)
64 {
65    struct assignment_entry *entry;
66 
67    foreach_list_typed(struct assignment_entry, entry, link, list) {
68       if (entry->var == var)
69 	 return entry;
70    }
71 
72    entry = (struct assignment_entry *)calloc(1, sizeof(*entry));
73    entry->var = var;
74    list->push_head(&entry->link);
75    return entry;
76 }
77 
78 ir_visitor_status
visit(ir_variable * ir)79 ir_constant_variable_visitor::visit(ir_variable *ir)
80 {
81    struct assignment_entry *entry = get_assignment_entry(ir, &this->list);
82    entry->our_scope = true;
83    return visit_continue;
84 }
85 
86 /* Skip derefs of variables so that we can detect declarations. */
87 ir_visitor_status
visit_enter(ir_dereference_variable * ir)88 ir_constant_variable_visitor::visit_enter(ir_dereference_variable *ir)
89 {
90    (void)ir;
91    return visit_continue_with_parent;
92 }
93 
94 ir_visitor_status
visit_enter(ir_assignment * ir)95 ir_constant_variable_visitor::visit_enter(ir_assignment *ir)
96 {
97    ir_constant *constval;
98    struct assignment_entry *entry;
99 
100    entry = get_assignment_entry(ir->lhs->variable_referenced(), &this->list);
101    assert(entry);
102    entry->assignment_count++;
103 
104    /* If it's already constant, don't do the work. */
105    if (entry->var->constant_value)
106       return visit_continue;
107 
108    /* OK, now find if we actually have all the right conditions for
109     * this to be a constant value assigned to the var.
110     */
111    if (ir->condition)
112       return visit_continue;
113 
114    ir_variable *var = ir->whole_variable_written();
115    if (!var)
116       return visit_continue;
117 
118    constval = ir->rhs->constant_expression_value();
119    if (!constval)
120       return visit_continue;
121 
122    /* Mark this entry as having a constant assignment (if the
123     * assignment count doesn't go >1).  do_constant_variable will fix
124     * up the variable with the constant value later.
125     */
126    entry->constval = constval;
127 
128    return visit_continue;
129 }
130 
131 ir_visitor_status
visit_enter(ir_call * ir)132 ir_constant_variable_visitor::visit_enter(ir_call *ir)
133 {
134    /* Mark any out parameters as assigned to */
135    exec_list_iterator sig_iter = ir->callee->parameters.iterator();
136    foreach_iter(exec_list_iterator, iter, *ir) {
137       ir_rvalue *param_rval = (ir_rvalue *)iter.get();
138       ir_variable *param = (ir_variable *)sig_iter.get();
139 
140       if (param->mode == ir_var_out ||
141 	  param->mode == ir_var_inout) {
142 	 ir_variable *var = param_rval->variable_referenced();
143 	 struct assignment_entry *entry;
144 
145 	 assert(var);
146 	 entry = get_assignment_entry(var, &this->list);
147 	 entry->assignment_count++;
148       }
149       sig_iter.next();
150    }
151 
152    /* Mark the return storage as having been assigned to */
153    if (ir->return_deref != NULL) {
154       ir_variable *var = ir->return_deref->variable_referenced();
155       struct assignment_entry *entry;
156 
157       assert(var);
158       entry = get_assignment_entry(var, &this->list);
159       entry->assignment_count++;
160    }
161 
162    return visit_continue;
163 }
164 
165 /**
166  * Does a copy propagation pass on the code present in the instruction stream.
167  */
168 bool
do_constant_variable(exec_list * instructions)169 do_constant_variable(exec_list *instructions)
170 {
171    bool progress = false;
172    ir_constant_variable_visitor v;
173 
174    v.run(instructions);
175 
176    while (!v.list.is_empty()) {
177 
178       struct assignment_entry *entry;
179       entry = exec_node_data(struct assignment_entry, v.list.head, link);
180 
181       if (entry->assignment_count == 1 && entry->constval && entry->our_scope) {
182 	 entry->var->constant_value = entry->constval;
183 	 progress = true;
184       }
185       entry->link.remove();
186       free(entry);
187    }
188 
189    return progress;
190 }
191 
192 bool
do_constant_variable_unlinked(exec_list * instructions)193 do_constant_variable_unlinked(exec_list *instructions)
194 {
195    bool progress = false;
196 
197    foreach_iter(exec_list_iterator, iter, *instructions) {
198       ir_instruction *ir = (ir_instruction *)iter.get();
199       ir_function *f = ir->as_function();
200       if (f) {
201 	 foreach_iter(exec_list_iterator, sigiter, *f) {
202 	    ir_function_signature *sig =
203 	       (ir_function_signature *) sigiter.get();
204 	    if (do_constant_variable(&sig->body))
205 	       progress = true;
206 	 }
207       }
208    }
209 
210    return progress;
211 }
212