2 * Constantright © 2010 Intel Corporation
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * constant 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, constant, 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:
11 * The above constantright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
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 CONSTANTRIGHT 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.
25 * \file ir_constant_propagation.cpp
27 * Tracks assignments of constants to channels of variables, and
28 * usage of those constant channels with direct usage of the constants.
30 * This can lead to constant folding and algebraic optimizations in
31 * those later expressions, while causing no increase in instruction
32 * count (due to constants being generally free to load from a
33 * constant push buffer or as instruction immediate values) and
34 * possibly reducing register pressure.
38 #include "ir_visitor.h"
39 #include "ir_basic_block.h"
40 #include "ir_optimization.h"
41 #include "glsl_types.h"
43 class acp_entry
: public exec_node
46 acp_entry(ir_variable
*var
, unsigned write_mask
, ir_constant
*constant
)
51 this->write_mask
= write_mask
;
52 this->constant
= constant
;
56 ir_constant
*constant
;
61 class kill_entry
: public exec_node
64 kill_entry(ir_variable
*var
, unsigned write_mask
)
68 this->write_mask
= write_mask
;
75 class ir_constant_propagation_visitor
: public ir_hierarchical_visitor
{
77 ir_constant_propagation_visitor()
80 mem_ctx
= talloc_new(0);
81 this->acp
= new(mem_ctx
) exec_list
;
82 this->kills
= new(mem_ctx
) exec_list
;
84 ~ir_constant_propagation_visitor()
89 virtual ir_visitor_status
visit_enter(class ir_loop
*);
90 virtual ir_visitor_status
visit_enter(class ir_function_signature
*);
91 virtual ir_visitor_status
visit_enter(class ir_function
*);
92 virtual ir_visitor_status
visit_enter(class ir_assignment
*);
93 virtual ir_visitor_status
visit_leave(class ir_assignment
*);
94 virtual ir_visitor_status
visit_enter(class ir_expression
*);
95 virtual ir_visitor_status
visit_enter(class ir_call
*);
96 virtual ir_visitor_status
visit_enter(class ir_if
*);
97 virtual ir_visitor_status
visit_enter(class ir_dereference_array
*);
98 virtual ir_visitor_status
visit_enter(class ir_texture
*);
100 void add_constant(ir_assignment
*ir
);
101 void kill(ir_variable
*ir
, unsigned write_mask
);
102 void handle_if_block(exec_list
*instructions
);
103 void handle_rvalue(ir_rvalue
**rvalue
);
105 /** List of acp_entry: The available constants to propagate */
109 * List of kill_entry: The masks of variables whose values were
110 * killed in this block.
123 ir_constant_propagation_visitor::handle_rvalue(ir_rvalue
**rvalue
)
128 const glsl_type
*type
= (*rvalue
)->type
;
129 if (!type
->is_scalar() && !type
->is_vector())
132 ir_swizzle
*swiz
= NULL
;
133 ir_dereference_variable
*deref
= (*rvalue
)->as_dereference_variable();
135 swiz
= (*rvalue
)->as_swizzle();
139 deref
= swiz
->val
->as_dereference_variable();
144 ir_constant_data data
;
145 memset(&data
, 0, sizeof(data
));
147 for (unsigned int i
= 0; i
< type
->components(); i
++) {
149 acp_entry
*found
= NULL
;
153 case 0: channel
= swiz
->mask
.x
; break;
154 case 1: channel
= swiz
->mask
.y
; break;
155 case 2: channel
= swiz
->mask
.z
; break;
156 case 3: channel
= swiz
->mask
.w
; break;
157 default: assert(!"shouldn't be reached"); channel
= 0; break;
163 foreach_iter(exec_list_iterator
, iter
, *this->acp
) {
164 acp_entry
*entry
= (acp_entry
*)iter
.get();
165 if (entry
->var
== deref
->var
&& entry
->write_mask
& (1 << channel
)) {
174 switch (type
->base_type
) {
175 case GLSL_TYPE_FLOAT
:
176 data
.f
[i
] = found
->constant
->value
.f
[channel
];
179 data
.i
[i
] = found
->constant
->value
.i
[channel
];
182 data
.u
[i
] = found
->constant
->value
.u
[channel
];
185 data
.b
[i
] = found
->constant
->value
.b
[channel
];
188 assert(!"not reached");
193 *rvalue
= new(talloc_parent(deref
)) ir_constant(type
, &data
);
194 this->progress
= true;
198 ir_constant_propagation_visitor::visit_enter(ir_function_signature
*ir
)
200 /* Treat entry into a function signature as a completely separate
201 * block. Any instructions at global scope will be shuffled into
202 * main() at link time, so they're irrelevant to us.
204 exec_list
*orig_acp
= this->acp
;
205 exec_list
*orig_kills
= this->kills
;
206 bool orig_killed_all
= this->killed_all
;
208 this->acp
= new(mem_ctx
) exec_list
;
209 this->kills
= new(mem_ctx
) exec_list
;
210 this->killed_all
= false;
212 visit_list_elements(this, &ir
->body
);
214 this->kills
= orig_kills
;
215 this->acp
= orig_acp
;
216 this->killed_all
= orig_killed_all
;
218 return visit_continue_with_parent
;
222 ir_constant_propagation_visitor::visit_enter(ir_assignment
*ir
)
224 handle_rvalue(&ir
->condition
);
225 handle_rvalue(&ir
->rhs
);
227 return visit_continue
;
231 ir_constant_propagation_visitor::visit_leave(ir_assignment
*ir
)
233 kill(ir
->lhs
->variable_referenced(), ir
->write_mask
);
237 return visit_continue
;
241 ir_constant_propagation_visitor::visit_enter(ir_expression
*ir
)
243 for (unsigned int i
= 0; i
< ir
->get_num_operands(); i
++) {
244 handle_rvalue(&ir
->operands
[i
]);
247 return visit_continue
;
251 ir_constant_propagation_visitor::visit_enter(ir_function
*ir
)
254 return visit_continue
;
258 ir_constant_propagation_visitor::visit_enter(ir_call
*ir
)
260 /* Do constant propagation on call parameters, but skip any out params */
261 exec_list_iterator sig_param_iter
= ir
->get_callee()->parameters
.iterator();
262 foreach_iter(exec_list_iterator
, iter
, ir
->actual_parameters
) {
263 ir_variable
*sig_param
= (ir_variable
*)sig_param_iter
.get();
264 ir_rvalue
*param
= (ir_rvalue
*)iter
.get();
265 if (sig_param
->mode
!= ir_var_out
&& sig_param
->mode
!= ir_var_inout
) {
266 ir_rvalue
*new_param
= param
;
267 handle_rvalue(&new_param
);
268 if (new_param
!= param
)
269 param
->replace_with(new_param
);
273 sig_param_iter
.next();
276 /* Since we're unlinked, we don't (necssarily) know the side effects of
277 * this call. So kill all copies.
280 this->killed_all
= true;
282 return visit_continue_with_parent
;
286 ir_constant_propagation_visitor::handle_if_block(exec_list
*instructions
)
288 exec_list
*orig_acp
= this->acp
;
289 exec_list
*orig_kills
= this->kills
;
290 bool orig_killed_all
= this->killed_all
;
292 this->acp
= new(mem_ctx
) exec_list
;
293 this->kills
= new(mem_ctx
) exec_list
;
294 this->killed_all
= false;
296 /* Populate the initial acp with a constant of the original */
297 foreach_iter(exec_list_iterator
, iter
, *orig_acp
) {
298 acp_entry
*a
= (acp_entry
*)iter
.get();
299 this->acp
->push_tail(new(this->mem_ctx
) acp_entry(a
->var
, a
->write_mask
,
303 visit_list_elements(this, instructions
);
305 if (this->killed_all
) {
306 orig_acp
->make_empty();
309 exec_list
*new_kills
= this->kills
;
310 this->kills
= orig_kills
;
311 this->acp
= orig_acp
;
312 this->killed_all
= this->killed_all
|| orig_killed_all
;
314 foreach_iter(exec_list_iterator
, iter
, *new_kills
) {
315 kill_entry
*k
= (kill_entry
*)iter
.get();
316 kill(k
->var
, k
->write_mask
);
321 ir_constant_propagation_visitor::visit_enter(ir_if
*ir
)
323 ir
->condition
->accept(this);
324 handle_rvalue(&ir
->condition
);
326 handle_if_block(&ir
->then_instructions
);
327 handle_if_block(&ir
->else_instructions
);
329 /* handle_if_block() already descended into the children. */
330 return visit_continue_with_parent
;
334 ir_constant_propagation_visitor::visit_enter(ir_dereference_array
*ir
)
336 handle_rvalue(&ir
->array_index
);
337 return visit_continue
;
341 ir_constant_propagation_visitor::visit_enter(ir_texture
*ir
)
343 handle_rvalue(&ir
->coordinate
);
344 handle_rvalue(&ir
->projector
);
345 handle_rvalue(&ir
->shadow_comparitor
);
351 handle_rvalue(&ir
->lod_info
.bias
);
355 handle_rvalue(&ir
->lod_info
.lod
);
358 handle_rvalue(&ir
->lod_info
.grad
.dPdx
);
359 handle_rvalue(&ir
->lod_info
.grad
.dPdy
);
363 return visit_continue
;
367 ir_constant_propagation_visitor::visit_enter(ir_loop
*ir
)
369 exec_list
*orig_acp
= this->acp
;
370 exec_list
*orig_kills
= this->kills
;
371 bool orig_killed_all
= this->killed_all
;
373 /* FINISHME: For now, the initial acp for loops is totally empty.
374 * We could go through once, then go through again with the acp
375 * cloned minus the killed entries after the first run through.
377 this->acp
= new(mem_ctx
) exec_list
;
378 this->kills
= new(mem_ctx
) exec_list
;
379 this->killed_all
= false;
381 visit_list_elements(this, &ir
->body_instructions
);
383 if (this->killed_all
) {
384 orig_acp
->make_empty();
387 exec_list
*new_kills
= this->kills
;
388 this->kills
= orig_kills
;
389 this->acp
= orig_acp
;
390 this->killed_all
= this->killed_all
|| orig_killed_all
;
392 foreach_iter(exec_list_iterator
, iter
, *new_kills
) {
393 kill_entry
*k
= (kill_entry
*)iter
.get();
394 kill(k
->var
, k
->write_mask
);
397 /* already descended into the children. */
398 return visit_continue_with_parent
;
402 ir_constant_propagation_visitor::kill(ir_variable
*var
, unsigned write_mask
)
406 /* We don't track non-vectors. */
407 if (!var
->type
->is_vector() && !var
->type
->is_scalar())
410 /* Remove any entries currently in the ACP for this kill. */
411 foreach_iter(exec_list_iterator
, iter
, *this->acp
) {
412 acp_entry
*entry
= (acp_entry
*)iter
.get();
414 if (entry
->var
== var
) {
415 entry
->write_mask
&= ~write_mask
;
416 if (entry
->write_mask
== 0)
421 /* Add this writemask of the variable to the list of killed
422 * variables in this block.
424 foreach_iter(exec_list_iterator
, iter
, *this->kills
) {
425 kill_entry
*entry
= (kill_entry
*)iter
.get();
427 if (entry
->var
== var
) {
428 entry
->write_mask
|= write_mask
;
432 /* Not already in the list. Make new entry. */
433 this->kills
->push_tail(new(this->mem_ctx
) kill_entry(var
, write_mask
));
437 * Adds an entry to the available constant list if it's a plain assignment
438 * of a variable to a variable.
441 ir_constant_propagation_visitor::add_constant(ir_assignment
*ir
)
446 ir_constant
*condition
= ir
->condition
->as_constant();
447 if (!condition
|| !condition
->value
.b
[0])
454 ir_dereference_variable
*deref
= ir
->lhs
->as_dereference_variable();
455 ir_constant
*constant
= ir
->rhs
->as_constant();
457 if (!deref
|| !constant
)
460 /* Only do constant propagation on vectors. Constant matrices,
461 * arrays, or structures would require more work elsewhere.
463 if (!deref
->var
->type
->is_vector() && !deref
->var
->type
->is_scalar())
466 entry
= new(this->mem_ctx
) acp_entry(deref
->var
, ir
->write_mask
, constant
);
467 this->acp
->push_tail(entry
);
471 * Does a constant propagation pass on the code present in the instruction stream.
474 do_constant_propagation(exec_list
*instructions
)
476 ir_constant_propagation_visitor v
;
478 visit_list_elements(&v
, instructions
);