Merge remote-tracking branch 'mesa-public/master' into vulkan
[mesa.git] / src / glsl / loop_analysis.cpp
index c7f929590ab1e41497b398fff3c0b03995a6c4f7..21d46ebce536129198256f4520b8392f154c3fe2 100644 (file)
@@ -52,9 +52,10 @@ loop_variable::record_reference(bool in_assignee,
    if (in_assignee) {
       assert(current_assignment != NULL);
 
-      this->conditional_or_nested_assignment =
-         in_conditional_code_or_nested_loop
-         || current_assignment->condition != 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);
@@ -133,7 +134,7 @@ loop_terminator *
 loop_variable_state::insert(ir_if *if_stmt)
 {
    void *mem_ctx = ralloc_parent(this);
-   loop_terminator *t = rzalloc(mem_ctx, loop_terminator);
+   loop_terminator *t = new(mem_ctx) loop_terminator();
 
    t->ir = if_stmt;
    this->terminators.push_tail(t);
@@ -221,13 +222,12 @@ loop_analysis::visit(ir_loop_jump *ir)
 
 
 ir_visitor_status
-loop_analysis::visit_enter(ir_call *ir)
+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_list(node, &this->state) {
-      loop_variable_state *const ls = (loop_variable_state *) node;
+   foreach_in_list(loop_variable_state, ls, &this->state) {
       ls->contains_calls = true;
    }
 
@@ -245,9 +245,7 @@ loop_analysis::visit(ir_dereference_variable *ir)
 
    bool nested = false;
 
-   foreach_list(node, &this->state) {
-      loop_variable_state *const ls = (loop_variable_state *) node;
-
+   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);
 
@@ -287,10 +285,10 @@ loop_analysis::visit_leave(ir_loop *ir)
    if (ls->contains_calls)
       return visit_continue;
 
-   foreach_list(node, &ir->body_instructions) {
+   foreach_in_list(ir_instruction, node, &ir->body_instructions) {
       /* Skip over declarations at the start of a loop.
        */
-      if (((ir_instruction *) node)->as_variable())
+      if (node->as_variable())
         continue;
 
       ir_if *if_stmt = ((ir_instruction *) node)->as_if();
@@ -302,9 +300,7 @@ loop_analysis::visit_leave(ir_loop *ir)
    }
 
 
-   foreach_list_safe(node, &ls->variables) {
-      loop_variable *lv = (loop_variable *) node;
-
+   foreach_in_list_safe(loop_variable, lv, &ls->variables) {
       /* Move variables that are already marked as being loop constant to
        * a separate list.  These trivially don't need to be tested.
        */
@@ -332,9 +328,7 @@ loop_analysis::visit_leave(ir_loop *ir)
    do {
       progress = false;
 
-      foreach_list_safe(node, &ls->variables) {
-        loop_variable *lv = (loop_variable *) node;
-
+      foreach_in_list_safe(loop_variable, lv, &ls->variables) {
         if (lv->conditional_or_nested_assignment || (lv->num_assignments > 1))
            continue;
 
@@ -358,9 +352,7 @@ loop_analysis::visit_leave(ir_loop *ir)
    /* The remaining variables that are not loop invariant might be loop
     * induction variables.
     */
-   foreach_list_safe(node, &ls->variables) {
-      loop_variable *lv = (loop_variable *) node;
-
+   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
@@ -388,8 +380,6 @@ loop_analysis::visit_leave(ir_loop *ir)
       ir_rvalue *const inc =
         get_basic_induction_increment(lv->first_assignment, ls->var_hash);
       if (inc != NULL) {
-        lv->iv_scale = NULL;
-        lv->biv = lv->var;
         lv->increment = inc;
 
         lv->remove();
@@ -397,6 +387,74 @@ loop_analysis::visit_leave(ir_loop *ir)
       }
    }
 
+   /* 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();
+
+        ir_rvalue *init = find_initial_value(ir, var);
+
+         loop_variable *lv = ls->get(var);
+         if (lv != NULL && lv->is_induction_var()) {
+            t->iterations = calculate_iterations(init, 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;
 }
 
@@ -521,8 +579,10 @@ get_basic_induction_increment(ir_assignment *ir, hash_table *var_hash)
         loop_variable *lv =
            (loop_variable *) hash_table_find(var_hash, inc_var);
 
-        if (!lv->is_loop_constant())
-           inc = NULL;
+         if (lv == NULL || !lv->is_loop_constant()) {
+            assert(lv != NULL);
+            inc = NULL;
+         }
       } else
         inc = NULL;
    }