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 <jason@jlekstrand.net>
This commit is contained in:
@@ -465,6 +465,65 @@ complex_unroll(nir_loop *loop, nir_loop_terminator *unlimit_term,
|
|||||||
_mesa_hash_table_destroy(remap_table, NULL);
|
_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
|
static bool
|
||||||
is_loop_small_enough_to_unroll(nir_shader *shader, nir_loop_info *li)
|
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) {
|
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)
|
if (has_nested_loop || loop->info->limiting_terminator == NULL)
|
||||||
goto exit;
|
goto exit;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user