diff --git a/src/imagination/pco/meson.build b/src/imagination/pco/meson.build index 1be85f5af8a..b5308986d2d 100644 --- a/src/imagination/pco/meson.build +++ b/src/imagination/pco/meson.build @@ -7,6 +7,7 @@ libpowervr_compiler_files = files( 'pco.c', 'pco_binary.c', 'pco_bool.c', + 'pco_cf.c', 'pco_const_imms.c', 'pco_debug.c', 'pco_end.c', diff --git a/src/imagination/pco/pco_cf.c b/src/imagination/pco/pco_cf.c new file mode 100644 index 00000000000..a22b981a297 --- /dev/null +++ b/src/imagination/pco/pco_cf.c @@ -0,0 +1,210 @@ +/* + * Copyright © 2025 Imagination Technologies Ltd. + * + * SPDX-License-Identifier: MIT + */ + +/** + * \file pco_cf.c + * + * \brief PCO control-flow passes. + */ + +#include "compiler/list.h" +#include "pco.h" +#include "pco_builder.h" +#include "util/bitscan.h" +#include "util/bitset.h" +#include "util/macros.h" +#include "util/ralloc.h" +#include "util/u_dynarray.h" + +#include +#include + +static pco_ref emc_ref(pco_func *func, pco_builder *b) +{ + if (pco_ref_is_null(func->emc)) { + /* Allocate and initialize the emc. */ + func->emc = pco_ref_new_vreg(func); + + pco_cndst(b, + pco_ref_pred(PCO_PRED_PE), + func->emc, + pco_zero, + pco_ref_imm8(1), + .exec_cnd = PCO_EXEC_CND_EX_ZX, + .cnd = PCO_CND_ALWAYS); + } + + return func->emc; +} + +static inline bool can_pred_exec(pco_if *pif) +{ + /* TODO */ + return false; +} + +static inline void +lower_if_pred_exec(pco_if *pif, bool has_else, bool invert_cond) +{ + /* TODO */ +} + +static inline pco_block *cf_section_create(pco_func *func, + pco_cf_node *parent_cf_node, + struct exec_list *cf_node_list, + enum pco_cf_node_flag flag) +{ + assert(flag == PCO_CF_NODE_FLAG_PROLOGUE || + flag == PCO_CF_NODE_FLAG_INTERLOGUE || + flag == PCO_CF_NODE_FLAG_EPILOGUE); + + pco_block *block = pco_block_create(func); + block->cf_node.parent = parent_cf_node; + block->cf_node.flag = flag; + exec_list_push_tail(cf_node_list, &block->cf_node.node); + + return block; +} + +static inline void lower_if(pco_if *pif, bool has_else, bool invert_cond) +{ + pco_func *func = pif->parent_func; + + pco_block *prologue = cf_section_create(func, + &pif->cf_node, + &pif->prologue, + PCO_CF_NODE_FLAG_PROLOGUE); + + pco_block *interlogue = has_else + ? cf_section_create(func, + &pif->cf_node, + &pif->interlogue, + PCO_CF_NODE_FLAG_INTERLOGUE) + : NULL; + + pco_block *epilogue = cf_section_create(func, + &pif->cf_node, + &pif->epilogue, + PCO_CF_NODE_FLAG_EPILOGUE); + + /* Setup the prologue. */ + pco_builder b = pco_builder_create(func, pco_cursor_after_block(prologue)); + pco_ref emc = emc_ref(func, &b); + + /* TODO: see if the cond producer can set p0 directly. */ + pco_tstz(&b, + pco_ref_null(), + pco_ref_pred(PCO_PRED_P0), + pif->cond, + .tst_type_main = PCO_TST_TYPE_MAIN_U32); + + pco_cndst(&b, + pco_ref_pred(PCO_PRED_PE), + emc, + emc, + pco_ref_imm8(1), + .exec_cnd = PCO_EXEC_CND_EX_ZX, + .cnd = invert_cond ? PCO_CND_P0_TRUE : PCO_CND_P0_FALSE); + + pco_br(&b, + has_else ? &interlogue->cf_node : &epilogue->cf_node, + .branch_cnd = PCO_BRANCH_CND_ALLINST); + + /* Setup the interlogue (if needed). */ + if (has_else) { + b.cursor = pco_cursor_after_block(interlogue); + + pco_cndef(&b, + pco_ref_pred(PCO_PRED_PE), + emc, + emc, + pco_ref_imm8(1), + .exec_cnd = PCO_EXEC_CND_EX_ZX, + .cnd = PCO_CND_ALWAYS); + + pco_br(&b, &epilogue->cf_node, .branch_cnd = PCO_BRANCH_CND_ALLINST); + } + + /* Setup the epilogue. */ + b.cursor = pco_cursor_after_block(epilogue); + + pco_cndend(&b, + pco_ref_pred(PCO_PRED_PE), + emc, + emc, + pco_ref_imm8(1), + .exec_cnd = PCO_EXEC_CND_EX_ZX); + + pif->cond = pco_ref_null(); +} + +static inline bool lower_ifs(pco_func *func) +{ + bool progress = false; + + pco_foreach_if_in_func (pif, func) { + assert(!pco_ref_is_null(pif->cond)); + + assert(exec_list_is_empty(&pif->prologue)); + assert(exec_list_is_empty(&pif->interlogue)); + assert(exec_list_is_empty(&pif->epilogue)); + + bool has_then = !exec_list_is_empty(&pif->then_body); + bool has_else = !exec_list_is_empty(&pif->else_body); + assert(has_then || has_else); + + /* If we only have an else body, invert the condition and bodies. */ + bool invert_cond = false; + if (!has_then && has_else) { + struct exec_list temp; + memcpy(&temp, &pif->then_body, sizeof(pif->then_body)); + memcpy(&pif->then_body, &pif->else_body, sizeof(pif->else_body)); + memcpy(&pif->else_body, &pif->then_body, sizeof(pif->then_body)); + invert_cond = true; + + has_then = true; + has_else = false; + } + + assert(has_then); + + if (can_pred_exec(pif)) + lower_if_pred_exec(pif, has_else, invert_cond); + else + lower_if(pif, has_else, invert_cond); + + progress = true; + } + + return progress; +} + +static inline bool pco_lower_cf(pco_func *func) +{ + bool progress = false; + + progress |= lower_ifs(func); + /* TODO: lower_loops(func); */ + + return progress; +} + +/** + * \brief Control-flow pass. + * + * \param[in,out] shader PCO shader. + * \return True if the pass made progress. + */ +bool pco_cf(pco_shader *shader) +{ + bool progress = false; + + pco_foreach_func_in_shader (func, shader) { + progress |= pco_lower_cf(func); + } + + return progress; +} diff --git a/src/imagination/pco/pco_index.c b/src/imagination/pco/pco_index.c index d1425c78eb1..89f82baee13 100644 --- a/src/imagination/pco/pco_index.c +++ b/src/imagination/pco/pco_index.c @@ -85,6 +85,13 @@ bool pco_index(pco_shader *shader, bool skip_ssa) func->next_if = 0; pco_foreach_if_in_func (pif, func) { pif->index = func->next_if++; + + if (!skip_ssa) { + if (!pco_ref_is_ssa(pif->cond)) + continue; + + pif->cond.val = ssa_idx_map[pif->cond.val]; + } } func->next_loop = 0; diff --git a/src/imagination/pco/pco_internal.h b/src/imagination/pco/pco_internal.h index 1573c73c4c5..8e7dd3f6f47 100644 --- a/src/imagination/pco/pco_internal.h +++ b/src/imagination/pco/pco_internal.h @@ -349,6 +349,8 @@ typedef struct _pco_func { unsigned temps; /** Number of temps allocated. */ + pco_ref emc; /** Execution mask counter register. */ + unsigned enc_offset; /** Encoding offset. */ } pco_func; @@ -1523,6 +1525,7 @@ static inline bool pco_should_print_binary(pco_shader *shader) /* PCO IR passes. */ bool pco_const_imms(pco_shader *shader); bool pco_bool(pco_shader *shader); +bool pco_cf(pco_shader *shader); bool pco_dce(pco_shader *shader); bool pco_end(pco_shader *shader); bool pco_group_instrs(pco_shader *shader); diff --git a/src/imagination/pco/pco_ir.c b/src/imagination/pco/pco_ir.c index 0c8103d47ce..e24378fe339 100644 --- a/src/imagination/pco/pco_ir.c +++ b/src/imagination/pco/pco_ir.c @@ -36,6 +36,13 @@ void pco_process_ir(pco_ctx *ctx, pco_shader *shader) } while (progress); PCO_PASS(_, shader, pco_bool); + PCO_PASS(_, shader, pco_cf); + + do { + progress = false; + PCO_PASS(progress, shader, pco_dce); + } while (progress); + /* TODO: schedule after RA instead as e.g. vecs may no longer be the first * time a drc result is used. */ diff --git a/src/imagination/pco/pco_nir.c b/src/imagination/pco/pco_nir.c index f817f51d899..c565ed1d3a4 100644 --- a/src/imagination/pco/pco_nir.c +++ b/src/imagination/pco/pco_nir.c @@ -133,6 +133,7 @@ void pco_preprocess_nir(pco_ctx *ctx, nir_shader *nir) NULL); NIR_PASS(_, nir, nir_copy_prop); NIR_PASS(_, nir, nir_opt_dce); + NIR_PASS(_, nir, nir_opt_dead_cf); NIR_PASS(_, nir, nir_opt_cse); if (pco_should_print_nir(nir)) { diff --git a/src/imagination/pco/pco_opt.c b/src/imagination/pco/pco_opt.c index 8fd5e42993c..634370f0cdb 100644 --- a/src/imagination/pco/pco_opt.c +++ b/src/imagination/pco/pco_opt.c @@ -576,6 +576,10 @@ bool pco_dce(pco_shader *shader) } } + pco_foreach_ssa_if_in_func (pif, func) { + BITSET_SET(ssa_used, pif->cond.val); + } + /* Remove instructions with unused SSA destinations (if they also have no * side-effects). */ diff --git a/src/imagination/pco/pco_print.c b/src/imagination/pco/pco_print.c index f647238742c..7ca02eafd65 100644 --- a/src/imagination/pco/pco_print.c +++ b/src/imagination/pco/pco_print.c @@ -465,14 +465,12 @@ static void _pco_print_instr(pco_print_state *state, pco_instr *instr) switch (instr->target_cf_node->type) { case PCO_CF_NODE_TYPE_BLOCK: { pco_block *target_block = pco_cf_node_as_block(instr->target_cf_node); - pco_printf(state, " "); pco_print_block_name(state, target_block); break; } case PCO_CF_NODE_TYPE_FUNC: { pco_func *target_func = pco_cf_node_as_func(instr->target_cf_node); - pco_printf(state, " "); pco_print_func_sig(state, target_func, true); break; } diff --git a/src/imagination/pco/pco_trans_nir.c b/src/imagination/pco/pco_trans_nir.c index 9e3cbe07546..0fe2d78837e 100644 --- a/src/imagination/pco/pco_trans_nir.c +++ b/src/imagination/pco/pco_trans_nir.c @@ -30,6 +30,7 @@ typedef struct _trans_ctx { pco_func *func; /** Current function. */ pco_builder b; /** Builder. */ mesa_shader_stage stage; /** Shader stage. */ + enum pco_cf_node_flag flag; /** Implementation-defined control-flow flag. */ BITSET_WORD *float_types; /** NIR SSA float vars. */ BITSET_WORD *int_types; /** NIR SSA int vars. */ @@ -1512,6 +1513,8 @@ static pco_block *trans_block(trans_ctx *tctx, nir_block *nblock) { pco_block *block = pco_block_create(tctx->func); + + block->cf_node.flag = tctx->flag; block->cf_node.parent = parent_cf_node; exec_list_push_tail(cf_node_list, &block->cf_node.node); @@ -1539,9 +1542,29 @@ static void trans_if(trans_ctx *tctx, { pco_if *pif = pco_if_create(tctx->func); - UNREACHABLE("finishme: trans_if"); + pif->cf_node.flag = tctx->flag; pif->cf_node.parent = parent_cf_node; exec_list_push_tail(cf_node_list, &pif->cf_node.node); + + pif->cond = pco_ref_nir_src_t(&nif->condition, tctx); + assert(pco_ref_is_scalar(pif->cond)); + + bool has_then = !nir_cf_list_is_empty_block(&nif->then_list); + bool has_else = !nir_cf_list_is_empty_block(&nif->else_list); + assert(has_then || has_else); + + enum pco_cf_node_flag flag = tctx->flag; + if (has_then) { + tctx->flag = PCO_CF_NODE_FLAG_IF_THEN; + trans_cf_nodes(tctx, &pif->cf_node, &pif->then_body, &nif->then_list); + } + + if (has_else) { + tctx->flag = PCO_CF_NODE_FLAG_IF_ELSE; + trans_cf_nodes(tctx, &pif->cf_node, &pif->else_body, &nif->else_list); + } + + tctx->flag = flag; } /** @@ -1559,8 +1582,10 @@ static void trans_loop(trans_ctx *tctx, { pco_loop *loop = pco_loop_create(tctx->func); + loop->cf_node.flag = tctx->flag; loop->cf_node.parent = parent_cf_node; exec_list_push_tail(cf_node_list, &loop->cf_node.node); + UNREACHABLE("finishme: trans_loop"); } @@ -1597,6 +1622,7 @@ static pco_func *trans_func(trans_ctx *tctx, nir_function_impl *impl) rzalloc_array(NULL, BITSET_WORD, BITSET_WORDS(impl->ssa_alloc)); nir_gather_types(impl, tctx->float_types, tctx->int_types); + tctx->flag = PCO_CF_NODE_FLAG_BODY; trans_cf_nodes(tctx, &func->cf_node, &func->body, &impl->body); ralloc_free(tctx->float_types);