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:
committed by
Marge Bot
parent
5ce4bc4671
commit
6d72ef6c0f
@@ -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',
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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).
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user