• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "compiler/glsl_types.h"
39 #include "util/hash_table.h"
40 
41 namespace {
42 
43 struct assignment_entry {
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    struct hash_table *ht;
58 };
59 
60 } /* unnamed namespace */
61 
62 static struct assignment_entry *
get_assignment_entry(ir_variable * var,struct hash_table * ht)63 get_assignment_entry(ir_variable *var, struct hash_table *ht)
64 {
65    struct hash_entry *hte = _mesa_hash_table_search(ht, var);
66    struct assignment_entry *entry;
67 
68    if (hte) {
69       entry = (struct assignment_entry *) hte->data;
70    } else {
71       entry = (struct assignment_entry *) calloc(1, sizeof(*entry));
72       entry->var = var;
73       _mesa_hash_table_insert(ht, var, entry);
74    }
75 
76    return entry;
77 }
78 
79 ir_visitor_status
visit(ir_variable * ir)80 ir_constant_variable_visitor::visit(ir_variable *ir)
81 {
82    struct assignment_entry *entry = get_assignment_entry(ir, this->ht);
83    entry->our_scope = true;
84    return visit_continue;
85 }
86 
87 /* Skip derefs of variables so that we can detect declarations. */
88 ir_visitor_status
visit_enter(ir_dereference_variable * ir)89 ir_constant_variable_visitor::visit_enter(ir_dereference_variable *ir)
90 {
91    (void)ir;
92    return visit_continue_with_parent;
93 }
94 
95 ir_visitor_status
visit_enter(ir_assignment * ir)96 ir_constant_variable_visitor::visit_enter(ir_assignment *ir)
97 {
98    ir_constant *constval;
99    struct assignment_entry *entry;
100 
101    entry = get_assignment_entry(ir->lhs->variable_referenced(), this->ht);
102    assert(entry);
103    entry->assignment_count++;
104 
105    /* If there's more than one assignment, don't bother - we won't do anything
106     * with this variable anyway, and continuing just wastes memory cloning
107     * constant expressions.
108     */
109    if (entry->assignment_count > 1)
110       return visit_continue;
111 
112    /* If it's already constant, don't do the work. */
113    if (entry->var->constant_value)
114       return visit_continue;
115 
116    /* OK, now find if we actually have all the right conditions for
117     * this to be a constant value assigned to the var.
118     */
119    if (ir->condition)
120       return visit_continue;
121 
122    ir_variable *var = ir->whole_variable_written();
123    if (!var)
124       return visit_continue;
125 
126    /* Ignore buffer variables, since the underlying storage is shared
127     * and we can't be sure that this variable won't be written by another
128     * thread.
129     */
130    if (var->data.mode == ir_var_shader_storage ||
131        var->data.mode == ir_var_shader_shared)
132       return visit_continue;
133 
134    constval = ir->rhs->constant_expression_value(ralloc_parent(ir));
135    if (!constval)
136       return visit_continue;
137 
138    /* Mark this entry as having a constant assignment (if the
139     * assignment count doesn't go >1).  do_constant_variable will fix
140     * up the variable with the constant value later.
141     */
142    entry->constval = constval;
143 
144    return visit_continue;
145 }
146 
147 ir_visitor_status
visit_enter(ir_call * ir)148 ir_constant_variable_visitor::visit_enter(ir_call *ir)
149 {
150    /* Mark any out parameters as assigned to */
151    foreach_two_lists(formal_node, &ir->callee->parameters,
152                      actual_node, &ir->actual_parameters) {
153       ir_rvalue *param_rval = (ir_rvalue *) actual_node;
154       ir_variable *param = (ir_variable *) formal_node;
155 
156       if (param->data.mode == ir_var_function_out ||
157 	  param->data.mode == ir_var_function_inout) {
158 	 ir_variable *var = param_rval->variable_referenced();
159 	 struct assignment_entry *entry;
160 
161 	 assert(var);
162 	 entry = get_assignment_entry(var, this->ht);
163 	 entry->assignment_count++;
164       }
165    }
166 
167    /* Mark the return storage as having been assigned to */
168    if (ir->return_deref != NULL) {
169       ir_variable *var = ir->return_deref->variable_referenced();
170       struct assignment_entry *entry;
171 
172       assert(var);
173       entry = get_assignment_entry(var, this->ht);
174       entry->assignment_count++;
175    }
176 
177    return visit_continue;
178 }
179 
180 /**
181  * Does a copy propagation pass on the code present in the instruction stream.
182  */
183 bool
do_constant_variable(exec_list * instructions)184 do_constant_variable(exec_list *instructions)
185 {
186    bool progress = false;
187    ir_constant_variable_visitor v;
188 
189    v.ht = _mesa_hash_table_create(NULL, _mesa_hash_pointer,
190                                   _mesa_key_pointer_equal);
191    v.run(instructions);
192 
193    struct hash_entry *hte;
194    hash_table_foreach(v.ht, hte) {
195       struct assignment_entry *entry = (struct assignment_entry *) hte->data;
196 
197       if (entry->assignment_count == 1 && entry->constval && entry->our_scope) {
198 	 entry->var->constant_value = entry->constval;
199 	 progress = true;
200       }
201       hte->data = NULL;
202       free(entry);
203    }
204    _mesa_hash_table_destroy(v.ht, NULL);
205 
206    return progress;
207 }
208 
209 bool
do_constant_variable_unlinked(exec_list * instructions)210 do_constant_variable_unlinked(exec_list *instructions)
211 {
212    bool progress = false;
213 
214    foreach_in_list(ir_instruction, ir, instructions) {
215       ir_function *f = ir->as_function();
216       if (f) {
217 	 foreach_in_list(ir_function_signature, sig, &f->signatures) {
218 	    if (do_constant_variable(&sig->body))
219 	       progress = true;
220 	 }
221       }
222    }
223 
224    return progress;
225 }
226