diff --git a/src/imagination/pco/meson.build b/src/imagination/pco/meson.build index a912cca2666..7db2e010dc4 100644 --- a/src/imagination/pco/meson.build +++ b/src/imagination/pco/meson.build @@ -12,6 +12,7 @@ libpowervr_compiler_files = files( 'pco_group_instrs.c', 'pco_index.c', 'pco_ir.c', + 'pco_legalize.c', 'pco_nir.c', 'pco_nir_pvfio.c', 'pco_opt.c', diff --git a/src/imagination/pco/pco_internal.h b/src/imagination/pco/pco_internal.h index c53b0b586c5..64a3d4b736a 100644 --- a/src/imagination/pco/pco_internal.h +++ b/src/imagination/pco/pco_internal.h @@ -337,6 +337,7 @@ typedef struct _pco_shader { const char *name; /** Shader name. */ bool is_internal; /** Whether this is an internal shader. */ bool is_grouped; /** Whether the shader uses igrps. */ + bool is_legalized; /** Whether the shader has been legalized. */ struct list_head funcs; /** List of functions. */ unsigned next_func; /** Next function index. */ @@ -1112,6 +1113,7 @@ bool pco_dce(pco_shader *shader); bool pco_end(pco_shader *shader); bool pco_group_instrs(pco_shader *shader); bool pco_index(pco_shader *shader, bool skip_ssa); +bool pco_legalize(pco_shader *shader); bool pco_nir_pfo(nir_shader *nir, pco_fs_data *fs); bool pco_nir_pvi(nir_shader *nir, pco_vs_data *vs); bool pco_opt(pco_shader *shader); @@ -1946,6 +1948,47 @@ static inline bool pco_refs_are_equal(pco_ref ref0, pco_ref ref1) return true; } +/** + * \brief Checks a reference has a valid hardware source mapping. + * + * \param[in] ref Reference. + * \param[in] mapped_src Hardware source mapping. + * \param[out] needs_s124 Whether the mapping needs to use S{1,2,4} + * rather than S{0,2,3}. + * \return True if the mapping is valid. + */ +static inline bool +ref_src_map_valid(pco_ref ref, enum pco_io mapped_src, bool *needs_s124) +{ + if (needs_s124) + *needs_s124 = false; + + /* Restrictions only apply to hardware registers. */ + if (!pco_ref_is_idx_reg(ref) && !pco_ref_is_reg(ref)) + return true; + + switch (pco_ref_get_reg_class(ref)) { + case PCO_REG_CLASS_COEFF: + case PCO_REG_CLASS_SHARED: + case PCO_REG_CLASS_INDEX: + case PCO_REG_CLASS_PIXOUT: + return (mapped_src == PCO_IO_S0) || (mapped_src == PCO_IO_S2) || + (mapped_src == PCO_IO_S3); + + case PCO_REG_CLASS_SPEC: + if (needs_s124) + *needs_s124 = true; + + return (mapped_src == PCO_IO_S1) || (mapped_src == PCO_IO_S2) || + (mapped_src == PCO_IO_S4); + + default: + return true; + } + + return false; +} + /** * \brief Returns whether none of the lower/upper sources in an instruction * group are set. diff --git a/src/imagination/pco/pco_ir.c b/src/imagination/pco/pco_ir.c index fbd05cafcd2..7b2dfe70795 100644 --- a/src/imagination/pco/pco_ir.c +++ b/src/imagination/pco/pco_ir.c @@ -60,6 +60,7 @@ void pco_process_ir(pco_ctx *ctx, pco_shader *shader) * time a drc result is used. */ PCO_PASS(_, shader, pco_schedule); + PCO_PASS(_, shader, pco_legalize); PCO_PASS(_, shader, pco_ra); PCO_PASS(_, shader, pco_end); PCO_PASS(_, shader, pco_group_instrs); diff --git a/src/imagination/pco/pco_legalize.c b/src/imagination/pco/pco_legalize.c new file mode 100644 index 00000000000..2ef9f425388 --- /dev/null +++ b/src/imagination/pco/pco_legalize.c @@ -0,0 +1,136 @@ +/* + * Copyright © 2025 Imagination Technologies Ltd. + * + * SPDX-License-Identifier: MIT + */ + +/** + * \file pco_legalize.c + * + * \brief PCO legalizing pass. + */ + +#include "pco.h" +#include "pco_builder.h" +#include "pco_internal.h" +#include "util/macros.h" + +#include + +/** + * \brief Insert a mov to legalize how a hardware register is referenced. + * + * \param[in,out] instr PCO instr. + * \param[in,out] ref Reference to be legalized. + * \param[in] needs_s124 Whether the mapping needs to use S{1,2,4} + * rather than S{0,2,3}. + * \return True if progress was made. + */ +static void insert_mov_ref(pco_instr *instr, pco_ref *ref, bool needs_s124) +{ + assert(pco_ref_is_scalar(*ref)); + pco_ref new_ref = pco_ref_new_ssa(instr->parent_func, + pco_ref_get_bits(*ref), + pco_ref_get_chans(*ref)); + + pco_ref_xfer_mods(&new_ref, ref, true); + + pco_builder b = + pco_builder_create(instr->parent_func, pco_cursor_before_instr(instr)); + + if (needs_s124) + pco_movs1(&b, new_ref, *ref); + else + pco_mbyp(&b, new_ref, *ref); + + *ref = new_ref; +} + +/** + * \brief Try to legalize an instruction's hardware source mappings. + * + * \param[in,out] instr PCO instr. + * \param[in] info PCO op info. + * \return True if progress was made. + */ +static bool try_legalize_src_mappings(pco_instr *instr, + const struct pco_op_info *info) +{ + bool progress = false; + bool needs_s124; + + /* Check dests. */ + pco_foreach_instr_dest (pdest, instr) { + unsigned dest_index = pdest - instr->dest; + if (!info->dest_intrn_map[dest_index]) + continue; + + enum pco_io mapped_src = PCO_IO_S0 + info->dest_intrn_map[dest_index] - 1; + + if (ref_src_map_valid(*pdest, mapped_src, &needs_s124)) + continue; + + insert_mov_ref(instr, pdest, needs_s124); + progress = true; + } + + /* Check srcs. */ + pco_foreach_instr_src (psrc, instr) { + unsigned src_index = psrc - instr->src; + if (!info->src_intrn_map[src_index]) + continue; + + enum pco_io mapped_src = PCO_IO_S0 + info->src_intrn_map[src_index] - 1; + + if (ref_src_map_valid(*psrc, mapped_src, &needs_s124)) + continue; + + insert_mov_ref(instr, psrc, needs_s124); + progress = true; + } + + return progress; +} + +/** + * \brief Try to legalizes an instruction. + * + * \param[in,out] instr PCO instr. + * \return True if progress was made. + */ +static bool try_legalize(pco_instr *instr) +{ + const struct pco_op_info *info = &pco_op_info[instr->op]; + bool progress = false; + + /* Skip pseudo instructions. */ + if (info->type == PCO_OP_TYPE_PSEUDO) + return false; + + progress |= try_legalize_src_mappings(instr, info); + + return progress; +} + +/** + * \brief Legalizes instructions where additional restrictions apply. + * + * \param[in,out] shader PCO shader. + * \return True if the pass made progress. + */ +bool pco_legalize(pco_shader *shader) +{ + bool progress = false; + + assert(!shader->is_grouped); + assert(!shader->is_legalized); + + pco_foreach_func_in_shader (func, shader) { + pco_foreach_instr_in_func_safe (instr, func) { + progress |= try_legalize(instr); + } + } + + shader->is_legalized = true; + return progress; +} diff --git a/src/imagination/pco/pco_map.py b/src/imagination/pco/pco_map.py index 01ed4452a8f..682f39452e7 100644 --- a/src/imagination/pco/pco_map.py +++ b/src/imagination/pco/pco_map.py @@ -781,6 +781,18 @@ encode_map(O_MOVC, ] ) +encode_map(O_MOVWM, + encodings=[ + (I_MOVC_EXT, [ + ('movw0', ('pco_ref_get_movw01', SRC(0))), + ('movw1', 0), + ('maskw0', (RM_ELEM, DEST(0))), + ('aw', True), + ('p2end', OM_PHASE2END) + ]) + ] +) + encode_map(O_ADD64_32, encodings=[ (I_INT32_64_EXT, [ @@ -1082,6 +1094,52 @@ group_map(O_UNPCK, dests=[('w[0]', ('0', DEST(0)), 'ft0')] ) +group_map(O_MOVWM, + hdr=(I_IGRP_HDR_MAIN, [ + ('oporg', 'p0_p2'), + ('olchk', OM_OLCHK), + ('w1p', False), + ('w0p', True), + ('cc', OM_EXEC_CND), + ('end', OM_END), + ('atom', OM_ATOM), + ('rpt', OM_RPT) + ]), + enc_ops=[ + ('0', O_MBYP, ['ft0'], [SRC(0)]), + ('2_mov', O_MOVWM, [DEST(0)], ['ft0', SRC(1)], [(OM_PHASE2END, OM_PHASE2END)]) + ], + srcs=[ + ('s[0]', ('0', SRC(0)), 's0'), + ('s[1]', ('2_mov', SRC(1)), 'is4') + ], + iss=[ + ('is[0]', 's1'), + ('is[4]', 'fte') + ], + dests=[('w[0]', ('2_mov', DEST(0)), 'w0')] +) + +group_map(O_MOVS1, + hdr=(I_IGRP_HDR_MAIN, [ + ('oporg', 'p2'), + ('olchk', OM_OLCHK), + ('w1p', False), + ('w0p', True), + ('cc', OM_EXEC_CND), + ('end', OM_END), + ('atom', OM_ATOM), + ('rpt', OM_RPT) + ]), + enc_ops=[('2_mov', O_MOVWM, [DEST(0)], [SRC(0), 'is4'], [(OM_PHASE2END, True)])], + srcs=[('s[1]', ('2_mov', SRC(0)), 'fte')], + iss=[ + ('is[0]', 's1'), + ('is[4]', 'fte') + ], + dests=[('w[0]', ('2_mov', DEST(0)), 'w0')] +) + group_map(O_ADD64_32, hdr=(I_IGRP_HDR_MAIN, [ ('oporg', 'p0'), diff --git a/src/imagination/pco/pco_ops.py b/src/imagination/pco/pco_ops.py index 60ad90b29da..d9748d87257 100644 --- a/src/imagination/pco/pco_ops.py +++ b/src/imagination/pco/pco_ops.py @@ -303,6 +303,9 @@ O_UNPCK = hw_op('unpck', OM_ALU + [OM_PCK_FMT, OM_ROUNDZERO, OM_SCALE], 1, 1, [] O_TST = hw_direct_op('tst', [OM_TST_OP_MAIN, OM_PHASE2END, OM_TST_TYPE_MAIN], 2, 2, [], [[RM_ELEM], [RM_ELEM]]) O_MOVC = hw_direct_op('movc', [OM_PHASE2END], 2, 5, [[RM_ELEM]]) +O_MOVWM = hw_op('movwm', OM_ALU + [OM_PHASE2END], 1, 2, [[RM_ELEM]]) +O_MOVS1 = hw_op('movs1', OM_ALU, 1, 1) + # TODO # O_PCK_ELEM = pseudo_op('pck.elem', OM_ALU_RPT1 + [OM_PCK_FMT, OM_ROUNDZERO, OM_SCALE], 1, 2) diff --git a/src/imagination/pco/pco_validate.c b/src/imagination/pco/pco_validate.c index a9b205c21a4..0f432472798 100644 --- a/src/imagination/pco/pco_validate.c +++ b/src/imagination/pco/pco_validate.c @@ -152,12 +152,85 @@ static void pco_validate_ssa(struct val_state *state) "SSA destination defined to more than once"); BITSET_SET(ssa_writes, pdest->val); } + + state->instr = NULL; + state->cf_node = NULL; } ralloc_free(ssa_writes); state->func = NULL; state->ref = NULL; + state->ref_cursor = REF_CURSOR_NONE; + } +} + +/** + * \brief Validates hardware source mappings. + * + * \param[in,out] state Validation state. + */ +static void pco_validate_src_maps(struct val_state *state) +{ + /* Only check after the legalize pass has run. */ + if (!state->shader->is_legalized) + return; + + bool needs_s124; + pco_foreach_func_in_shader (func, state->shader) { + state->func = func; + + pco_foreach_instr_in_func (instr, func) { + const struct pco_op_info *info = &pco_op_info[instr->op]; + if (info->type == PCO_OP_TYPE_PSEUDO) + continue; + + state->cf_node = &instr->parent_block->cf_node; + state->instr = instr; + + state->ref_cursor = REF_CURSOR_INSTR_DEST; + pco_foreach_instr_dest (pdest, instr) { + state->ref = pdest; + unsigned dest_index = pdest - instr->dest; + + if (!info->dest_intrn_map[dest_index]) + continue; + + enum pco_io mapped_src = + PCO_IO_S0 + info->dest_intrn_map[dest_index] - 1; + + bool valid = ref_src_map_valid(*pdest, mapped_src, &needs_s124); + PCO_ASSERT(state, + valid, + "HW register reference should be mapped to %s", + needs_s124 ? "S1/S2/S4" : "S0/S2/S3"); + } + + state->ref_cursor = REF_CURSOR_INSTR_SRC; + pco_foreach_instr_src (psrc, instr) { + state->ref = psrc; + unsigned src_index = psrc - instr->src; + + if (!info->src_intrn_map[src_index]) + continue; + + enum pco_io mapped_src = + PCO_IO_S0 + info->src_intrn_map[src_index] - 1; + + bool valid = ref_src_map_valid(*psrc, mapped_src, &needs_s124); + PCO_ASSERT(state, + valid, + "HW register reference should be mapped to %s", + needs_s124 ? "S1/S2/S4" : "S0/S2/S3"); + } + + state->instr = NULL; + state->cf_node = NULL; + } + + state->func = NULL; + state->ref = NULL; + state->ref_cursor = REF_CURSOR_NONE; } } @@ -179,8 +252,10 @@ void pco_validate_shader(UNUSED pco_shader *shader, UNUSED const char *when) .phase = -1, }; - if (!shader->is_grouped) + if (!shader->is_grouped) { pco_validate_ssa(&state); + pco_validate_src_maps(&state); + } puts("finishme: pco_validate_shader"); #endif /* NDEBUG */