From 5db981952ad65010dd140074ca15d4ee3d0686cc Mon Sep 17 00:00:00 2001 From: Timothy Arceri Date: Sat, 7 Jul 2018 17:56:26 +1000 Subject: [PATCH] nir: add loop unroll support for wrapper loops This adds support for unrolling the classic do { // ... } while (false) that is used to wrap multi-line macros. GLSL IR also wraps switch statements in a loop like this. shader-db results IVB: total loops in shared programs: 2515 -> 2512 (-0.12%) loops in affected programs: 33 -> 30 (-9.09%) helped: 3 HURT: 0 Reviewed-by: Jason Ekstrand --- src/compiler/nir/nir_opt_loop_unroll.c | 77 ++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/src/compiler/nir/nir_opt_loop_unroll.c b/src/compiler/nir/nir_opt_loop_unroll.c index e0e0b754716..9c33267cb72 100644 --- a/src/compiler/nir/nir_opt_loop_unroll.c +++ b/src/compiler/nir/nir_opt_loop_unroll.c @@ -465,6 +465,65 @@ complex_unroll(nir_loop *loop, nir_loop_terminator *unlimit_term, _mesa_hash_table_destroy(remap_table, NULL); } +/* Unrolls the classic wrapper loops e.g + * + * do { + * // ... + * } while (false) + */ +static bool +wrapper_unroll(nir_loop *loop) +{ + bool progress = false; + + nir_block *blk_after_loop = + nir_cursor_current_block(nir_after_cf_node(&loop->cf_node)); + + /* There may still be some single src phis following the loop that + * have not yet been cleaned up by another pass. Tidy those up before + * unrolling the loop. + */ + nir_foreach_instr_safe(instr, blk_after_loop) { + if (instr->type != nir_instr_type_phi) + break; + + nir_phi_instr *phi = nir_instr_as_phi(instr); + assert(exec_list_length(&phi->srcs) == 1); + + nir_phi_src *phi_src = exec_node_data(nir_phi_src, + exec_list_get_head(&phi->srcs), + node); + + nir_ssa_def_rewrite_uses(&phi->dest.ssa, phi_src->src); + nir_instr_remove(instr); + + progress = true; + } + + nir_block *last_loop_blk = nir_loop_last_block(loop); + if (nir_block_ends_in_break(last_loop_blk)) { + + /* Remove break at end of the loop */ + nir_instr *break_instr = nir_block_last_instr(last_loop_blk); + nir_instr_remove(break_instr); + + /* Pluck out the loop body. */ + nir_cf_list loop_body; + nir_cf_extract(&loop_body, nir_before_block(nir_loop_first_block(loop)), + nir_after_block(nir_loop_last_block(loop))); + + /* Reinsert loop body after the loop */ + nir_cf_reinsert(&loop_body, nir_after_cf_node(&loop->cf_node)); + + /* The loop has been unrolled so remove it. */ + nir_cf_node_remove(&loop->cf_node); + + progress = true; + } + + return progress; +} + static bool is_loop_small_enough_to_unroll(nir_shader *shader, nir_loop_info *li) { @@ -516,6 +575,24 @@ process_loops(nir_shader *sh, nir_cf_node *cf_node, bool *has_nested_loop_out) */ if (!progress) { + /* Check for the classic + * + * do { + * // ... + * } while (false) + * + * that is used to wrap multi-line macros. GLSL IR also wraps switch + * statements in a loop like this. + */ + if (loop->info->limiting_terminator == NULL && + list_empty(&loop->info->loop_terminator_list) && + !loop->info->complex_loop) { + + progress = wrapper_unroll(loop); + + goto exit; + } + if (has_nested_loop || loop->info->limiting_terminator == NULL) goto exit; -- 2.30.2