diff options
author | Timothy Arceri <[email protected]> | 2016-12-09 16:49:09 +1100 |
---|---|---|
committer | Timothy Arceri <[email protected]> | 2016-12-23 10:59:32 +1100 |
commit | f20ba7ad4476013a6a322a8d562f38bc6c4e2370 (patch) | |
tree | 230ea46c23ccc3530c24a1ef576a915ee5147a08 | |
parent | 40e9f2f13847ddd94e1216088aa00456d7b02d2b (diff) |
nir: update nir_lower_returns to only predicate instructions when needed
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 <[email protected]>
-rw-r--r-- | src/compiler/nir/nir_lower_returns.c | 47 |
1 files changed, 41 insertions, 6 deletions
diff --git a/src/compiler/nir/nir_lower_returns.c b/src/compiler/nir/nir_lower_returns.c index cf49d5b09e3..33490b223b4 100644 --- a/src/compiler/nir/nir_lower_returns.c +++ b/src/compiler/nir/nir_lower_returns.c @@ -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); |