diff --git a/src/compiler/nir/nir_opt_if.c b/src/compiler/nir/nir_opt_if.c index 8a971c43f2..cb86a2f780 100644 --- a/src/compiler/nir/nir_opt_if.c +++ b/src/compiler/nir/nir_opt_if.c @@ -237,6 +237,70 @@ is_block_empty(nir_block *block) exec_list_is_empty(&block->instr_list); } +static bool +nir_block_ends_in_continue(nir_block *block) +{ + if (exec_list_is_empty(&block->instr_list)) + return false; + + nir_instr *instr = nir_block_last_instr(block); + return instr->type == nir_instr_type_jump && + nir_instr_as_jump(instr)->type == nir_jump_continue; +} + +/** + * This optimization turns: + * + * loop { + * ... + * if (cond) { + * do_work_1(); + * continue; + * } else { + * } + * do_work_2(); + * } + * + * into: + * + * loop { + * ... + * if (cond) { + * do_work_1(); + * } else { + * do_work_2(); + * } + * } + */ +static bool +opt_loop_remove_last_continue(nir_loop *loop) +{ + nir_block *last_block = nir_loop_last_block(loop); + if (is_block_empty(last_block)) + return false; + + nir_cf_node *if_node = nir_cf_node_prev(&last_block->cf_node); + if (!if_node || if_node->type != nir_cf_node_if) + return false; + + nir_if *nif = nir_cf_node_as_if(if_node); + + nir_block *then_block = nir_if_first_then_block(nif); + if (!nir_block_ends_in_continue(then_block)) + return false; + + nir_cf_list tmp; + nir_cf_extract(&tmp, nir_before_block(last_block), + nir_after_block(last_block)); + nir_cf_reinsert(&tmp, nir_after_cf_list(&nif->else_list)); + + nir_instr *continue_instr = nir_block_last_instr(then_block); + nir_lower_phis_to_regs_block(nir_loop_first_block(loop)); + nir_instr_remove(continue_instr); + + return true; +} + /** * This optimization turns: * @@ -596,6 +660,7 @@ opt_if_cf_list(nir_builder *b, struct exec_list *cf_list) nir_loop *loop = nir_cf_node_as_loop(cf_node); progress |= opt_if_cf_list(b, &loop->body); progress |= opt_peel_loop_initial_if(loop); + progress |= opt_loop_remove_last_continue(loop); break; }