#include "ir.h"
#include "ir_visitor.h"
#include "ir_optimization.h"
#include "glsl_types.h"
namespace {
struct assignment_entry {
exec_node link;
int assignment_count;
ir_variable *var;
ir_constant *constval;
bool our_scope;
};
class ir_constant_variable_visitor : public ir_hierarchical_visitor {
public:
virtual ir_visitor_status visit_enter(ir_dereference_variable *);
virtual ir_visitor_status visit(ir_variable *);
virtual ir_visitor_status visit_enter(ir_assignment *);
virtual ir_visitor_status visit_enter(ir_call *);
virtual ir_visitor_status visit_enter(ir_function_signature *);
exec_list list;
};
}
static struct assignment_entry *
get_assignment_entry(ir_variable *var, exec_list *list)
{
struct assignment_entry *entry;
foreach_list_typed(struct assignment_entry, entry, link, list) {
if (entry->var == var)
return entry;
}
entry = (struct assignment_entry *)calloc(1, sizeof(*entry));
entry->var = var;
list->push_head(&entry->link);
return entry;
}
ir_visitor_status
ir_constant_variable_visitor::visit(ir_variable *ir)
{
struct assignment_entry *entry = get_assignment_entry(ir, &this->list);
entry->our_scope = true;
return visit_continue;
}
ir_visitor_status
ir_constant_variable_visitor::visit_enter(ir_dereference_variable *ir)
{
(void)ir;
return visit_continue_with_parent;
}
ir_visitor_status
ir_constant_variable_visitor::visit_enter(ir_assignment *ir)
{
ir_constant *constval;
struct assignment_entry *entry;
entry = get_assignment_entry(ir->lhs->variable_referenced(), &this->list);
assert(entry);
entry->assignment_count++;
if (entry->var->constant_value)
return visit_continue;
if (ir->condition)
return visit_continue;
ir_variable *var = ir->whole_variable_written();
if (!var)
return visit_continue;
constval = ir->rhs->constant_expression_value();
if (!constval)
return visit_continue;
entry->constval = constval;
return visit_continue;
}
ir_visitor_status
ir_constant_variable_visitor::visit_enter(ir_call *ir)
{
foreach_two_lists(formal_node, &ir->callee->parameters,
actual_node, &ir->actual_parameters) {
ir_rvalue *param_rval = (ir_rvalue *) actual_node;
ir_variable *param = (ir_variable *) formal_node;
if (param->data.mode == ir_var_function_out ||
param->data.mode == ir_var_function_inout) {
ir_variable *var = param_rval->variable_referenced();
struct assignment_entry *entry;
assert(var);
entry = get_assignment_entry(var, &this->list);
entry->assignment_count++;
}
}
if (ir->return_deref != NULL) {
ir_variable *var = ir->return_deref->variable_referenced();
struct assignment_entry *entry;
assert(var);
entry = get_assignment_entry(var, &this->list);
entry->assignment_count++;
}
return visit_continue;
}
ir_visitor_status
ir_constant_variable_visitor::visit_enter(ir_function_signature *ir)
{
foreach_in_list(ir_variable, var, &ir->parameters) {
if (var->data.mode == ir_var_function_in || var->data.mode == ir_var_const_in || var->data.mode == ir_var_function_inout) {
struct assignment_entry *entry;
entry = get_assignment_entry(var, &this->list);
entry->assignment_count++;
}
}
visit_list_elements(this, &ir->body);
return visit_continue_with_parent;
}
bool
do_constant_variable(exec_list *instructions)
{
bool progress = false;
ir_constant_variable_visitor v;
v.run(instructions);
while (!v.list.is_empty()) {
struct assignment_entry *entry;
entry = exec_node_data(struct assignment_entry, v.list.head, link);
if (entry->assignment_count == 1 && entry->constval && entry->our_scope) {
entry->var->constant_value = entry->constval;
progress = true;
}
entry->link.remove();
free(entry);
}
return progress;
}
bool
do_constant_variable_unlinked(exec_list *instructions)
{
bool progress = false;
foreach_in_list(ir_instruction, ir, instructions) {
ir_function *f = ir->as_function();
if (f) {
foreach_in_list(ir_function_signature, sig, &f->signatures) {
if (do_constant_variable(&sig->body))
progress = true;
}
}
}
return progress;
}