summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/compiler/nir/nir_opt_loop_unroll.c77
1 files changed, 77 insertions, 0 deletions
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;