From: Karol Herbst Date: Fri, 15 May 2020 10:47:53 +0000 (+0200) Subject: spirv: parse unstructured CFG X-Git-Url: https://git.libre-soc.org/?p=mesa.git;a=commitdiff_plain;h=217def3ac6721bc7d8a1f99d2fc4501e5ee8627d spirv: parse unstructured CFG v2 (Boris Brezillon): handle functions with return values v3: call structurizer v4: entire rewrite v5: fix handling of already visited default branches v2 (Jason Ekstrand): Stop walking hash tables Signed-off-by: Karol Herbst Reviewed-by: Jason Ekstrand Tested-by: Jesse Natalie Part-of: --- diff --git a/src/compiler/nir/nir.c b/src/compiler/nir/nir.c index d1db749423e..d8ff265d9f3 100644 --- a/src/compiler/nir/nir.c +++ b/src/compiler/nir/nir.c @@ -732,8 +732,6 @@ reduce_cursor(nir_cursor cursor) { switch (cursor.option) { case nir_cursor_before_block: - assert(nir_cf_node_prev(&cursor.block->cf_node) == NULL || - nir_cf_node_prev(&cursor.block->cf_node)->type != nir_cf_node_block); if (exec_list_is_empty(&cursor.block->instr_list)) { /* Empty block. After is as good as before. */ cursor.option = nir_cursor_after_block; diff --git a/src/compiler/spirv/spirv_to_nir.c b/src/compiler/spirv/spirv_to_nir.c index 2ef9b8c3673..747d1a0cdcc 100644 --- a/src/compiler/spirv/spirv_to_nir.c +++ b/src/compiler/spirv/spirv_to_nir.c @@ -5594,6 +5594,9 @@ spirv_to_nir(const uint32_t *words, size_t word_count, if (entry_point->num_params && b->shader->info.stage == MESA_SHADER_KERNEL) entry_point = vtn_emit_kernel_entry_point_wrapper(b, entry_point); + /* structurize the CFG */ + nir_lower_goto_ifs(b->shader); + entry_point->is_entrypoint = true; /* When multiple shader stages exist in the same SPIR-V module, we diff --git a/src/compiler/spirv/vtn_cfg.c b/src/compiler/spirv/vtn_cfg.c index 023b2decf7f..9dc2a842da6 100644 --- a/src/compiler/spirv/vtn_cfg.c +++ b/src/compiler/spirv/vtn_cfg.c @@ -22,6 +22,7 @@ */ #include "vtn_private.h" +#include "spirv_info.h" #include "nir/nir_vla.h" static struct vtn_block * @@ -817,6 +818,9 @@ vtn_build_cfg(struct vtn_builder *b, const uint32_t *words, const uint32_t *end) vtn_foreach_instruction(b, words, end, vtn_cfg_handle_prepass_instruction); + if (b->shader->info.stage == MESA_SHADER_KERNEL) + return; + vtn_foreach_cf_node(func_node, &b->functions) { struct vtn_function *func = vtn_cf_node_as_function(func_node); @@ -1186,6 +1190,141 @@ vtn_emit_cf_list_structured(struct vtn_builder *b, struct list_head *cf_list, } } +static struct nir_block * +vtn_new_unstructured_block(struct vtn_builder *b, struct vtn_function *func) +{ + struct nir_block *n = nir_block_create(b->shader); + exec_list_push_tail(&func->impl->body, &n->cf_node.node); + n->cf_node.parent = &func->impl->cf_node; + return n; +} + +static void +vtn_add_unstructured_block(struct vtn_builder *b, + struct vtn_function *func, + struct list_head *work_list, + struct vtn_block *block) +{ + if (!block->block) { + block->block = vtn_new_unstructured_block(b, func); + list_addtail(&block->node.link, work_list); + } +} + +static void +vtn_emit_cf_func_unstructured(struct vtn_builder *b, struct vtn_function *func, + vtn_instruction_handler handler) +{ + struct list_head work_list; + list_inithead(&work_list); + + func->start_block->block = nir_start_block(func->impl); + list_addtail(&func->start_block->node.link, &work_list); + while (!list_is_empty(&work_list)) { + struct vtn_block *block = + list_first_entry(&work_list, struct vtn_block, node.link); + list_del(&block->node.link); + + vtn_assert(block->block); + + const uint32_t *block_start = block->label; + const uint32_t *block_end = block->branch; + + b->nb.cursor = nir_after_block(block->block); + block_start = vtn_foreach_instruction(b, block_start, block_end, + vtn_handle_phis_first_pass); + vtn_foreach_instruction(b, block_start, block_end, handler); + block->end_nop = nir_intrinsic_instr_create(b->nb.shader, + nir_intrinsic_nop); + nir_builder_instr_insert(&b->nb, &block->end_nop->instr); + + SpvOp op = *block_end & SpvOpCodeMask; + switch (op) { + case SpvOpBranch: { + struct vtn_block *branch_block = vtn_block(b, block->branch[1]); + vtn_add_unstructured_block(b, func, &work_list, branch_block); + nir_goto(&b->nb, branch_block->block); + break; + } + + case SpvOpBranchConditional: { + nir_ssa_def *cond = vtn_ssa_value(b, block->branch[1])->def; + struct vtn_block *then_block = vtn_block(b, block->branch[2]); + struct vtn_block *else_block = vtn_block(b, block->branch[3]); + + vtn_add_unstructured_block(b, func, &work_list, then_block); + if (then_block == else_block) { + nir_goto(&b->nb, then_block->block); + } else { + vtn_add_unstructured_block(b, func, &work_list, else_block); + nir_goto_if(&b->nb, then_block->block, nir_src_for_ssa(cond), + else_block->block); + } + + break; + } + + case SpvOpSwitch: { + struct list_head cases; + list_inithead(&cases); + vtn_parse_switch(b, NULL, block->branch, &cases); + + nir_ssa_def *sel = vtn_get_nir_ssa(b, block->branch[1]); + + struct vtn_case *def = NULL; + vtn_foreach_cf_node(case_node, &cases) { + struct vtn_case *cse = vtn_cf_node_as_case(case_node); + if (cse->is_default) { + assert(def == NULL); + def = cse; + continue; + } + + nir_ssa_def *cond = nir_imm_false(&b->nb); + util_dynarray_foreach(&cse->values, uint64_t, val) { + nir_ssa_def *imm = nir_imm_intN_t(&b->nb, *val, sel->bit_size); + cond = nir_ior(&b->nb, cond, nir_ieq(&b->nb, sel, imm)); + } + + /* block for the next check */ + nir_block *e = vtn_new_unstructured_block(b, func); + vtn_add_unstructured_block(b, func, &work_list, cse->block); + + /* add branching */ + nir_goto_if(&b->nb, cse->block->block, nir_src_for_ssa(cond), e); + b->nb.cursor = nir_after_block(e); + } + + vtn_assert(def != NULL); + vtn_add_unstructured_block(b, func, &work_list, def->block); + + /* now that all cases are handled, branch into the default block */ + nir_goto(&b->nb, def->block->block); + break; + } + + case SpvOpKill: { + nir_intrinsic_instr *discard = + nir_intrinsic_instr_create(b->nb.shader, nir_intrinsic_discard); + nir_builder_instr_insert(&b->nb, &discard->instr); + nir_goto(&b->nb, b->func->impl->end_block); + break; + } + + case SpvOpUnreachable: + case SpvOpReturn: + case SpvOpReturnValue: { + vtn_emit_ret_store(b, block); + nir_goto(&b->nb, b->func->impl->end_block); + break; + } + + default: + vtn_fail("Unhandled opcode %s", spirv_op_to_string(op)); + } + } +} + void vtn_function_emit(struct vtn_builder *b, struct vtn_function *func, vtn_instruction_handler instruction_handler) @@ -1197,7 +1336,13 @@ vtn_function_emit(struct vtn_builder *b, struct vtn_function *func, b->has_loop_continue = false; b->phi_table = _mesa_pointer_hash_table_create(b); - vtn_emit_cf_list_structured(b, &func->body, NULL, NULL, instruction_handler); + if (b->shader->info.stage == MESA_SHADER_KERNEL) { + b->func->impl->structured = false; + vtn_emit_cf_func_unstructured(b, func, instruction_handler); + } else { + vtn_emit_cf_list_structured(b, &func->body, NULL, NULL, + instruction_handler); + } vtn_foreach_instruction(b, func->start_block->label, func->end, vtn_handle_phi_second_pass); diff --git a/src/compiler/spirv/vtn_private.h b/src/compiler/spirv/vtn_private.h index 8ee4f7be70b..6fc4f2e4f3e 100644 --- a/src/compiler/spirv/vtn_private.h +++ b/src/compiler/spirv/vtn_private.h @@ -242,6 +242,9 @@ struct vtn_block { /** Every block ends in a nop intrinsic so that we can find it again */ nir_intrinsic_instr *end_nop; + + /** attached nir_block */ + struct nir_block *block; }; struct vtn_function {