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_copy_propagation.cpp
26 *
27 * Moves usage of recently-copied variables to the previous copy of
28 * the variable.
29 *
30 * This should reduce the number of MOV instructions in the generated
31 * programs unless copy propagation is also done on the LIR, and may
32 * help anyway by triggering other optimizations that live in the HIR.
33 */
34
35 #include "ir.h"
36 #include "ir_visitor.h"
37 #include "ir_basic_block.h"
38 #include "ir_optimization.h"
39 #include "compiler/glsl_types.h"
40 #include "util/hash_table.h"
41
42 namespace {
43
44 class kill_entry : public exec_node
45 {
46 public:
47 /* override operator new from exec_node */
48 DECLARE_LINEAR_ZALLOC_CXX_OPERATORS(kill_entry)
49
kill_entry(ir_variable * var)50 kill_entry(ir_variable *var)
51 {
52 assert(var);
53 this->var = var;
54 }
55
56 ir_variable *var;
57 };
58
59 class ir_copy_propagation_visitor : public ir_hierarchical_visitor {
60 public:
ir_copy_propagation_visitor()61 ir_copy_propagation_visitor()
62 {
63 progress = false;
64 mem_ctx = ralloc_context(0);
65 lin_ctx = linear_alloc_parent(mem_ctx, 0);
66 acp = _mesa_hash_table_create(mem_ctx, _mesa_hash_pointer,
67 _mesa_key_pointer_equal);
68 this->kills = new(mem_ctx) exec_list;
69 killed_all = false;
70 }
~ir_copy_propagation_visitor()71 ~ir_copy_propagation_visitor()
72 {
73 ralloc_free(mem_ctx);
74 }
75
76 virtual ir_visitor_status visit(class ir_dereference_variable *);
77 void handle_loop(class ir_loop *, bool keep_acp);
78 virtual ir_visitor_status visit_enter(class ir_loop *);
79 virtual ir_visitor_status visit_enter(class ir_function_signature *);
80 virtual ir_visitor_status visit_enter(class ir_function *);
81 virtual ir_visitor_status visit_leave(class ir_assignment *);
82 virtual ir_visitor_status visit_enter(class ir_call *);
83 virtual ir_visitor_status visit_enter(class ir_if *);
84
85 void add_copy(ir_assignment *ir);
86 void kill(ir_variable *ir);
87 void handle_if_block(exec_list *instructions);
88
89 /** Hash of lhs->rhs: The available copies to propagate */
90 hash_table *acp;
91 /**
92 * List of kill_entry: The variables whose values were killed in this
93 * block.
94 */
95 exec_list *kills;
96
97 bool progress;
98
99 bool killed_all;
100
101 void *mem_ctx;
102 void *lin_ctx;
103 };
104
105 } /* unnamed namespace */
106
107 ir_visitor_status
visit_enter(ir_function_signature * ir)108 ir_copy_propagation_visitor::visit_enter(ir_function_signature *ir)
109 {
110 /* Treat entry into a function signature as a completely separate
111 * block. Any instructions at global scope will be shuffled into
112 * main() at link time, so they're irrelevant to us.
113 */
114 hash_table *orig_acp = this->acp;
115 exec_list *orig_kills = this->kills;
116 bool orig_killed_all = this->killed_all;
117
118 acp = _mesa_hash_table_create(NULL, _mesa_hash_pointer,
119 _mesa_key_pointer_equal);
120 this->kills = new(mem_ctx) exec_list;
121 this->killed_all = false;
122
123 visit_list_elements(this, &ir->body);
124
125 _mesa_hash_table_destroy(acp, NULL);
126 ralloc_free(this->kills);
127
128 this->kills = orig_kills;
129 this->acp = orig_acp;
130 this->killed_all = orig_killed_all;
131
132 return visit_continue_with_parent;
133 }
134
135 ir_visitor_status
visit_leave(ir_assignment * ir)136 ir_copy_propagation_visitor::visit_leave(ir_assignment *ir)
137 {
138 kill(ir->lhs->variable_referenced());
139
140 add_copy(ir);
141
142 return visit_continue;
143 }
144
145 ir_visitor_status
visit_enter(ir_function * ir)146 ir_copy_propagation_visitor::visit_enter(ir_function *ir)
147 {
148 (void) ir;
149 return visit_continue;
150 }
151
152 /**
153 * Replaces dereferences of ACP RHS variables with ACP LHS variables.
154 *
155 * This is where the actual copy propagation occurs. Note that the
156 * rewriting of ir_dereference means that the ir_dereference instance
157 * must not be shared by multiple IR operations!
158 */
159 ir_visitor_status
visit(ir_dereference_variable * ir)160 ir_copy_propagation_visitor::visit(ir_dereference_variable *ir)
161 {
162 if (this->in_assignee)
163 return visit_continue;
164
165 struct hash_entry *entry = _mesa_hash_table_search(acp, ir->var);
166 if (entry) {
167 ir->var = (ir_variable *) entry->data;
168 progress = true;
169 }
170
171 return visit_continue;
172 }
173
174
175 ir_visitor_status
visit_enter(ir_call * ir)176 ir_copy_propagation_visitor::visit_enter(ir_call *ir)
177 {
178 /* Do copy propagation on call parameters, but skip any out params */
179 foreach_two_lists(formal_node, &ir->callee->parameters,
180 actual_node, &ir->actual_parameters) {
181 ir_variable *sig_param = (ir_variable *) formal_node;
182 ir_rvalue *ir = (ir_rvalue *) actual_node;
183 if (sig_param->data.mode != ir_var_function_out
184 && sig_param->data.mode != ir_var_function_inout) {
185 ir->accept(this);
186 }
187 }
188
189 /* Since this pass can run when unlinked, we don't (necessarily) know
190 * the side effects of calls. (When linked, most calls are inlined
191 * anyway, so it doesn't matter much.)
192 *
193 * One place where this does matter is IR intrinsics. They're never
194 * inlined. We also know what they do - while some have side effects
195 * (such as image writes), none edit random global variables. So we
196 * can assume they're side-effect free (other than the return value
197 * and out parameters).
198 */
199 if (!ir->callee->is_intrinsic()) {
200 _mesa_hash_table_clear(acp, NULL);
201 this->killed_all = true;
202 } else {
203 if (ir->return_deref)
204 kill(ir->return_deref->var);
205
206 foreach_two_lists(formal_node, &ir->callee->parameters,
207 actual_node, &ir->actual_parameters) {
208 ir_variable *sig_param = (ir_variable *) formal_node;
209 if (sig_param->data.mode == ir_var_function_out ||
210 sig_param->data.mode == ir_var_function_inout) {
211 ir_rvalue *ir = (ir_rvalue *) actual_node;
212 ir_variable *var = ir->variable_referenced();
213 kill(var);
214 }
215 }
216 }
217
218 return visit_continue_with_parent;
219 }
220
221 void
handle_if_block(exec_list * instructions)222 ir_copy_propagation_visitor::handle_if_block(exec_list *instructions)
223 {
224 hash_table *orig_acp = this->acp;
225 exec_list *orig_kills = this->kills;
226 bool orig_killed_all = this->killed_all;
227
228 acp = _mesa_hash_table_create(NULL, _mesa_hash_pointer,
229 _mesa_key_pointer_equal);
230 this->kills = new(mem_ctx) exec_list;
231 this->killed_all = false;
232
233 /* Populate the initial acp with a copy of the original */
234 struct hash_entry *entry;
235 hash_table_foreach(orig_acp, entry) {
236 _mesa_hash_table_insert(acp, entry->key, entry->data);
237 }
238
239 visit_list_elements(this, instructions);
240
241 if (this->killed_all) {
242 _mesa_hash_table_clear(orig_acp, NULL);
243 }
244
245 exec_list *new_kills = this->kills;
246 this->kills = orig_kills;
247 _mesa_hash_table_destroy(acp, NULL);
248 this->acp = orig_acp;
249 this->killed_all = this->killed_all || orig_killed_all;
250
251 foreach_in_list(kill_entry, k, new_kills) {
252 kill(k->var);
253 }
254
255 ralloc_free(new_kills);
256 }
257
258 ir_visitor_status
visit_enter(ir_if * ir)259 ir_copy_propagation_visitor::visit_enter(ir_if *ir)
260 {
261 ir->condition->accept(this);
262
263 handle_if_block(&ir->then_instructions);
264 handle_if_block(&ir->else_instructions);
265
266 /* handle_if_block() already descended into the children. */
267 return visit_continue_with_parent;
268 }
269
270 void
handle_loop(ir_loop * ir,bool keep_acp)271 ir_copy_propagation_visitor::handle_loop(ir_loop *ir, bool keep_acp)
272 {
273 hash_table *orig_acp = this->acp;
274 exec_list *orig_kills = this->kills;
275 bool orig_killed_all = this->killed_all;
276
277 acp = _mesa_hash_table_create(NULL, _mesa_hash_pointer,
278 _mesa_key_pointer_equal);
279 this->kills = new(mem_ctx) exec_list;
280 this->killed_all = false;
281
282 if (keep_acp) {
283 struct hash_entry *entry;
284 hash_table_foreach(orig_acp, entry) {
285 _mesa_hash_table_insert(acp, entry->key, entry->data);
286 }
287 }
288
289 visit_list_elements(this, &ir->body_instructions);
290
291 if (this->killed_all) {
292 _mesa_hash_table_clear(orig_acp, NULL);
293 }
294
295 exec_list *new_kills = this->kills;
296 this->kills = orig_kills;
297 _mesa_hash_table_destroy(acp, NULL);
298 this->acp = orig_acp;
299 this->killed_all = this->killed_all || orig_killed_all;
300
301 foreach_in_list(kill_entry, k, new_kills) {
302 kill(k->var);
303 }
304
305 ralloc_free(new_kills);
306 }
307
308 ir_visitor_status
visit_enter(ir_loop * ir)309 ir_copy_propagation_visitor::visit_enter(ir_loop *ir)
310 {
311 /* Make a conservative first pass over the loop with an empty ACP set.
312 * This also removes any killed entries from the original ACP set.
313 */
314 handle_loop(ir, false);
315
316 /* Then, run it again with the real ACP set, minus any killed entries.
317 * This takes care of propagating values from before the loop into it.
318 */
319 handle_loop(ir, true);
320
321 /* already descended into the children. */
322 return visit_continue_with_parent;
323 }
324
325 void
kill(ir_variable * var)326 ir_copy_propagation_visitor::kill(ir_variable *var)
327 {
328 assert(var != NULL);
329
330 /* Remove any entries currently in the ACP for this kill. */
331 struct hash_entry *entry = _mesa_hash_table_search(acp, var);
332 if (entry) {
333 _mesa_hash_table_remove(acp, entry);
334 }
335
336 hash_table_foreach(acp, entry) {
337 if (var == (ir_variable *) entry->data) {
338 _mesa_hash_table_remove(acp, entry);
339 }
340 }
341
342 /* Add the LHS variable to the list of killed variables in this block.
343 */
344 this->kills->push_tail(new(this->lin_ctx) kill_entry(var));
345 }
346
347 /**
348 * Adds an entry to the available copy list if it's a plain assignment
349 * of a variable to a variable.
350 */
351 void
add_copy(ir_assignment * ir)352 ir_copy_propagation_visitor::add_copy(ir_assignment *ir)
353 {
354 if (ir->condition)
355 return;
356
357 ir_variable *lhs_var = ir->whole_variable_written();
358 ir_variable *rhs_var = ir->rhs->whole_variable_referenced();
359
360 if ((lhs_var != NULL) && (rhs_var != NULL)) {
361 if (lhs_var == rhs_var) {
362 /* This is a dumb assignment, but we've conveniently noticed
363 * it here. Removing it now would mess up the loop iteration
364 * calling us. Just flag it to not execute, and someone else
365 * will clean up the mess.
366 */
367 ir->condition = new(ralloc_parent(ir)) ir_constant(false);
368 this->progress = true;
369 } else if (lhs_var->data.mode != ir_var_shader_storage &&
370 lhs_var->data.mode != ir_var_shader_shared &&
371 lhs_var->data.precise == rhs_var->data.precise) {
372 assert(lhs_var);
373 assert(rhs_var);
374 _mesa_hash_table_insert(acp, lhs_var, rhs_var);
375 }
376 }
377 }
378
379 /**
380 * Does a copy propagation pass on the code present in the instruction stream.
381 */
382 bool
do_copy_propagation(exec_list * instructions)383 do_copy_propagation(exec_list *instructions)
384 {
385 ir_copy_propagation_visitor v;
386
387 visit_list_elements(&v, instructions);
388
389 return v.progress;
390 }
391