pco: initial control-flow support

Signed-off-by: Simon Perretta <simon.perretta@imgtec.com>
Acked-by: Erik Faye-Lund <erik.faye-lund@collabora.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/36412>
This commit is contained in:
Simon Perretta
2025-01-01 14:26:00 +00:00
committed by Marge Bot
parent 5ce4bc4671
commit 6d72ef6c0f
9 changed files with 260 additions and 3 deletions
+1
View File
@@ -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',
+210
View File
@@ -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 <assert.h>
#include <stdbool.h>
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;
}
+7
View File
@@ -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;
+3
View File
@@ -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);
+7
View File
@@ -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.
*/
+1
View File
@@ -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)) {
+4
View File
@@ -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).
*/
-2
View File
@@ -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;
}
+27 -1
View File
@@ -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);