nir/loop_analyze: Treat do{}while(false) loops as 0 iterations
authorDanylo Piliaiev <danylo.piliaiev@globallogic.com>
Tue, 20 Aug 2019 15:48:33 +0000 (18:48 +0300)
committerTimothy Arceri <tarceri@itsqueeze.com>
Wed, 21 Aug 2019 11:01:15 +0000 (11:01 +0000)
Loops like:

block block_0:
vec1 32 ssa_2 = load_const (0x00000020)
vec1 32 ssa_3 = load_const (0x00000001)
loop {
    vec1 32 ssa_7 = phi block_0: ssa_3, block_4: ssa_9
    vec1 1 ssa_8 = ige ssa_2, ssa_7
    if ssa_8 {
        break
    } else {
    }
    vec1 32 ssa_9 = iadd ssa_7, ssa_1
}

Were treated as having more than 1 iteration and after unrolling
produced wrong results, however such loop will exit during
the first iteration if not unrolled.

So we check if loop will actually loop.

Fixes tests/shaders/glsl-fs-loop-while-false-02.shader_test

Signed-off-by: Danylo Piliaiev <danylo.piliaiev@globallogic.com>
Reviewed-by: Timothy Arceri <tarceri@itsqueeze.com>
src/compiler/nir/nir_loop_analyze.c

index 0ac04b82799e19a104a46f538a5ee3a784c1b605..4689d2230af46a09912c667c2e48b79bf655d64e 100644 (file)
@@ -647,6 +647,43 @@ get_iteration(nir_op cond_op, nir_const_value initial, nir_const_value step,
    return iter_u64 > INT_MAX ? -1 : (int)iter_u64;
 }
 
+static bool
+will_break_on_first_iteration(nir_const_value step,
+                              nir_alu_type induction_base_type,
+                              unsigned trip_offset,
+                              nir_op cond_op, unsigned bit_size,
+                              nir_const_value initial,
+                              nir_const_value limit,
+                              bool limit_rhs, bool invert_cond)
+{
+   if (trip_offset == 1) {
+      nir_op add_op;
+      switch (induction_base_type) {
+      case nir_type_float:
+         add_op = nir_op_fadd;
+         break;
+      case nir_type_int:
+      case nir_type_uint:
+         add_op = nir_op_iadd;
+         break;
+      default:
+         unreachable("Unhandled induction variable base type!");
+      }
+
+      initial = eval_const_binop(add_op, bit_size, initial, step);
+   }
+
+   nir_const_value *src[2];
+   src[limit_rhs ? 0 : 1] = &initial;
+   src[limit_rhs ? 1 : 0] = &limit;
+
+   /* Evaluate the loop exit condition */
+   nir_const_value result;
+   nir_eval_const_opcode(cond_op, &result, 1, bit_size, src);
+
+   return invert_cond ? !result.b : result.b;
+}
+
 static bool
 test_iterations(int32_t iter_int, nir_const_value step,
                 nir_const_value limit, nir_op cond_op, unsigned bit_size,
@@ -741,6 +778,18 @@ calculate_iterations(nir_const_value initial, nir_const_value step,
    assert(nir_src_bit_size(alu->src[0].src) ==
           nir_src_bit_size(alu->src[1].src));
    unsigned bit_size = nir_src_bit_size(alu->src[0].src);
+
+   /* get_iteration works under assumption that iterator will be
+    * incremented or decremented until it hits the limit,
+    * however if the loop condition is false on the first iteration
+    * get_iteration's assumption is broken. Handle such loops first.
+    */
+   if (will_break_on_first_iteration(step, induction_base_type, trip_offset,
+                                     alu_op, bit_size, initial,
+                                     limit, limit_rhs, invert_cond)) {
+      return 0;
+   }
+
    int iter_int = get_iteration(alu_op, initial, step, limit, bit_size);
 
    /* If iter_int is negative the loop is ill-formed or is the conditional is