Implement "fract" builtin.
[mesa.git] / ir_dead_code_local.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 ir_dead_code_local.cpp
26 *
27 * Eliminates local dead assignments from the code.
28 *
29 * This operates on basic blocks, tracking assignments and finding if
30 * they're used before the variable is completely reassigned.
31 *
32 * Compare this to ir_dead_code.cpp, which operates globally looking
33 * for assignments to variables that are never read.
34 */
35
36 #include <stdio.h>
37 #include "ir.h"
38 #include "ir_visitor.h"
39 #include "ir_print_visitor.h"
40 #include "ir_basic_block.h"
41 #include "ir_visit_tree.h"
42 #include "ir_optimization.h"
43 #include "glsl_types.h"
44
45 static bool debug = false;
46
47 class assignment_entry : public exec_node
48 {
49 public:
50 assignment_entry(ir_variable *lhs, ir_instruction *ir)
51 {
52 assert(lhs);
53 assert(ir);
54 this->lhs = lhs;
55 this->ir = ir;
56 }
57
58 ir_variable *lhs;
59 ir_instruction *ir;
60 };
61
62 static void
63 ir_kill_for_derefs_callback(ir_instruction *ir, void *data)
64 {
65 exec_list *assignments = (exec_list *)data;
66 ir_variable *var = ir->as_variable();
67
68 if (!var)
69 return;
70
71 foreach_iter(exec_list_iterator, iter, *assignments) {
72 assignment_entry *entry = (assignment_entry *)iter.get();
73
74 if (entry->lhs == var) {
75 if (debug)
76 printf("kill %s\n", entry->lhs->name);
77 entry->remove();
78 }
79 }
80 }
81
82 static void
83 kill_for_derefs(ir_instruction *ir, exec_list *assignments)
84 {
85 ir_visit_tree(ir, ir_kill_for_derefs_callback, assignments);
86 }
87
88 /**
89 * Adds an entry to the available copy list if it's a plain assignment
90 * of a variable to a variable.
91 */
92 static bool
93 process_assignment(ir_assignment *ir, exec_list *assignments)
94 {
95 ir_variable *var = NULL;
96 bool progress = false;
97 ir_instruction *current;
98
99 /* Kill assignment entries for things used to produce this assignment. */
100 kill_for_derefs(ir->rhs, assignments);
101 if (ir->condition) {
102 kill_for_derefs(ir->condition, assignments);
103 }
104
105 /* Walk down the dereference chain to find the variable at the end
106 * of it that we're actually modifying. Kill assignment enties used as
107 * array indices, too.
108 */
109 for (current = ir->lhs; current != NULL;) {
110 ir_swizzle *swiz;
111 ir_dereference *deref;
112
113 if ((swiz = current->as_swizzle())) {
114 current = swiz->val;
115 } else if ((deref = current->as_dereference())) {
116 if (deref->mode == ir_dereference::ir_reference_array)
117 kill_for_derefs(deref->selector.array_index, assignments);
118 current = deref->var;
119 } else {
120 var = current->as_variable();
121
122 current = NULL;
123 break;
124 }
125 }
126
127 assert(var);
128
129 bool always_assign = true;
130 if (ir->condition) {
131 ir_constant *condition = ir->condition->as_constant();
132 if (!condition || !condition->value.b[0])
133 always_assign = false;
134 }
135
136 /* Now, check if we did a whole-variable assignment. */
137 ir_dereference *lhs_deref = ir->lhs->as_dereference();
138 if (always_assign &&
139 lhs_deref &&
140 lhs_deref->mode == ir_dereference::ir_reference_variable) {
141 /* We did a whole-variable assignment. So, any instruction in
142 * the assignment list with the same LHS is dead.
143 */
144 if (debug)
145 printf("looking for %s to remove\n", var->name);
146 foreach_iter(exec_list_iterator, iter, *assignments) {
147 assignment_entry *entry = (assignment_entry *)iter.get();
148
149 if (entry->lhs == var) {
150 if (debug)
151 printf("removing %s\n", var->name);
152 entry->ir->remove();
153 entry->remove();
154 progress = true;
155 }
156 }
157 }
158
159 /* Add this instruction to the assignment list. */
160 assignment_entry *entry = new assignment_entry(var, ir);
161 assignments->push_tail(entry);
162
163 if (debug) {
164 printf("add %s\n", var->name);
165
166 printf("current entries\n");
167 foreach_iter(exec_list_iterator, iter, *assignments) {
168 assignment_entry *entry = (assignment_entry *)iter.get();
169
170 printf(" %s\n", entry->lhs->name);
171 }
172 }
173
174 return progress;
175 }
176
177 static void
178 dead_code_local_basic_block(ir_instruction *first,
179 ir_instruction *last,
180 void *data)
181 {
182 ir_instruction *ir, *ir_next;
183 /* List of avaialble_copy */
184 exec_list assignments;
185 bool *out_progress = (bool *)data;
186 bool progress = false;
187
188 /* Safe looping, since process_assignment */
189 for (ir = first, ir_next = (ir_instruction *)first->next;;
190 ir = ir_next, ir_next = (ir_instruction *)ir->next) {
191 ir_assignment *ir_assign = ir->as_assignment();
192
193 if (debug) {
194 ir_print_visitor v;
195 ir->accept(&v);
196 printf("\n");
197 }
198
199 if (ir_assign) {
200 progress = process_assignment(ir_assign, &assignments) || progress;
201 } else {
202 kill_for_derefs(ir, &assignments);
203 }
204
205 if (ir == last)
206 break;
207 }
208 *out_progress = progress;
209 }
210
211 /**
212 * Does a copy propagation pass on the code present in the instruction stream.
213 */
214 bool
215 do_dead_code_local(exec_list *instructions)
216 {
217 bool progress = false;
218
219 call_for_basic_blocks(instructions, dead_code_local_basic_block, &progress);
220
221 return progress;
222 }