| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804 |
- /*
- * Copyright © 2010 Intel Corporation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice (including the next
- * paragraph) shall be included in all copies or substantial portions of the
- * Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
- #include "glsl_types.h"
- #include "loop_analysis.h"
- #include "ir_hierarchical_visitor.h"
- #include "ir_variable_refcount.h"
- static bool is_loop_terminator(ir_if *ir);
- static bool used_outside_loops(exec_node *head, ir_variable *var, bool first_assignment);
- static bool all_expression_operands_are_loop_constant(ir_rvalue *,
- hash_table *);
- static ir_rvalue *get_basic_induction_increment(ir_assignment *, hash_table *);
- /**
- * Record the fact that the given loop variable was referenced inside the loop.
- *
- * \arg in_assignee is true if the reference was on the LHS of an assignment.
- *
- * \arg in_conditional_code_or_nested_loop is true if the reference occurred
- * inside an if statement or a nested loop.
- *
- * \arg current_assignment is the ir_assignment node that the loop variable is
- * on the LHS of, if any (ignored if \c in_assignee is false).
- */
- void
- loop_variable::record_reference(bool in_assignee,
- bool in_conditional_code_or_nested_loop,
- ir_assignment *current_assignment)
- {
- if (in_assignee) {
- assert(current_assignment != NULL);
- if (in_conditional_code_or_nested_loop ||
- current_assignment->condition != NULL) {
- this->conditional_or_nested_assignment = true;
- }
- if (this->first_assignment == NULL) {
- assert(this->num_assignments == 0);
- this->first_assignment = current_assignment;
- }
- this->num_assignments++;
- } else if (this->first_assignment == current_assignment) {
- /* This catches the case where the variable is used in the RHS of an
- * assignment where it is also in the LHS.
- */
- this->read_before_write = true;
- }
- }
- loop_state::loop_state()
- {
- this->ht = hash_table_ctor(0, hash_table_pointer_hash,
- hash_table_pointer_compare);
- this->ht_inductors = hash_table_ctor(0, hash_table_pointer_hash,
- hash_table_pointer_compare);
- this->ht_non_inductors = hash_table_ctor(0, hash_table_pointer_hash,
- hash_table_pointer_compare);
- this->mem_ctx = ralloc_context(NULL);
- this->loop_found = false;
- }
- loop_state::~loop_state()
- {
- hash_table_dtor(this->ht);
- hash_table_dtor(this->ht_inductors);
- hash_table_dtor(this->ht_non_inductors);
- ralloc_free(this->mem_ctx);
- }
- loop_variable_state *
- loop_state::insert(ir_loop *ir)
- {
- loop_variable_state *ls = new(this->mem_ctx) loop_variable_state;
- hash_table_insert(this->ht, ls, ir);
- this->loop_found = true;
- return ls;
- }
- loop_variable_state *
- loop_state::get(const ir_loop *ir)
- {
- return (loop_variable_state *) hash_table_find(this->ht, ir);
- }
- loop_variable_state *
- loop_state::get_for_inductor(const ir_variable *ir)
- {
- return (loop_variable_state *) hash_table_find(this->ht_inductors, ir);
- }
- void
- loop_state::insert_non_inductor(ir_variable *var)
- {
- // key doesn't matter, just needs to be non-NULL
- hash_table_insert(this->ht_non_inductors, this, var);
- }
- bool
- loop_state::insert_inductor(loop_variable* loopvar, loop_variable_state* state, ir_loop* loop)
- {
- ir_variable* var = loopvar->var;
- // Check if this variable is already marked as "sure can't be a private inductor variable"
- if (hash_table_find(this->ht_non_inductors, var))
- return false;
- // Check if this variable is used after the loop anywhere. If it is, it can't be a
- // variable that's private to the loop.
- ir_variable_refcount_visitor refs;
- for (exec_node* node = loop->next;
- !node->is_tail_sentinel();
- node = node->next)
- {
- ir_instruction *ir = (ir_instruction *) node;
- ir->accept (&refs);
- if (refs.find_variable_entry(var))
- {
- // add to list of "non inductors", so that next loop does not try
- // to add it as inductor again
- hash_table_insert(this->ht_non_inductors, state, var);
- return false;
- }
- }
- // Check if this variable is used before the loop anywhere. If it is, it can't be a
- // variable that's private to the loop.
- // Skip over the IR that declared the variable or assigned the initial value though.
- for (exec_node* node = loop->prev;
- !node->is_head_sentinel();
- node = node->prev)
- {
- ir_instruction *ir = (ir_instruction *) node;
- if (ir == loopvar->initial_value_ir)
- continue;
- if (ir->ir_type == ir_type_variable)
- continue;
- ir->accept (&refs);
- if (refs.find_variable_entry(var))
- {
- // add to list of "non inductors", so that next loop does not try
- // to add it as inductor again
- hash_table_insert(this->ht_non_inductors, state, var);
- return false;
- }
- }
-
- state->private_induction_variable_count++;
- hash_table_insert(this->ht_inductors, state, var);
- return true;
- }
- loop_variable *
- loop_variable_state::get(const ir_variable *ir)
- {
- return (loop_variable *) hash_table_find(this->var_hash, ir);
- }
- loop_variable *
- loop_variable_state::insert(ir_variable *var)
- {
- void *mem_ctx = ralloc_parent(this);
- loop_variable *lv = rzalloc(mem_ctx, loop_variable);
- lv->var = var;
- hash_table_insert(this->var_hash, lv, lv->var);
- this->variables.push_tail(lv);
- return lv;
- }
- loop_terminator *
- loop_variable_state::insert(ir_if *if_stmt)
- {
- void *mem_ctx = ralloc_parent(this);
- loop_terminator *t = new(mem_ctx) loop_terminator();
- t->ir = if_stmt;
- this->terminators.push_tail(t);
- return t;
- }
- /**
- * If the given variable already is recorded in the state for this loop,
- * return the corresponding loop_variable object that records information
- * about it.
- *
- * Otherwise, create a new loop_variable object to record information about
- * the variable, and set its \c read_before_write field appropriately based on
- * \c in_assignee.
- *
- * \arg in_assignee is true if this variable was encountered on the LHS of an
- * assignment.
- */
- loop_variable *
- loop_variable_state::get_or_insert(ir_variable *var, bool in_assignee)
- {
- loop_variable *lv = this->get(var);
- if (lv == NULL) {
- lv = this->insert(var);
- lv->read_before_write = !in_assignee;
- }
- return lv;
- }
- namespace {
- class loop_analysis : public ir_hierarchical_visitor {
- public:
- loop_analysis(loop_state *loops);
- virtual ir_visitor_status visit(ir_loop_jump *);
- virtual ir_visitor_status visit(ir_dereference_variable *);
- virtual ir_visitor_status visit(ir_variable *);
- virtual ir_visitor_status visit_enter(ir_call *);
- virtual ir_visitor_status visit_enter(ir_loop *);
- virtual ir_visitor_status visit_leave(ir_loop *);
- virtual ir_visitor_status visit_enter(ir_assignment *);
- virtual ir_visitor_status visit_leave(ir_assignment *);
- virtual ir_visitor_status visit_enter(ir_if *);
- virtual ir_visitor_status visit_leave(ir_if *);
- loop_state *loops;
- int if_statement_depth;
- ir_assignment *current_assignment;
- exec_list state;
- };
- } /* anonymous namespace */
- loop_analysis::loop_analysis(loop_state *loops)
- : loops(loops), if_statement_depth(0), current_assignment(NULL)
- {
- /* empty */
- }
- ir_visitor_status
- loop_analysis::visit(ir_loop_jump *ir)
- {
- (void) ir;
- assert(!this->state.is_empty());
- loop_variable_state *const ls =
- (loop_variable_state *) this->state.get_head();
- ls->num_loop_jumps++;
- return visit_continue;
- }
- ir_visitor_status
- loop_analysis::visit(ir_variable *var)
- {
- // if inside a loop, simply continue - we're only interested in variables declared
- // entirely outside of any loops
- if (!this->state.is_empty())
- return visit_continue;
- // Check if this variable is used outside a loop anywhere. If it is, it can't be a
- // variable that's private to the loop, so can't be an inductor.
- // This doesn't reject all possible non-inductors, notably anything declared in an
- // outer loop that isn't an inductor in an inner loop, but it can eliminate some
- // problem cases
- if (used_outside_loops(var->next, var, false))
- {
- // add to list of "non inductors"
- loops->insert_non_inductor(var);
- }
- return visit_continue;
- }
- ir_visitor_status
- loop_analysis::visit_enter(ir_call *)
- {
- /* Mark every loop that we're currently analyzing as containing an ir_call
- * (even those at outer nesting levels).
- */
- foreach_in_list(loop_variable_state, ls, &this->state) {
- ls->contains_calls = true;
- }
- return visit_continue_with_parent;
- }
- ir_visitor_status
- loop_analysis::visit(ir_dereference_variable *ir)
- {
- /* If we're not somewhere inside a loop, there's nothing to do.
- */
- if (this->state.is_empty())
- return visit_continue;
- bool nested = false;
- foreach_in_list(loop_variable_state, ls, &this->state) {
- ir_variable *var = ir->variable_referenced();
- loop_variable *lv = ls->get_or_insert(var, this->in_assignee);
- lv->record_reference(this->in_assignee,
- nested || this->if_statement_depth > 0,
- this->current_assignment);
- nested = true;
- }
- return visit_continue;
- }
- ir_visitor_status
- loop_analysis::visit_enter(ir_loop *ir)
- {
- loop_variable_state *ls = this->loops->insert(ir);
- this->state.push_head(ls);
- return visit_continue;
- }
- ir_visitor_status
- loop_analysis::visit_leave(ir_loop *ir)
- {
- loop_variable_state *const ls =
- (loop_variable_state *) this->state.pop_head();
- /* Function calls may contain side effects. These could alter any of our
- * variables in ways that cannot be known, and may even terminate shader
- * execution (say, calling discard in the fragment shader). So we can't
- * rely on any of our analysis about assignments to variables.
- *
- * We could perform some conservative analysis (prove there's no statically
- * possible assignment, etc.) but it isn't worth it for now; function
- * inlining will allow us to unroll loops anyway.
- */
- if (ls->contains_calls)
- return visit_continue;
- foreach_in_list(ir_instruction, node, &ir->body_instructions) {
- /* Skip over declarations at the start of a loop.
- */
- if (node->as_variable())
- continue;
- ir_if *if_stmt = ((ir_instruction *) node)->as_if();
- if ((if_stmt != NULL) && is_loop_terminator(if_stmt))
- ls->insert(if_stmt);
- else
- break;
- }
- foreach_in_list_safe(loop_variable, lv, &ls->variables) {
- ir_variable *var = lv->var;
- if (var != NULL) {
- lv->initial_value = find_initial_value(ir, var, &lv->initial_value_ir);
- }
- /* Move variables that are already marked as being loop constant to
- * a separate list. These trivially don't need to be tested.
- */
- if (lv->is_loop_constant()) {
- lv->remove();
- ls->constants.push_tail(lv);
- }
- }
- /* Each variable assigned in the loop that isn't already marked as being loop
- * constant might still be loop constant. The requirements at this point
- * are:
- *
- * - Variable is written before it is read.
- *
- * - Only one assignment to the variable.
- *
- * - All operands on the RHS of the assignment are also loop constants.
- *
- * The last requirement is the reason for the progress loop. A variable
- * marked as a loop constant on one pass may allow other variables to be
- * marked as loop constant on following passes.
- */
- bool progress;
- do {
- progress = false;
- foreach_in_list_safe(loop_variable, lv, &ls->variables) {
- if (lv->conditional_or_nested_assignment || (lv->num_assignments > 1))
- continue;
- /* Process the RHS of the assignment. If all of the variables
- * accessed there are loop constants, then add this
- */
- ir_rvalue *const rhs = lv->first_assignment->rhs;
- if (all_expression_operands_are_loop_constant(rhs, ls->var_hash)) {
- lv->rhs_clean = true;
- if (lv->is_loop_constant()) {
- progress = true;
- lv->remove();
- ls->constants.push_tail(lv);
- }
- }
- }
- } while (progress);
- /* The remaining variables that are not loop invariant might be loop
- * induction variables.
- */
- foreach_in_list_safe(loop_variable, lv, &ls->variables) {
- /* If there is more than one assignment to a variable, it cannot be a
- * loop induction variable. This isn't strictly true, but this is a
- * very simple induction variable detector, and it can't handle more
- * complex cases.
- */
- if (lv->num_assignments > 1)
- continue;
- /* All of the variables with zero assignments in the loop are loop
- * invariant, and they should have already been filtered out.
- */
- assert(lv->num_assignments == 1);
- assert(lv->first_assignment != NULL);
- /* The assignment to the variable in the loop must be unconditional and
- * not inside a nested loop.
- */
- if (lv->conditional_or_nested_assignment)
- continue;
- /* Basic loop induction variables have a single assignment in the loop
- * that has the form 'VAR = VAR + i' or 'VAR = VAR - i' where i is a
- * loop invariant.
- */
- ir_rvalue *const inc =
- get_basic_induction_increment(lv->first_assignment, ls->var_hash);
- if (inc != NULL) {
- lv->increment = inc;
- if (loops->insert_inductor(lv, ls, ir)) {
- lv->remove();
- ls->induction_variables.push_tail(lv);
- }
- }
- }
- /* Search the loop terminating conditions for those of the form 'i < c'
- * where i is a loop induction variable, c is a constant, and < is any
- * relative operator. From each of these we can infer an iteration count.
- * Also figure out which terminator (if any) produces the smallest
- * iteration count--this is the limiting terminator.
- */
- foreach_in_list(loop_terminator, t, &ls->terminators) {
- ir_if *if_stmt = t->ir;
- /* If-statements can be either 'if (expr)' or 'if (deref)'. We only care
- * about the former here.
- */
- ir_expression *cond = if_stmt->condition->as_expression();
- if (cond == NULL)
- continue;
- switch (cond->operation) {
- case ir_binop_less:
- case ir_binop_greater:
- case ir_binop_lequal:
- case ir_binop_gequal: {
- /* The expressions that we care about will either be of the form
- * 'counter < limit' or 'limit < counter'. Figure out which is
- * which.
- */
- ir_rvalue *counter = cond->operands[0]->as_dereference_variable();
- ir_constant *limit = cond->operands[1]->as_constant();
- enum ir_expression_operation cmp = cond->operation;
- if (limit == NULL) {
- counter = cond->operands[1]->as_dereference_variable();
- limit = cond->operands[0]->as_constant();
- switch (cmp) {
- case ir_binop_less: cmp = ir_binop_greater; break;
- case ir_binop_greater: cmp = ir_binop_less; break;
- case ir_binop_lequal: cmp = ir_binop_gequal; break;
- case ir_binop_gequal: cmp = ir_binop_lequal; break;
- default: assert(!"Should not get here.");
- }
- }
- if ((counter == NULL) || (limit == NULL))
- break;
- ir_variable *var = counter->variable_referenced();
- loop_variable *lv = ls->get(var);
- if (lv != NULL && lv->is_induction_var()) {
- t->iterations = calculate_iterations(lv->initial_value, limit, lv->increment,
- cmp);
- if (t->iterations >= 0 &&
- (ls->limiting_terminator == NULL ||
- t->iterations < ls->limiting_terminator->iterations)) {
- ls->limiting_terminator = t;
- }
- }
- break;
- }
- default:
- break;
- }
- }
- return visit_continue;
- }
- ir_visitor_status
- loop_analysis::visit_enter(ir_if *ir)
- {
- (void) ir;
- if (!this->state.is_empty())
- this->if_statement_depth++;
- return visit_continue;
- }
- ir_visitor_status
- loop_analysis::visit_leave(ir_if *ir)
- {
- (void) ir;
- if (!this->state.is_empty())
- this->if_statement_depth--;
- return visit_continue;
- }
- ir_visitor_status
- loop_analysis::visit_enter(ir_assignment *ir)
- {
- /* If we're not somewhere inside a loop, there's nothing to do.
- */
- if (this->state.is_empty())
- return visit_continue_with_parent;
- this->current_assignment = ir;
- return visit_continue;
- }
- ir_visitor_status
- loop_analysis::visit_leave(ir_assignment *ir)
- {
- /* Since the visit_enter exits with visit_continue_with_parent for this
- * case, the loop state stack should never be empty here.
- */
- assert(!this->state.is_empty());
- assert(this->current_assignment == ir);
- this->current_assignment = NULL;
- return visit_continue;
- }
- class examine_rhs : public ir_hierarchical_visitor {
- public:
- examine_rhs(hash_table *loop_variables)
- {
- this->only_uses_loop_constants = true;
- this->loop_variables = loop_variables;
- }
- virtual ir_visitor_status visit(ir_dereference_variable *ir)
- {
- loop_variable *lv =
- (loop_variable *) hash_table_find(this->loop_variables, ir->var);
- assert(lv != NULL);
- if (lv->is_loop_constant()) {
- return visit_continue;
- } else {
- this->only_uses_loop_constants = false;
- return visit_stop;
- }
- }
- hash_table *loop_variables;
- bool only_uses_loop_constants;
- };
- bool
- all_expression_operands_are_loop_constant(ir_rvalue *ir, hash_table *variables)
- {
- examine_rhs v(variables);
- ir->accept(&v);
- return v.only_uses_loop_constants;
- }
- ir_rvalue *
- get_basic_induction_increment(ir_assignment *ir, hash_table *var_hash)
- {
- /* The RHS must be a binary expression.
- */
- ir_expression *const rhs = ir->rhs->as_expression();
- if ((rhs == NULL)
- || ((rhs->operation != ir_binop_add)
- && (rhs->operation != ir_binop_sub)))
- return NULL;
- /* One of the of operands of the expression must be the variable assigned.
- * If the operation is subtraction, the variable in question must be the
- * "left" operand.
- */
- ir_variable *const var = ir->lhs->variable_referenced();
- ir_variable *const op0 = rhs->operands[0]->variable_referenced();
- ir_variable *const op1 = rhs->operands[1]->variable_referenced();
- if (((op0 != var) && (op1 != var))
- || ((op1 == var) && (rhs->operation == ir_binop_sub)))
- return NULL;
- ir_rvalue *inc = (op0 == var) ? rhs->operands[1] : rhs->operands[0];
- if (inc->as_constant() == NULL) {
- ir_variable *const inc_var = inc->variable_referenced();
- if (inc_var != NULL) {
- loop_variable *lv =
- (loop_variable *) hash_table_find(var_hash, inc_var);
- if (lv == NULL || !lv->is_loop_constant()) {
- assert(lv != NULL);
- inc = NULL;
- }
- } else
- inc = NULL;
- }
- if ((inc != NULL) && (rhs->operation == ir_binop_sub)) {
- void *mem_ctx = ralloc_parent(ir);
- inc = new(mem_ctx) ir_expression(ir_unop_neg,
- inc->type,
- inc->clone(mem_ctx, NULL),
- NULL);
- }
- return inc;
- }
- /**
- * Detect whether an if-statement is a loop terminating condition
- *
- * Detects if-statements of the form
- *
- * (if (expression bool ...) (break))
- */
- bool
- is_loop_terminator(ir_if *ir)
- {
- if (!ir->else_instructions.is_empty())
- return false;
- ir_instruction *const inst =
- (ir_instruction *) ir->then_instructions.get_head();
- if (inst == NULL)
- return false;
- if (inst->ir_type != ir_type_loop_jump)
- return false;
- ir_loop_jump *const jump = (ir_loop_jump *) inst;
- if (jump->mode != ir_loop_jump::jump_break)
- return false;
- return true;
- }
- bool
- used_outside_loops(exec_node *head, ir_variable *var, bool first_assignment)
- {
- ir_variable_refcount_visitor refs;
- for (exec_node* node = head;
- !node->is_tail_sentinel();
- node = node->next)
- {
- ir_instruction *ir = (ir_instruction *) node;
- if (ir->ir_type == ir_type_variable)
- continue;
- // ignore the first assignment
- if (!first_assignment && ir->ir_type == ir_type_assignment)
- {
- ir_assignment *assign = ir->as_assignment();
- ir_variable *assignee = assign->lhs->whole_variable_referenced();
- if(assignee == var)
- {
- first_assignment = true;
- continue;
- }
- }
- // we don't want to recurse into loops
- if (ir->ir_type == ir_type_loop)
- continue;
- // recurse only for if statements, the other case we would need to recurse is
- // loops, but we are looking for uses outside of loops.
- if (ir->ir_type == ir_type_if)
- {
- ir_if *irif = ir->as_if();
- if (used_outside_loops(irif->then_instructions.head, var, first_assignment))
- return true;
- if (used_outside_loops(irif->else_instructions.head, var, first_assignment))
- return true;
- // if we didn't find in each branch with our recursion, skip
- // otherwise the accept (&refs) below will recurse into loops
- // and may give a false positive.
- continue;
- }
- // we know that we're not inside a loop as we haven't recursed inside,
- // and we started outside of a loop, so any references to this variable
- // mean it is used outside of any loops
- ir->accept (&refs);
- if (refs.find_variable_entry(var))
- {
- return true;
- }
- }
- return false;
- }
- loop_state *
- analyze_loop_variables(exec_list *instructions)
- {
- loop_state *loops = new loop_state;
- loop_analysis v(loops);
- v.run(instructions);
- return v.loops;
- }
|