nir: update nir_lower_returns to only predicate instructions when needed
authorTimothy Arceri <timothy.arceri@collabora.com>
Fri, 9 Dec 2016 05:49:09 +0000 (16:49 +1100)
committerTimothy Arceri <timothy.arceri@collabora.com>
Thu, 22 Dec 2016 23:59:32 +0000 (10:59 +1100)
Unless an if statement contains nested returns we can simply add
any following instructions to the branch without the return.

V2: fix handling if_nested_return value when there is a sibling if/loop
that doesn't contain a return. (Spotted by Ken)

V3:
 - add a better comment to the new variable
 - remove instructions after if when both branches return

Reviewed-by: Jason Ekstrand <jason@jlekstrand.net>
src/compiler/nir/nir_lower_returns.c

index cf49d5b09e35d4d5216090e360418aefddf4fb06..33490b223b41c36f3e060beb08b61b4ee223130e 100644 (file)
@@ -30,6 +30,13 @@ struct lower_returns_state {
    struct exec_list *cf_list;
    nir_loop *loop;
    nir_variable *return_flag;
+
+   /* This indicates that we have a return which is predicated on some form of
+    * control-flow.  Since whether or not the return happens can only be
+    * determined dynamically at run-time, everything that occurs afterwards
+    * needs to be predicated on the return flag variable.
+    */
+   bool has_predicated_return;
 };
 
 static bool lower_returns_in_cf_list(struct exec_list *cf_list,
@@ -82,8 +89,10 @@ lower_returns_in_loop(nir_loop *loop, struct lower_returns_state *state)
     * flag set to true.  We need to predicate everything following the loop
     * on the return flag.
     */
-   if (progress)
+   if (progress) {
       predicate_following(&loop->cf_node, state);
+      state->has_predicated_return = true;
+   }
 
    return progress;
 }
@@ -91,10 +100,14 @@ lower_returns_in_loop(nir_loop *loop, struct lower_returns_state *state)
 static bool
 lower_returns_in_if(nir_if *if_stmt, struct lower_returns_state *state)
 {
-   bool progress;
+   bool progress, then_progress, else_progress;
+
+   bool has_predicated_return = state->has_predicated_return;
+   state->has_predicated_return = false;
 
-   progress = lower_returns_in_cf_list(&if_stmt->then_list, state);
-   progress = lower_returns_in_cf_list(&if_stmt->else_list, state) || progress;
+   then_progress = lower_returns_in_cf_list(&if_stmt->then_list, state);
+   else_progress = lower_returns_in_cf_list(&if_stmt->else_list, state);
+   progress = then_progress || else_progress;
 
    /* If either of the recursive calls made progress, then there were
     * returns inside of the body of the if.  If we're in a loop, then these
@@ -106,8 +119,29 @@ lower_returns_in_if(nir_if *if_stmt, struct lower_returns_state *state)
     * after a return, we need to predicate everything following on the
     * return flag.
     */
-   if (progress && !state->loop)
-      predicate_following(&if_stmt->cf_node, state);
+   if (progress && !state->loop) {
+      if (state->has_predicated_return) {
+         predicate_following(&if_stmt->cf_node, state);
+      } else {
+         /* If there are no nested returns we can just add the instructions to
+          * the end of the branch that doesn't have the return.
+          */
+         nir_cf_list list;
+         nir_cf_extract(&list, nir_after_cf_node(&if_stmt->cf_node),
+                        nir_after_cf_list(state->cf_list));
+
+         if (then_progress && else_progress) {
+            /* Both branches return so delete instructions following the if */
+            nir_cf_delete(&list);
+         } else if (then_progress) {
+            nir_cf_reinsert(&list, nir_after_cf_list(&if_stmt->else_list));
+         } else {
+            nir_cf_reinsert(&list, nir_after_cf_list(&if_stmt->then_list));
+         }
+      }
+   }
+
+   state->has_predicated_return = progress || has_predicated_return;
 
    return progress;
 }
@@ -221,6 +255,7 @@ nir_lower_returns_impl(nir_function_impl *impl)
    state.cf_list = &impl->body;
    state.loop = NULL;
    state.return_flag = NULL;
+   state.has_predicated_return = false;
    nir_builder_init(&state.builder, impl);
 
    bool progress = lower_returns_in_cf_list(&impl->body, &state);