*/
/**
- * \file ir_dead_code.cpp
+ * \file ir_copy_propagation.cpp
*
- * Eliminates dead assignments and variable declarations from the code.
+ * Moves usage of recently-copied variables to the previous copy of
+ * the variable within basic blocks.
+ *
+ * This should reduce the number of MOV instructions in the generated
+ * programs unless copy propagation is also done on the LIR, and may
+ * help anyway by triggering other optimizations that live in the HIR.
*/
-#include <stdio.h>
#include "ir.h"
#include "ir_visitor.h"
#include "ir_basic_block.h"
-#include "ir_copy_propagation.h"
+#include "ir_optimization.h"
#include "glsl_types.h"
class acp_entry : public exec_node
ir_variable *rhs;
};
-class ir_copy_propagation_visitor : public ir_visitor {
+class ir_copy_propagation_visitor : public ir_hierarchical_visitor {
public:
ir_copy_propagation_visitor(exec_list *acp)
{
progress = false;
+ in_lhs = false;
this->acp = acp;
}
- /**
- * \name Visit methods
- *
- * As typical for the visitor pattern, there must be one \c visit method for
- * each concrete subclass of \c ir_instruction. Virtual base classes within
- * the hierarchy should not have \c visit methods.
- */
- /*@{*/
- virtual void visit(ir_variable *);
- virtual void visit(ir_loop *);
- virtual void visit(ir_loop_jump *);
- virtual void visit(ir_function_signature *);
- virtual void visit(ir_function *);
- virtual void visit(ir_expression *);
- virtual void visit(ir_swizzle *);
- virtual void visit(ir_dereference *);
- virtual void visit(ir_assignment *);
- virtual void visit(ir_constant *);
- virtual void visit(ir_call *);
- virtual void visit(ir_return *);
- virtual void visit(ir_if *);
- /*@}*/
+ virtual ir_visitor_status visit(class ir_dereference_variable *);
+ virtual ir_visitor_status visit_enter(class ir_loop *);
+ virtual ir_visitor_status visit_enter(class ir_function_signature *);
+ virtual ir_visitor_status visit_enter(class ir_function *);
+ virtual ir_visitor_status visit_enter(class ir_assignment *);
+ virtual ir_visitor_status visit_enter(class ir_call *);
+ virtual ir_visitor_status visit_enter(class ir_if *);
/** List of acp_entry */
exec_list *acp;
bool progress;
+
+ /** Currently in the LHS of an assignment? */
+ bool in_lhs;
};
-void
-ir_copy_propagation_visitor::visit(ir_variable *ir)
+ir_visitor_status
+ir_copy_propagation_visitor::visit_enter(ir_loop *ir)
{
(void)ir;
+ return visit_continue_with_parent;
}
-
-void
-ir_copy_propagation_visitor::visit(ir_loop *ir)
+ir_visitor_status
+ir_copy_propagation_visitor::visit_enter(ir_function_signature *ir)
{
(void)ir;
+ return visit_continue_with_parent;
}
-void
-ir_copy_propagation_visitor::visit(ir_loop_jump *ir)
+ir_visitor_status
+ir_copy_propagation_visitor::visit_enter(ir_assignment *ir)
{
(void) ir;
+ this->in_lhs = true;
+ return visit_continue;
}
-
-void
-ir_copy_propagation_visitor::visit(ir_function_signature *ir)
-{
- (void)ir;
-}
-
-void
-ir_copy_propagation_visitor::visit(ir_function *ir)
+ir_visitor_status
+ir_copy_propagation_visitor::visit_enter(ir_function *ir)
{
(void) ir;
-}
-
-void
-ir_copy_propagation_visitor::visit(ir_expression *ir)
-{
- unsigned int operand;
-
- for (operand = 0; operand < ir->get_num_operands(); operand++) {
- ir->operands[operand]->accept(this);
- }
-}
-
-
-void
-ir_copy_propagation_visitor::visit(ir_swizzle *ir)
-{
- ir->val->accept(this);
+ return visit_continue_with_parent;
}
/**
* rewriting of ir_dereference means that the ir_dereference instance
* must not be shared by multiple IR operations!
*/
-void
-ir_copy_propagation_visitor::visit(ir_dereference *ir)
+ir_visitor_status
+ir_copy_propagation_visitor::visit(ir_dereference_variable *ir)
{
- ir_variable *var;
-
- if (ir->mode == ir_dereference::ir_reference_array) {
- ir->selector.array_index->accept(this);
- }
-
- var = ir->var->as_variable();
- if (var) {
- foreach_iter(exec_list_iterator, iter, *this->acp) {
- acp_entry *entry = (acp_entry *)iter.get();
-
- if (var == entry->lhs) {
- ir->var = entry->rhs;
- this->progress = true;
- break;
- }
- }
- } else {
- ir->var->accept(this);
- }
-}
-
-void
-ir_copy_propagation_visitor::visit(ir_assignment *ir)
-{
- if (ir->condition)
- ir->condition->accept(this);
-
/* Ignores the LHS. Don't want to rewrite the LHS to point at some
* other storage!
*/
+ if (this->in_lhs) {
+ this->in_lhs = false;
+ return visit_continue;
+ }
- ir->rhs->accept(this);
-}
+ ir_variable *var = ir->variable_referenced();
+ foreach_iter(exec_list_iterator, iter, *this->acp) {
+ acp_entry *entry = (acp_entry *)iter.get();
-void
-ir_copy_propagation_visitor::visit(ir_constant *ir)
-{
- (void) ir;
+ if (var == entry->lhs) {
+ ir->var = entry->rhs;
+ this->progress = true;
+ break;
+ }
+ }
+
+ return visit_continue;
}
-void
-ir_copy_propagation_visitor::visit(ir_call *ir)
+ir_visitor_status
+ir_copy_propagation_visitor::visit_enter(ir_call *ir)
{
(void)ir;
/* Note, if we were to do copy propagation to parameters of calls, we'd
* have to be careful about out params.
*/
+ return visit_continue_with_parent;
}
-void
-ir_copy_propagation_visitor::visit(ir_return *ir)
-{
- ir_rvalue *val = ir->get_value();
-
- if (val)
- val->accept(this);
-}
-
-
-void
-ir_copy_propagation_visitor::visit(ir_if *ir)
+ir_visitor_status
+ir_copy_propagation_visitor::visit_enter(ir_if *ir)
{
ir->condition->accept(this);
+
+ /* Do not traverse into the body of the if-statement since that is a
+ * different basic block.
+ */
+ return visit_continue_with_parent;
}
-static void
+static bool
propagate_copies(ir_instruction *ir, exec_list *acp)
{
ir_copy_propagation_visitor v(acp);
ir->accept(&v);
+
+ return v.progress;
}
static void
kill_invalidated_copies(ir_assignment *ir, exec_list *acp)
{
- ir_dereference *lhs_deref = ir->lhs->as_dereference();
-
- /* Only handle simple dereferences for now. */
- if (lhs_deref &&
- lhs_deref->mode == ir_dereference::ir_reference_variable) {
- ir_variable *var = lhs_deref->var->as_variable();
+ ir_variable *var = ir->lhs->variable_referenced();
+ assert(var != NULL);
- foreach_iter(exec_list_iterator, iter, *acp) {
- acp_entry *entry = (acp_entry *)iter.get();
+ foreach_iter(exec_list_iterator, iter, *acp) {
+ acp_entry *entry = (acp_entry *)iter.get();
- if (entry->lhs == var || entry->rhs == var) {
- entry->remove();
- }
+ if (entry->lhs == var || entry->rhs == var) {
+ entry->remove();
}
- } else {
- /* FINISHME: Only clear out the entries we overwrote here. */
- acp->make_empty();
}
}
return;
}
- ir_dereference *lhs_deref = ir->lhs->as_dereference();
- if (!lhs_deref || lhs_deref->mode != ir_dereference::ir_reference_variable)
- return;
- ir_variable *lhs_var = lhs_deref->var->as_variable();
+ ir_variable *lhs_var = ir->lhs->whole_variable_referenced();
+ ir_variable *rhs_var = ir->rhs->whole_variable_referenced();
- ir_dereference *rhs_deref = ir->rhs->as_dereference();
- if (!rhs_deref || rhs_deref->mode != ir_dereference::ir_reference_variable)
- return;
- ir_variable *rhs_var = rhs_deref->var->as_variable();
-
- entry = new acp_entry(lhs_var, rhs_var);
- acp->push_tail(entry);
+ if ((lhs_var != NULL) && (rhs_var != NULL)) {
+ entry = new acp_entry(lhs_var, rhs_var);
+ acp->push_tail(entry);
+ }
}
static void
copy_propagation_basic_block(ir_instruction *first,
- ir_instruction *last)
+ ir_instruction *last,
+ void *data)
{
ir_instruction *ir;
/* List of avaialble_copy */
exec_list acp;
+ bool *out_progress = (bool *)data;
+ bool progress = false;
for (ir = first;; ir = (ir_instruction *)ir->next) {
ir_assignment *ir_assign = ir->as_assignment();
- propagate_copies(ir, &acp);
+ progress = propagate_copies(ir, &acp) || progress;
if (ir_assign) {
kill_invalidated_copies(ir_assign, &acp);
if (ir == last)
break;
}
+ *out_progress = progress;
}
/**
{
bool progress = false;
- call_for_basic_blocks(instructions, copy_propagation_basic_block);
+ call_for_basic_blocks(instructions, copy_propagation_basic_block, &progress);
- /* FINISHME: Return a legit progress value. */
return progress;
}