diff --git a/docs/envvars.rst b/docs/envvars.rst index 5b8c058b2fa..cc4e0c47bfa 100644 --- a/docs/envvars.rst +++ b/docs/envvars.rst @@ -1594,6 +1594,32 @@ PowerVR driver environment variables A comma-separated list of debug options. Use `PVR_DEBUG=help` to print a list of available options. +.. envvar:: ROGUE_DEBUG + + a comma-separated list of named flags for the Rogue compiler, + which do various things: + + ``nir`` + Print the input NIR to stdout. + ``nir_passes`` + Print the output of each NIR pass to stdout. + ``ir`` + Print the input Rogue IR to stdout. + ``ir_passes`` + Print the output of each Rogue IR pass to stdout. + ``ir_details`` + Includes additional details when printing Rogue IR. + ``vld_skip`` + Skips the compiler validation step. + ``vld_nonfatal`` + Prints all the validation errors instead of stopping after the first. + +.. envvar:: ROGUE_COLOR + + if set to ``auto`` Rogue IR will be colorized if stdout is not a pipe. + Color is forced off if set to ``off``/``0`` or on if set to ``on``/``1``. + Defaults to ``auto``. + i915 driver environment variables --------------------------------- diff --git a/src/imagination/.clang-format b/src/imagination/.clang-format index af3a4e06169..17106e7baaa 100644 --- a/src/imagination/.clang-format +++ b/src/imagination/.clang-format @@ -226,6 +226,38 @@ ForEachMacros: [ 'nir_foreach_use', 'nir_foreach_use_safe', 'nir_foreach_variable_with_modes', + 'rogue_foreach_block', + 'rogue_foreach_block_safe', + 'rogue_foreach_block_rev', + 'rogue_foreach_block_safe_rev', + 'rogue_foreach_block_use', + 'rogue_foreach_block_use_safe', + 'rogue_foreach_drc_trxn', + 'rogue_foreach_drc_trxn_safe', + 'rogue_foreach_phase_in_set', + 'rogue_foreach_phase_in_set_rev', + 'rogue_foreach_imm_use', + 'rogue_foreach_imm_use_safe', + 'rogue_foreach_instr_group_in_block', + 'rogue_foreach_instr_group_in_block_safe', + 'rogue_foreach_instr_group_in_shader', + 'rogue_foreach_instr_group_in_shader_safe', + 'rogue_foreach_instr_in_block', + 'rogue_foreach_instr_in_block_safe', + 'rogue_foreach_instr_in_block_rev', + 'rogue_foreach_instr_in_block_safe_rev', + 'rogue_foreach_instr_in_shader', + 'rogue_foreach_instr_in_shader_safe', + 'rogue_foreach_instr_in_shader_rev', + 'rogue_foreach_instr_in_shader_safe_rev', + 'rogue_foreach_reg', + 'rogue_foreach_reg_safe', + 'rogue_foreach_reg_use', + 'rogue_foreach_reg_use_safe', + 'rogue_foreach_reg_write', + 'rogue_foreach_reg_write_safe', + 'rogue_foreach_regarray', + 'rogue_foreach_regarray_safe', 'rb_tree_foreach', 'rb_tree_foreach_safe', 'u_foreach_bit', diff --git a/src/imagination/rogue/meson.build b/src/imagination/rogue/meson.build index 2431f4f1f0d..49c27002656 100644 --- a/src/imagination/rogue/meson.build +++ b/src/imagination/rogue/meson.build @@ -21,25 +21,35 @@ with_imagination_tools = with_tools.contains('imagination') +inc_rogue = include_directories([ + '.', +]) + libpowervr_rogue_files = files( - 'nir/rogue_nir_constreg.c', 'nir/rogue_nir_lower_io.c', 'nir/rogue_nir_pfo.c', 'rogue.c', 'rogue_build_data.c', - 'rogue_compiler.c', + 'rogue_builder.c', + 'rogue_compile.c', 'rogue_constreg.c', - 'rogue_dump.c', + 'rogue_debug.c', 'rogue_encode.c', - 'rogue_encoders.c', - 'rogue_instr.c', + 'rogue_info.c', 'rogue_nir.c', - 'rogue_operand.c', - 'rogue_regalloc.c', - 'rogue_shader.c', - 'rogue_util.c', + 'rogue_print.c', 'rogue_validate.c', + + 'passes/rogue_constreg.c', + 'passes/rogue_copy_prop.c', + 'passes/rogue_dce.c', + 'passes/rogue_lower_pseudo_ops.c', + 'passes/rogue_regalloc.c', + 'passes/rogue_schedule_instr_groups.c', + 'passes/rogue_schedule_uvsw.c', + 'passes/rogue_schedule_wdf.c', + 'passes/rogue_trim.c', ) libpowervr_rogue = shared_library( @@ -61,20 +71,4 @@ libpowervr_rogue = shared_library( install : true, ) -rogue_compiler = executable( - 'rogue_compiler', - 'tools/offline_compiler.c', - link_with : [libpowervr_rogue], - dependencies : [idep_mesautil, idep_nir], - include_directories : [ - inc_mesa, - inc_include, - inc_src, - inc_mapi, - inc_gallium, - inc_gallium_aux, - inc_compiler, - ], - build_by_default : with_imagination_tools, - install : with_imagination_tools, -) +subdir('tools') diff --git a/src/imagination/rogue/nir/rogue_nir_constreg.c b/src/imagination/rogue/nir/rogue_nir_constreg.c deleted file mode 100644 index 30bda0cc971..00000000000 --- a/src/imagination/rogue/nir/rogue_nir_constreg.c +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright © 2022 Imagination Technologies Ltd. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "nir/nir.h" -#include "nir/nir_builder.h" -#include "nir/nir_search_helpers.h" -#include "rogue_constreg.h" -#include "rogue_nir.h" - -/* TODO: optimize: if value is in const regs, replace, else, use shared regs and - * notify driver they need to be populated? - */ - -/* Replaces multiple ssa uses from load_const with a single use -> a register. - */ -void rogue_nir_constreg(nir_shader *shader) -{ - nir_function_impl *impl = nir_shader_get_entrypoint(shader); - nir_builder b; - - nir_builder_init(&b, impl); - - /* Find load_const instructions. */ - nir_foreach_block (block, impl) { - nir_foreach_instr_safe (instr, block) { - if (instr->type != nir_instr_type_load_const) - continue; - - nir_load_const_instr *load_const = nir_instr_as_load_const(instr); - - /* Skip values that can be pulled from constant registers. */ - uint32_t value = nir_const_value_as_uint(load_const->value[0], 32); - size_t const_reg = rogue_constreg_lookup(value); - if (const_reg != ROGUE_NO_CONST_REG) - continue; - - b.cursor = nir_after_instr(&load_const->instr); - nir_ssa_def *mov = nir_mov(&b, &load_const->def); - - nir_foreach_use_safe (use_src, &load_const->def) { - if (use_src->parent_instr == mov->parent_instr) - continue; - - /* Skip when used as an index for intrinsics, as we want to - * access that value directly. - */ - if (use_src->parent_instr->type == nir_instr_type_intrinsic) - continue; - - nir_instr_rewrite_src_ssa(use_src->parent_instr, use_src, mov); - } - } - } -} diff --git a/src/imagination/rogue/nir/rogue_nir_lower_io.c b/src/imagination/rogue/nir/rogue_nir_lower_io.c index dba7e5de3bf..cb61f96b4d9 100644 --- a/src/imagination/rogue/nir/rogue_nir_lower_io.c +++ b/src/imagination/rogue/nir/rogue_nir_lower_io.c @@ -21,14 +21,21 @@ * SOFTWARE. */ -#include -#include - #include "nir/nir.h" #include "nir/nir_builder.h" #include "nir/nir_search_helpers.h" -#include "rogue_nir.h" -#include "rogue_nir_helpers.h" +#include "rogue.h" +#include "util/macros.h" + +#include +#include +#include + +/** + * \file rogue_nir_lower_io.c + * + * \brief Contains the rogue_nir_lower_io pass. + */ static void lower_vulkan_resource_index(nir_builder *b, nir_intrinsic_instr *intr, @@ -67,7 +74,7 @@ static void lower_load_ubo_to_scalar(nir_builder *b, nir_intrinsic_instr *intr) nir_ssa_def *loads[NIR_MAX_VEC_COMPONENTS]; for (uint8_t i = 0; i < intr->num_components; i++) { - size_t scaled_range = nir_intrinsic_range(intr) / intr->num_components; + unsigned scaled_range = nir_intrinsic_range(intr) / intr->num_components; nir_intrinsic_instr *chan_intr = nir_intrinsic_instr_create(b->shader, intr->intrinsic); nir_ssa_dest_init(&chan_intr->instr, @@ -155,6 +162,7 @@ static bool lower_impl(nir_function_impl *impl, void *layout) return progress; } +PUBLIC bool rogue_nir_lower_io(nir_shader *shader, void *layout) { bool progress = false; diff --git a/src/imagination/rogue/nir/rogue_nir_pfo.c b/src/imagination/rogue/nir/rogue_nir_pfo.c index d14d11b8240..c376c45fbc0 100644 --- a/src/imagination/rogue/nir/rogue_nir_pfo.c +++ b/src/imagination/rogue/nir/rogue_nir_pfo.c @@ -24,7 +24,14 @@ #include "nir/nir.h" #include "nir/nir_builder.h" #include "nir/nir_search_helpers.h" -#include "rogue_nir.h" +#include "rogue.h" +#include "util/macros.h" + +/** + * \file rogue_nir_pfo.c + * + * \brief Contains the rogue_nir_pfo pass. + */ static void insert_pfo(nir_builder *b, nir_intrinsic_instr *store_output, @@ -44,6 +51,7 @@ static void insert_pfo(nir_builder *b, nir_intrinsic_set_src_type(store_output, nir_type_uint32); } +PUBLIC void rogue_nir_pfo(nir_shader *shader) { nir_function_impl *impl = nir_shader_get_entrypoint(shader); diff --git a/src/imagination/rogue/rogue_dump.h b/src/imagination/rogue/passes/rogue_constreg.c similarity index 58% rename from src/imagination/rogue/rogue_dump.h rename to src/imagination/rogue/passes/rogue_constreg.c index 537f923cfac..0e113072154 100644 --- a/src/imagination/rogue/rogue_dump.h +++ b/src/imagination/rogue/passes/rogue_constreg.c @@ -21,24 +21,41 @@ * SOFTWARE. */ -#ifndef ROGUE_DUMP_H -#define ROGUE_DUMP_H - -#include -#include - -#include "rogue_instr.h" -#include "rogue_operand.h" -#include "rogue_shader.h" +#include "rogue.h" #include "util/macros.h" -PUBLIC -bool rogue_dump_operand(const struct rogue_operand *operand, FILE *fp); +#include -PUBLIC -bool rogue_dump_instr(const struct rogue_instr *instr, FILE *fp); +/** + * \file rogue_constreg.c + * + * \brief Contains the rogue_constreg pass. + */ +/* Converts immediate values to constant register values. */ +/* TODO: For values that aren't in constant registers, either insert a bitwise + * mov, or ask driver to put it into shared regs. */ PUBLIC -bool rogue_dump_shader(const struct rogue_shader *shader, FILE *fp); +bool rogue_constreg(rogue_shader *shader) +{ + if (shader->is_grouped) + return false; -#endif /* ROGUE_DUMP_H */ + bool progress = false; + + rogue_foreach_imm_use_safe (imm_use, shader) { + unsigned index = rogue_constreg_lookup(*imm_use->imm); + if (index == ROGUE_NO_CONST_REG) + unreachable("Immediate value not in constant registers."); + + rogue_reg *reg = rogue_const_reg(shader, index); + + struct list_head imm_use_link = imm_use->link; + if (rogue_src_imm_replace(imm_use, reg)) { + list_del(&imm_use_link); + progress |= true; + } + } + + return progress; +} diff --git a/src/imagination/rogue/passes/rogue_copy_prop.c b/src/imagination/rogue/passes/rogue_copy_prop.c new file mode 100644 index 00000000000..a1102aeb9eb --- /dev/null +++ b/src/imagination/rogue/passes/rogue_copy_prop.c @@ -0,0 +1,160 @@ +/* + * Copyright © 2022 Imagination Technologies Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "rogue.h" +#include "rogue_builder.h" +#include "util/macros.h" + +#include + +/** + * \file rogue_copy_prop.c + * + * \brief Contains the rogue_copy_prop pass. + */ + +static bool can_back_prop(rogue_alu_instr *mov) +{ + /* TODO: Check for src/dst modifiers when support is added for them. */ + + if (!rogue_ref_is_reg(&mov->src[0].ref) || !rogue_ref_is_reg(&mov->dst.ref)) + return false; + + if (mov->src[0].ref.reg->regarray) + return false; + + /* Vertex outputs require uvsw.write; only back-propagate if the parent + * instruction is also a mov. */ + if (mov->dst.ref.reg->class == ROGUE_REG_CLASS_VTXOUT) { + rogue_reg_write *write = + list_first_entry(&mov->src[0].ref.reg->writes, rogue_reg_write, link); + + if (write->instr->type != ROGUE_INSTR_TYPE_ALU) + return false; + + rogue_alu_instr *alu = rogue_instr_as_alu(write->instr); + if (alu->op != ROGUE_ALU_OP_MOV) + return false; + } + + /* Is the source register only written to once? */ + if (!list_is_singular(&mov->src[0].ref.reg->writes)) + return false; + + /* Is this the only instruction that writes to this register? */ + if (!list_is_singular(&mov->dst.ref.reg->writes)) + return false; + + return true; +} + +static bool can_forward_prop(rogue_alu_instr *mov) +{ + /* TODO: Check for src/dst modifiers when support is added for them. */ + + if (!rogue_ref_is_reg(&mov->src[0].ref) || !rogue_ref_is_reg(&mov->dst.ref)) + return false; + + if (mov->dst.ref.reg->regarray) + return false; + + if (mov->dst.ref.reg->class != ROGUE_REG_CLASS_SSA) + return false; + + /* Is the source register written to more than once (driver-supplied regs can + * have zero writes)? */ + if (list_length(&mov->src[0].ref.reg->writes) > 1) + return false; + + /* Is this the only instruction that writes to this register? */ + if (!list_is_singular(&mov->dst.ref.reg->writes)) + return false; + + return true; +} + +static bool rogue_back_prop(rogue_alu_instr *mov) +{ + rogue_reg *mov_src = mov->src[0].ref.reg; + rogue_reg *mov_dst = mov->dst.ref.reg; + + rogue_reg_write *write = + list_first_entry(&mov_src->writes, rogue_reg_write, link); + + if (!rogue_dst_reg_replace(write, mov_dst)) + return false; + + rogue_instr_delete(&mov->instr); + + return true; +} + +static bool rogue_forward_prop(rogue_alu_instr *mov) +{ + bool success = true; + + rogue_reg *mov_src = mov->src[0].ref.reg; + rogue_reg *mov_dst = mov->dst.ref.reg; + + rogue_foreach_reg_use_safe (use, mov_dst) + if (rogue_can_replace_reg_use(use, mov_src)) + success &= rogue_src_reg_replace(use, mov_src); + else + success = false; + + if (!success) + return false; + + rogue_instr_delete(&mov->instr); + + return true; +} + +/* Copy propagation pass. */ +/* Forward and back, so that regarrays can be handled to a degree (making sure + * to propagate the regarray register rather than replace it and mess up the + * contiguous numbering). */ +PUBLIC +bool rogue_copy_prop(rogue_shader *shader) +{ + if (shader->is_grouped) + return false; + + bool progress = false; + + rogue_foreach_instr_in_shader_safe (instr, shader) { + if (instr->type != ROGUE_INSTR_TYPE_ALU) + continue; + + rogue_alu_instr *mov = rogue_instr_as_alu(instr); + if (mov->op != ROGUE_ALU_OP_MOV) + continue; + + if (can_forward_prop(mov)) + progress |= rogue_forward_prop(mov); + else if (can_back_prop(mov)) + progress |= rogue_back_prop(mov); + } + + return progress; +} diff --git a/src/imagination/rogue/passes/rogue_dce.c b/src/imagination/rogue/passes/rogue_dce.c new file mode 100644 index 00000000000..6acbdd20fcb --- /dev/null +++ b/src/imagination/rogue/passes/rogue_dce.c @@ -0,0 +1,133 @@ +/* + * Copyright © 2022 Imagination Technologies Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "rogue.h" +#include "util/macros.h" + +#include + +/** + * \file rogue_dce.c + * + * \brief Contains the rogue_dce pass. + */ + +/* TODO: + * 0) Add bools/flags to registers in rogue_info that specifies whether they can + * be I/O registers (i.e. populated by driver/used by driver). + * 1) Loop through instructions and delete ones that have their destinations + * unused (with exception of pixel output, shared, etc.). + * 2) Loop through registers and delete ones that have no uses/writes. + */ + +static bool rogue_dce_alu_instr(rogue_alu_instr *alu) +{ + bool progress = false; + + switch (alu->op) { + case ROGUE_ALU_OP_MOV: + case ROGUE_ALU_OP_MBYP: + if (!alu->mod && rogue_alu_dst_src_equal(&alu->dst, &alu->src[0])) { + rogue_instr_delete(&alu->instr); + progress = true; + } + break; + + default: + break; + } + + return progress; +} + +static bool rogue_dce_instrs(rogue_shader *shader) +{ + bool progress = false; + + rogue_foreach_instr_in_shader_safe (instr, shader) { + switch (instr->type) { + case ROGUE_INSTR_TYPE_ALU: + progress |= rogue_dce_alu_instr(rogue_instr_as_alu(instr)); + break; + + case ROGUE_INSTR_TYPE_BACKEND: + break; + + case ROGUE_INSTR_TYPE_CTRL: + break; + + default: + unreachable("Unsupported instruction type."); + return false; + } + } + + return progress; +} + +/* TODO: Do this in rogue_trim instead? */ +static bool rogue_try_release_reg(rogue_reg *reg) +{ + /* Check if the register is used or written to. */ + if (!rogue_reg_is_unused(reg)) + return false; + + /* Check if the register is part of a regarray. */ + if (reg->regarray) + return false; + + /* Register is unused, delete it. */ + rogue_reg_delete(reg); + + return true; +} + +static bool rogue_dce_regs(rogue_shader *shader) +{ + bool progress = false; + + /* Remove unused SSA/temp registers that aren't in any regarrays. */ + rogue_foreach_reg_safe (reg, shader, ROGUE_REG_CLASS_SSA) { + progress |= rogue_try_release_reg(reg); + } + + rogue_foreach_reg_safe (reg, shader, ROGUE_REG_CLASS_TEMP) { + progress |= rogue_try_release_reg(reg); + } + + return progress; +} + +PUBLIC +bool rogue_dce(rogue_shader *shader) +{ + if (shader->is_grouped) + return false; + + bool progress = false; + + progress |= rogue_dce_instrs(shader); + progress |= rogue_dce_regs(shader); + + return progress; +} diff --git a/src/imagination/rogue/passes/rogue_lower_pseudo_ops.c b/src/imagination/rogue/passes/rogue_lower_pseudo_ops.c new file mode 100644 index 00000000000..26a1ed26619 --- /dev/null +++ b/src/imagination/rogue/passes/rogue_lower_pseudo_ops.c @@ -0,0 +1,166 @@ +/* + * Copyright © 2022 Imagination Technologies Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "rogue.h" +#include "rogue_builder.h" +#include "util/macros.h" + +#include + +/** + * \file rogue_lower_pseudo_ops.c + * + * \brief Contains the rogue_lower_pseudo_ops pass. + */ + +static inline bool rogue_lower_FABS(rogue_builder *b, rogue_alu_instr *fabs) +{ + rogue_alu_instr *mbyp = rogue_MBYP(b, fabs->dst.ref, fabs->src[0].ref); + rogue_merge_instr_comment(&mbyp->instr, &fabs->instr, "fabs"); + rogue_set_alu_src_mod(mbyp, 0, ROGUE_ALU_SRC_MOD_ABS); + rogue_instr_delete(&fabs->instr); + + return true; +} + +static inline bool rogue_lower_FNEG(rogue_builder *b, rogue_alu_instr *fneg) +{ + rogue_alu_instr *mbyp = rogue_MBYP(b, fneg->dst.ref, fneg->src[0].ref); + rogue_merge_instr_comment(&mbyp->instr, &fneg->instr, "fneg"); + rogue_set_alu_src_mod(mbyp, 0, ROGUE_ALU_SRC_MOD_NEG); + rogue_instr_delete(&fneg->instr); + + return true; +} + +static inline bool rogue_lower_FNABS(rogue_builder *b, rogue_alu_instr *fnabs) +{ + rogue_alu_instr *mbyp = rogue_MBYP(b, fnabs->dst.ref, fnabs->src[0].ref); + rogue_merge_instr_comment(&mbyp->instr, &fnabs->instr, "fnabs"); + rogue_set_alu_src_mod(mbyp, 0, ROGUE_ALU_SRC_MOD_ABS); + rogue_set_alu_src_mod(mbyp, 0, ROGUE_ALU_SRC_MOD_NEG); + rogue_instr_delete(&fnabs->instr); + + return true; +} + +static inline bool rogue_lower_MOV(rogue_builder *b, rogue_alu_instr *mov) +{ + rogue_instr *instr; + + /* If we're writing to a vertex output register, we need to use uvsw.write. + */ + if (rogue_ref_is_reg(&mov->dst.ref) && + mov->dst.ref.reg->class == ROGUE_REG_CLASS_VTXOUT) { + instr = &rogue_UVSW_WRITE(b, mov->dst.ref, mov->src[0].ref)->instr; + } else { + instr = &rogue_MBYP(b, mov->dst.ref, mov->src[0].ref)->instr; + } + + rogue_merge_instr_comment(instr, &mov->instr, "mov"); + rogue_instr_delete(&mov->instr); + + return true; +} + +static inline bool rogue_lower_alu_instr(rogue_builder *b, rogue_alu_instr *alu) +{ + switch (alu->op) { + case ROGUE_ALU_OP_MOV: + return rogue_lower_MOV(b, alu); + + case ROGUE_ALU_OP_FABS: + return rogue_lower_FABS(b, alu); + + case ROGUE_ALU_OP_FNEG: + return rogue_lower_FNEG(b, alu); + + case ROGUE_ALU_OP_FNABS: + return rogue_lower_FNABS(b, alu); + + default: + break; + } + + return false; +} + +static inline bool rogue_lower_END(rogue_builder *b, rogue_ctrl_instr *end) +{ + rogue_ctrl_instr *nop = rogue_NOP(b); + rogue_merge_instr_comment(&nop->instr, &end->instr, "end"); + rogue_set_ctrl_op_mod(nop, ROGUE_CTRL_OP_MOD_END); + rogue_instr_delete(&end->instr); + + return true; +} + +static inline bool rogue_lower_ctrl_instr(rogue_builder *b, + rogue_ctrl_instr *ctrl) +{ + switch (ctrl->op) { + case ROGUE_CTRL_OP_END: + return rogue_lower_END(b, ctrl); + + default: + break; + } + + return false; +} + +/* TODO: This should only really be called after a distribute_src_mods pass (to + * come later). */ +PUBLIC +bool rogue_lower_pseudo_ops(rogue_shader *shader) +{ + if (shader->is_grouped) + return false; + + bool progress = false; + + rogue_builder b; + rogue_builder_init(&b, shader); + + rogue_foreach_instr_in_shader_safe (instr, shader) { + /* Skip real ops. */ + if (rogue_instr_supported_phases(instr)) + continue; + + b.cursor = rogue_cursor_before_instr(instr); + switch (instr->type) { + case ROGUE_INSTR_TYPE_ALU: + progress |= rogue_lower_alu_instr(&b, rogue_instr_as_alu(instr)); + break; + + case ROGUE_INSTR_TYPE_CTRL: + progress |= rogue_lower_ctrl_instr(&b, rogue_instr_as_ctrl(instr)); + break; + + default: + continue; + } + } + + return progress; +} diff --git a/src/imagination/rogue/passes/rogue_regalloc.c b/src/imagination/rogue/passes/rogue_regalloc.c new file mode 100644 index 00000000000..75493a2700d --- /dev/null +++ b/src/imagination/rogue/passes/rogue_regalloc.c @@ -0,0 +1,281 @@ +/* + * Copyright © 2022 Imagination Technologies Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "rogue.h" +#include "util/macros.h" +#include "util/register_allocate.h" +#include "util/sparse_array.h" + +#include + +/** + * \file rogue_regalloc.c + * + * \brief Contains the rogue_regalloc pass. + */ + +/* TODO: Tweak this value. */ +#define ROGUE_SSA_LIVE_RANGE_NODE_SIZE 512 + +/* TODO: Internal register support for high register pressure regs. */ + +typedef struct rogue_live_range { + unsigned start; + unsigned end; +} rogue_live_range; + +PUBLIC +bool rogue_regalloc(rogue_shader *shader) +{ + if (shader->is_grouped) + return false; + + bool progress = false; + + unsigned num_ssa_regs = list_length(&shader->regs[ROGUE_REG_CLASS_SSA]); + if (!num_ssa_regs) + return false; + + /* If we already have some temps in use in the shader, we'll skip using them + * for allocation. */ + unsigned num_temp_regs = list_length(&shader->regs[ROGUE_REG_CLASS_TEMP]); + unsigned hw_temps = rogue_reg_infos[ROGUE_REG_CLASS_TEMP].num; + + struct ra_regs *ra_regs = ra_alloc_reg_set(shader, hw_temps, true); + + for (enum rogue_regalloc_class c = 0; c < ROGUE_REGALLOC_CLASS_COUNT; ++c) { + ASSERTED struct ra_class *ra_class = + ra_alloc_contig_reg_class(ra_regs, regalloc_info[c].stride); + assert(c == ra_class_index(ra_class)); + } + + for (unsigned t = num_temp_regs; t < hw_temps; ++t) + for (enum rogue_regalloc_class c = 0; c < ROGUE_REGALLOC_CLASS_COUNT; ++c) + if (!(t % regalloc_info[c].stride)) + ra_class_add_reg(ra_get_class_from_index(ra_regs, c), t); + + ra_set_finalize(ra_regs, NULL); + + struct util_sparse_array ssa_live_range; + util_sparse_array_init(&ssa_live_range, + sizeof(rogue_live_range), + ROGUE_SSA_LIVE_RANGE_NODE_SIZE); + + /* Populate live ranges. */ + rogue_foreach_reg (reg, shader, ROGUE_REG_CLASS_SSA) { + rogue_live_range *live_range = + util_sparse_array_get(&ssa_live_range, reg->index); + rogue_reg_write *write = + list_first_entry(®->writes, rogue_reg_write, link); + + live_range->start = write->instr->index; + live_range->end = live_range->start; + + rogue_foreach_reg_use (use, reg) + live_range->end = MAX2(live_range->end, use->instr->index); + + /* Here dirty represents whether the register has been added to the regset + * yet or not. */ + reg->dirty = false; + } + + struct ra_graph *ra_graph = + ra_alloc_interference_graph(ra_regs, num_ssa_regs); + ralloc_steal(ra_regs, ra_graph); + + /* Set register class for regarrays/vectors. */ + rogue_foreach_regarray (regarray, shader) { + enum rogue_reg_class class = regarray->regs[0]->class; + if (class != ROGUE_REG_CLASS_SSA) + continue; + + if (regarray->parent) + continue; + + if (regarray->size != 4) + unreachable("Unsupported regarray size."); + + ra_set_node_class(ra_graph, + regarray->regs[0]->index, + ra_get_class_from_index(ra_regs, + ROGUE_REGALLOC_CLASS_TEMP_4)); + + for (unsigned u = 0; u < regarray->size; ++u) + regarray->regs[u]->dirty = true; + } + + /* Set register class for "standalone" registers. */ + rogue_foreach_reg (reg, shader, ROGUE_REG_CLASS_SSA) { + if (reg->dirty) + continue; + + ra_set_node_class(ra_graph, + reg->index, + ra_get_class_from_index(ra_regs, + ROGUE_REGALLOC_CLASS_TEMP_1)); + reg->dirty = true; + } + + /* Build interference graph from overlapping live ranges. */ + for (unsigned index0 = 0; index0 < num_ssa_regs; ++index0) { + rogue_live_range *live_range0 = + util_sparse_array_get(&ssa_live_range, index0); + + for (unsigned index1 = 0; index1 < num_ssa_regs; ++index1) { + if (index0 == index1) + continue; + + rogue_live_range *live_range1 = + util_sparse_array_get(&ssa_live_range, index1); + + /* If the live ranges overlap, those register nodes interfere. */ + if (!(live_range0->start >= live_range1->end || + live_range1->start >= live_range0->end)) + ra_add_node_interference(ra_graph, index0, index1); + } + } + + /* Same src/dst interferences are disabled for the moment. + * This may need to be re-enabled in the future as certain instructions have + * restrictions on this. + */ +#if 0 + /* Add node interferences such that the same register can't be used for + * both an instruction's source and destination. + */ + rogue_foreach_instr_in_shader (instr, shader) { + switch (instr->type) { + case ROGUE_INSTR_TYPE_ALU: + { + const rogue_alu_instr *alu = rogue_instr_as_alu(instr); + const rogue_alu_op_info *info = &rogue_alu_op_infos[alu->op]; + + if (rogue_ref_get_reg_class(&alu->dst.ref) != ROGUE_REG_CLASS_SSA) + continue; + + for (unsigned s = 0; s < info->num_srcs; ++s) { + if (!rogue_ref_is_reg(&alu->src[s].ref)) + continue; + + if (rogue_ref_get_reg_class(&alu->src[s].ref) != ROGUE_REG_CLASS_SSA) + continue; + + ra_add_node_interference(ra_graph, rogue_ref_get_reg_index(&alu->dst.ref), rogue_ref_get_reg_index(&alu->src[s].ref)); + } + + break; + } + + case ROGUE_INSTR_TYPE_BACKEND: + { + const rogue_backend_instr *backend = rogue_instr_as_backend(instr); + const rogue_backend_op_info *info = &rogue_backend_op_infos[backend->op]; + + for (unsigned d = 0; d < info->num_dsts; ++d) { + if (rogue_ref_get_reg_class(&backend->dst[d].ref) != ROGUE_REG_CLASS_SSA) + continue; + + for (unsigned s = 0; s < info->num_srcs; ++s) { + if (!rogue_ref_is_reg(&backend->src[s].ref)) + continue; + + if (rogue_ref_get_reg_class(&backend->src[s].ref) != ROGUE_REG_CLASS_SSA) + continue; + + ra_add_node_interference(ra_graph, rogue_ref_get_reg_index(&backend->dst[d].ref), rogue_ref_get_reg_index(&backend->src[s].ref)); + } + } + + break; + } + + case ROGUE_INSTR_TYPE_CTRL: + { + /* TODO: Support control instructions with I/O. */ + break; + } + + default: + unreachable("Unsupported instruction type."); + break; + } + } +#endif + + /* TODO: Spilling support. */ + if (!ra_allocate(ra_graph)) + unreachable("Register allocation failed."); + + /* Replace regarray SSA registers with allocated physical registers first. + * Don't want to be in a situation where the reg is updated before the + * regarray. + */ + rogue_foreach_regarray (regarray, shader) { + enum rogue_reg_class class = regarray->regs[0]->class; + if (class != ROGUE_REG_CLASS_SSA) + continue; + + if (regarray->parent) + continue; + + unsigned start_index = regarray->regs[0]->index; + unsigned new_base_index = ra_get_node_reg(ra_graph, start_index); + for (unsigned u = 0; u < regarray->size; ++u) { + enum rogue_regalloc_class ra_class = + ra_class_index(ra_get_node_class(ra_graph, start_index + u)); + enum rogue_reg_class new_class = regalloc_info[ra_class].class; + + /* Register should not have already been used. */ + assert(!BITSET_TEST(shader->regs_used[new_class], new_base_index + u)); + progress |= rogue_reg_rewrite(shader, + regarray->regs[u], + new_class, + new_base_index + u); + } + } + + /* Replace remaining standalone SSA registers with allocated physical + * registers. */ + rogue_foreach_reg_safe (reg, shader, ROGUE_REG_CLASS_SSA) { + assert(!reg->regarray); + unsigned new_index = ra_get_node_reg(ra_graph, reg->index); + + enum rogue_regalloc_class ra_class = + ra_class_index(ra_get_node_class(ra_graph, reg->index)); + enum rogue_reg_class new_class = regalloc_info[ra_class].class; + + /* First time using new register, modify in place. */ + if (!BITSET_TEST(shader->regs_used[new_class], new_index)) { + progress |= rogue_reg_rewrite(shader, reg, new_class, new_index); + } else { + /* Register has already been used, replace references and delete. */ + assert(list_is_singular(®->writes)); /* SSA reg. */ + rogue_reg *new_reg = rogue_temp_reg(shader, new_index); + progress |= rogue_reg_replace(reg, new_reg); + } + } + + util_sparse_array_finish(&ssa_live_range); + ralloc_free(ra_regs); + return progress; +} diff --git a/src/imagination/rogue/passes/rogue_schedule_instr_groups.c b/src/imagination/rogue/passes/rogue_schedule_instr_groups.c new file mode 100644 index 00000000000..271ba8b83a6 --- /dev/null +++ b/src/imagination/rogue/passes/rogue_schedule_instr_groups.c @@ -0,0 +1,635 @@ +/* + * Copyright © 2022 Imagination Technologies Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "rogue.h" +#include "rogue_builder.h" +#include "util/macros.h" + +#include + +/** + * \file rogue_schedule_instr_groups.c + * + * \brief Contains the rogue_schedule_instr_groups pass. + */ + +static inline void rogue_set_io_sel(rogue_instr_group_io_sel *map, + enum rogue_io io, + rogue_ref *ref, + bool is_dst) +{ + /* Hookup feedthrough outputs to W0 using IS4. */ + if (is_dst && rogue_io_is_ft(io)) { + *(rogue_instr_group_io_sel_ref(map, ROGUE_IO_IS4)) = rogue_ref_io(io); + io = ROGUE_IO_W0; + } + + /* Pack source */ + if (!is_dst && io == ROGUE_IO_IS3) { + enum rogue_io src = ROGUE_IO_S0; + *(rogue_instr_group_io_sel_ref(map, ROGUE_IO_IS0)) = rogue_ref_io(src); + *(rogue_instr_group_io_sel_ref(map, ROGUE_IO_IS3)) = + rogue_ref_io(ROGUE_IO_FTE); + io = src; + } + + if (!is_dst && rogue_io_is_dst(io)) { + enum rogue_io dst_ft = (io == ROGUE_IO_W0 ? ROGUE_IO_IS4 : ROGUE_IO_IS5); + enum rogue_io src = ROGUE_IO_S0; + *(rogue_instr_group_io_sel_ref(map, dst_ft)) = rogue_ref_io(ROGUE_IO_FTE); + *(rogue_instr_group_io_sel_ref(map, ROGUE_IO_IS0)) = rogue_ref_io(src); + io = src; + } + + *(rogue_instr_group_io_sel_ref(map, io)) = *ref; +} + +/* TODO NEXT: Abort if anything in sel map is already set. */ +/* TODO NEXT: Assert that these are register refs being set. */ + +static void rogue_lower_alu_io(rogue_alu_instr *alu, rogue_instr_group *group) +{ + const rogue_alu_op_info *info = &rogue_alu_op_infos[alu->op]; + enum rogue_instr_phase phase = alu->instr.index; + + rogue_set_io_sel(&group->io_sel, + info->phase_io[phase].dst, + &alu->dst.ref, + true); + alu->dst.ref = rogue_ref_io(info->phase_io[phase].dst); + + for (unsigned u = 0; u < info->num_srcs; ++u) { + if (info->phase_io[phase].src[u] == ROGUE_IO_INVALID) + continue; + + rogue_set_io_sel(&group->io_sel, + info->phase_io[phase].src[u], + &alu->src[u].ref, + false); + alu->src[u].ref = rogue_ref_io(info->phase_io[phase].src[u]); + } +} + +static void rogue_lower_backend_io(rogue_backend_instr *backend, + rogue_instr_group *group) +{ + const rogue_backend_op_info *info = &rogue_backend_op_infos[backend->op]; + + for (unsigned u = 0; u < info->num_dsts; ++u) { + if (info->phase_io.dst[u] == ROGUE_IO_INVALID) + continue; + + rogue_set_io_sel(&group->io_sel, + info->phase_io.dst[u], + &backend->dst[u].ref, + true); + backend->dst[u].ref = rogue_ref_io(info->phase_io.dst[u]); + } + + for (unsigned u = 0; u < info->num_srcs; ++u) { + if (info->phase_io.src[u] == ROGUE_IO_INVALID) + continue; + + rogue_set_io_sel(&group->io_sel, + info->phase_io.src[u], + &backend->src[u].ref, + false); + backend->src[u].ref = rogue_ref_io(info->phase_io.src[u]); + } +} + +static void rogue_lower_ctrl_io(rogue_ctrl_instr *ctrl, + rogue_instr_group *group) +{ + /* TODO: Support control instructions with I/O. */ +} + +static void rogue_lower_instr_group_io(rogue_instr *instr, + rogue_instr_group *group) +{ + switch (instr->type) { + case ROGUE_INSTR_TYPE_ALU: + rogue_lower_alu_io(rogue_instr_as_alu(instr), group); + break; + + case ROGUE_INSTR_TYPE_BACKEND: + rogue_lower_backend_io(rogue_instr_as_backend(instr), group); + break; + + case ROGUE_INSTR_TYPE_CTRL: + rogue_lower_ctrl_io(rogue_instr_as_ctrl(instr), group); + break; + + default: + unreachable("Invalid instruction group type."); + } +} + +/* This function uses unreachables rather than asserts because some Rogue IR + * instructions are pseudo-instructions that need lowering on certain cores, but + * real instructions on others, so these mistakes are more likely to happen. + */ +static inline void rogue_instr_group_put(rogue_instr *instr, + rogue_instr_group *group) +{ + uint64_t supported_phases = rogue_instr_supported_phases(instr); + if (!supported_phases) + unreachable("Can't schedule pseudo-instructions."); + else if (!util_is_power_of_two_or_zero64(supported_phases)) + unreachable("Multi-phase instructions unsupported."); + + enum rogue_instr_phase phase = + rogue_get_supported_phase(supported_phases, group->header.phases); + if (phase == ROGUE_INSTR_PHASE_INVALID) + unreachable("Failed to schedule group instruction."); + + /* Update phases. */ + instr->group = group; + instr->index = phase; + group->instrs[phase] = instr; + group->header.phases |= BITFIELD_BIT(phase); + + /* Update repeat count. */ + group->header.repeat = instr->repeat; + instr->repeat = 0; + + /* Set end flag. */ + group->header.end = instr->end; + + /* Set conditional execution flag. */ + /* TODO: Set this from the instruction. */ + group->header.exec_cond = ROGUE_EXEC_COND_PE_TRUE; + + /* Lower I/O to sources/destinations/ISS. */ + rogue_lower_instr_group_io(instr, group); +} + +static inline void rogue_move_instr_to_group(rogue_instr *instr, + rogue_instr_group *group) +{ + /* Remove instruction from block instructions list. */ + list_del(&instr->link); + + /* ralloc_steal instr context from block to instruction group */ + ralloc_steal(group, instr); + + /* Assign instruction to instruction group. */ + rogue_instr_group_put(instr, group); +} + +static void rogue_lower_regs(rogue_shader *shader) +{ + rogue_foreach_reg (reg, shader, ROGUE_REG_CLASS_INTERNAL) { + rogue_reg_rewrite(shader, + reg, + ROGUE_REG_CLASS_SPECIAL, + reg->index + ROGUE_INTERNAL0_OFFSET); + } + + rogue_foreach_reg_safe (reg, shader, ROGUE_REG_CLASS_CONST) { + rogue_reg_rewrite(shader, reg, ROGUE_REG_CLASS_SPECIAL, reg->index); + } + + rogue_foreach_reg_safe (reg, shader, ROGUE_REG_CLASS_PIXOUT) { + rogue_reg_rewrite(shader, + reg, + ROGUE_REG_CLASS_SPECIAL, + reg->index + + (reg->index < ROGUE_PIXOUT_GROUP + ? ROGUE_PIXOUT0_OFFSET + : (ROGUE_PIXOUT4_OFFSET - ROGUE_PIXOUT_GROUP))); + } +} + +static unsigned rogue_reg_bank_bits(const rogue_ref *ref) +{ + const rogue_reg *reg; + + if (rogue_ref_is_reg(ref)) + reg = ref->reg; + else if (rogue_ref_is_regarray(ref)) + reg = ref->regarray->regs[0]; + else + unreachable("Non-register reference."); + + unsigned bits = util_last_bit(rogue_reg_bank_encoding(reg->class)); + return !bits ? 1 : bits; +} + +static unsigned rogue_reg_index_bits(const rogue_ref *ref) +{ + const rogue_reg *reg; + + if (rogue_ref_is_reg(ref)) + reg = ref->reg; + else if (rogue_ref_is_regarray(ref)) + reg = ref->regarray->regs[0]; + else + unreachable("Non-register reference."); + + unsigned bits = util_last_bit(reg->index); + return !bits ? 1 : bits; +} + +static void rogue_calc_dsts_size(rogue_instr_group *group) +{ + const rogue_instr_group_io_sel *io_sel = &group->io_sel; + + unsigned num_dsts = !rogue_ref_is_null(&io_sel->dsts[0]) + + !rogue_ref_is_null(&io_sel->dsts[1]); + unsigned bank_bits[ROGUE_ISA_DSTS] = { 0 }; + unsigned index_bits[ROGUE_ISA_DSTS] = { 0 }; + + if (!num_dsts) { + return; + } else if (num_dsts == 1) { + const rogue_ref *dst_ref = !rogue_ref_is_null(&io_sel->dsts[0]) + ? &io_sel->dsts[0] + : &io_sel->dsts[1]; + bank_bits[0] = rogue_reg_bank_bits(dst_ref); + index_bits[0] = rogue_reg_index_bits(dst_ref); + } else { + bank_bits[0] = rogue_reg_bank_bits(&io_sel->dsts[0]); + bank_bits[1] = rogue_reg_bank_bits(&io_sel->dsts[1]); + index_bits[0] = rogue_reg_index_bits(&io_sel->dsts[0]); + index_bits[1] = rogue_reg_index_bits(&io_sel->dsts[1]); + } + + for (unsigned u = 0; u < ROGUE_REG_DST_VARIANTS; ++u) { + const rogue_reg_dst_info *info = &rogue_reg_dst_infos[u]; + + if ((info->num_dsts < num_dsts) || (info->bank_bits[0] < bank_bits[0]) || + (info->bank_bits[1] < bank_bits[1]) || + (info->index_bits[0] < index_bits[0]) || + (info->index_bits[1] < index_bits[1])) + continue; + + group->encode_info.dst_index = u; + group->size.dsts = info->bytes; + group->size.total += group->size.dsts; + return; + } + + unreachable("Unable to encode instruction group dsts."); +} + +static void rogue_calc_iss_size(rogue_instr_group *group) +{ + group->size.iss = (group->header.alu == ROGUE_ALU_MAIN); + group->size.total += group->size.iss; +} + +static void rogue_calc_srcs_size(rogue_instr_group *group, bool upper_srcs) +{ + const rogue_instr_group_io_sel *io_sel = &group->io_sel; + unsigned mux_bits = 0; + + unsigned offset = upper_srcs ? 3 : 0; + const rogue_reg_src_info *info_array = + upper_srcs ? rogue_reg_upper_src_infos : rogue_reg_lower_src_infos; + + unsigned *src_index = upper_srcs ? &group->encode_info.upper_src_index + : &group->encode_info.lower_src_index; + unsigned *srcs = upper_srcs ? &group->size.upper_srcs + : &group->size.lower_srcs; + + /* Special case: some control instructions have no sources. */ + if (group->header.alu == ROGUE_ALU_CONTROL) { + const rogue_ctrl_instr *ctrl = + rogue_instr_as_ctrl(group->instrs[ROGUE_INSTR_PHASE_CTRL]); + if (!rogue_ctrl_op_has_srcs(ctrl->op)) + return; + } else if (!upper_srcs && group->header.alu == ROGUE_ALU_MAIN) { + /* Special case, IS0 */ + if (rogue_ref_is_io(&io_sel->iss[0])) { + switch (io_sel->iss[0].io) { + case ROGUE_IO_S0: + mux_bits = 0; + break; + case ROGUE_IO_S3: + mux_bits = 1; + break; + case ROGUE_IO_S4: + mux_bits = 2; + break; + case ROGUE_IO_S5: + mux_bits = 2; + break; + case ROGUE_IO_S1: + mux_bits = 3; + break; + case ROGUE_IO_S2: + mux_bits = 3; + break; + + default: + unreachable("IS0 set to invalid value."); + } + } + } + + unsigned num_srcs = 1; + if (!rogue_ref_is_null(&io_sel->srcs[2 + offset])) + num_srcs = 3; + else if (!rogue_ref_is_null(&io_sel->srcs[1 + offset])) + num_srcs = 2; + + unsigned bank_bits[ROGUE_ISA_SRCS / 2] = { 0 }; + unsigned index_bits[ROGUE_ISA_SRCS / 2] = { 0 }; + + for (unsigned u = 0; u < ARRAY_SIZE(bank_bits); ++u) { + const rogue_ref *src = &io_sel->srcs[u + offset]; + if (rogue_ref_is_null(src)) + continue; + + bank_bits[u] = rogue_reg_bank_bits(src); + index_bits[u] = rogue_reg_index_bits(src); + } + + for (unsigned u = 0; u < ROGUE_REG_SRC_VARIANTS; ++u) { + const rogue_reg_src_info *info = &info_array[u]; + + if ((info->num_srcs < num_srcs) || (info->mux_bits < mux_bits) || + (info->bank_bits[0] < bank_bits[0]) || + (info->bank_bits[1] < bank_bits[1]) || + (info->bank_bits[2] < bank_bits[2]) || + (info->index_bits[0] < index_bits[0]) || + (info->index_bits[1] < index_bits[1]) || + (info->index_bits[2] < index_bits[2])) { + continue; + } + + *src_index = u; + *srcs = info->bytes; + group->size.total += *srcs; + + return; + } + + unreachable("Unable to encode instruction group srcs."); +} + +#define SM(src_mod) ROGUE_ALU_SRC_MOD_##src_mod +#define DM(dst_mod) ROGUE_ALU_DST_MOD_##dst_mod +#define OM(op_mod) ROGUE_ALU_OP_MOD_##op_mod +static void rogue_calc_alu_instrs_size(rogue_instr_group *group, + rogue_alu_instr *alu, + enum rogue_instr_phase phase) +{ + switch (alu->op) { + /* TODO: All single source have 1 byte and optional extra byte w/ext, + * commonise some of these when adding support for more single source + * instructions. + */ + case ROGUE_ALU_OP_MBYP: + if (rogue_alu_src_mod_is_set(alu, 0, SM(NEG)) || + rogue_alu_src_mod_is_set(alu, 0, SM(ABS))) { + group->size.instrs[phase] = 2; + } else { + group->size.instrs[phase] = 1; + } + break; + + case ROGUE_ALU_OP_FMUL: + group->size.instrs[phase] = 1; + break; + + case ROGUE_ALU_OP_FMAD: + if (rogue_alu_op_mod_is_set(alu, OM(LP)) || + rogue_alu_src_mod_is_set(alu, 1, SM(ABS)) || + rogue_alu_src_mod_is_set(alu, 1, SM(NEG)) || + rogue_alu_src_mod_is_set(alu, 2, SM(FLR)) || + rogue_alu_src_mod_is_set(alu, 2, SM(ABS))) { + group->size.instrs[phase] = 2; + } else { + group->size.instrs[phase] = 1; + } + break; + + case ROGUE_ALU_OP_PCK_U8888: + group->size.instrs[phase] = 2; + break; + + default: + unreachable("Invalid alu op."); + } +} +#undef OM +#undef DM +#undef SM + +static void rogue_calc_backend_instrs_size(rogue_instr_group *group, + rogue_backend_instr *backend, + enum rogue_instr_phase phase) +{ + switch (backend->op) { + case ROGUE_BACKEND_OP_FITRP_PIXEL: + group->size.instrs[phase] = 2; + break; + + case ROGUE_BACKEND_OP_UVSW_WRITETHENEMITTHENENDTASK: + case ROGUE_BACKEND_OP_UVSW_WRITE: + group->size.instrs[phase] = 2; + break; + + case ROGUE_BACKEND_OP_UVSW_EMIT: + case ROGUE_BACKEND_OP_UVSW_ENDTASK: + case ROGUE_BACKEND_OP_UVSW_EMITTHENENDTASK: + group->size.instrs[phase] = 1; + break; + + default: + unreachable("Invalid backend op."); + } +} + +static void rogue_calc_ctrl_instrs_size(rogue_instr_group *group, + rogue_ctrl_instr *ctrl, + enum rogue_instr_phase phase) +{ + switch (ctrl->op) { + case ROGUE_CTRL_OP_WDF: + group->size.instrs[phase] = 0; + break; + + case ROGUE_CTRL_OP_NOP: + group->size.instrs[phase] = 1; + break; + + default: + unreachable("Invalid ctrl op."); + } +} + +static void rogue_calc_instrs_size(rogue_instr_group *group) +{ + rogue_foreach_phase_in_set (p, group->header.phases) { + const rogue_instr *instr = group->instrs[p]; + + switch (instr->type) { + case ROGUE_INSTR_TYPE_ALU: + rogue_calc_alu_instrs_size(group, rogue_instr_as_alu(instr), p); + break; + + case ROGUE_INSTR_TYPE_BACKEND: + rogue_calc_backend_instrs_size(group, + rogue_instr_as_backend(instr), + p); + break; + + case ROGUE_INSTR_TYPE_CTRL: + rogue_calc_ctrl_instrs_size(group, rogue_instr_as_ctrl(instr), p); + break; + + default: + unreachable("Invalid instruction type."); + } + + group->size.total += group->size.instrs[p]; + } +} + +static void rogue_calc_header_size(rogue_instr_group *group) +{ + group->size.header = 2; + if (group->header.alu != ROGUE_ALU_MAIN || + (group->header.end || group->header.repeat > 1 || + group->header.exec_cond > ROGUE_EXEC_COND_P0_TRUE)) { + group->size.header = 3; + } + + group->size.total += group->size.header; +} + +static void rogue_calc_padding_size(rogue_instr_group *group) +{ + group->size.word_padding = (group->size.total % 2); + group->size.total += group->size.word_padding; +} + +static void rogue_finalise_instr_group(rogue_instr_group *group) +{ + rogue_calc_dsts_size(group); + rogue_calc_iss_size(group); + rogue_calc_srcs_size(group, true); + rogue_calc_srcs_size(group, false); + rogue_calc_instrs_size(group); + rogue_calc_header_size(group); + rogue_calc_padding_size(group); +} + +static void rogue_finalise_shader_offsets(rogue_shader *shader) +{ + rogue_instr_group *penultimate_group = NULL; + rogue_instr_group *last_group = NULL; + + /* Set instruction group offsets. */ + unsigned offset = 0; + rogue_foreach_instr_group_in_shader (group, shader) { + group->size.offset = offset; + offset += group->size.total; + + penultimate_group = last_group; + last_group = group; + } + + /* Ensure the final instruction group has a total size and offset that are a + * multiple of the icache alignment. */ + unsigned total_align = last_group->size.total % ROGUE_ISA_ICACHE_ALIGN; + unsigned offset_align = last_group->size.offset % ROGUE_ISA_ICACHE_ALIGN; + + if (total_align) { + unsigned padding = ROGUE_ISA_ICACHE_ALIGN - total_align; + /* Pad the size of the last instruction. */ + last_group->size.align_padding += padding; + last_group->size.total += padding; + } + + if (offset_align) { + unsigned padding = ROGUE_ISA_ICACHE_ALIGN - offset_align; + /* Pad the size of the penultimate instruction. */ + penultimate_group->size.align_padding += padding; + penultimate_group->size.total += padding; + /* Update the offset of the last instruction. */ + last_group->size.offset += padding; + } +} + +/* TODO: This just puts single instructions into groups for now. Later we need + * to: + * - create rules for what instructions can be co-issued/groups. + * - schedule/shuffle instructions to get them ready for grouping (also need to + * implement ways to stop certain instructions being rearranged, etc. first!) + */ +PUBLIC +bool rogue_schedule_instr_groups(rogue_shader *shader, bool multi_instr_groups) +{ + if (shader->is_grouped) + return false; + + if (multi_instr_groups) { + unreachable("Multi instruction groups are unsupported."); + return false; + } + + rogue_lower_regs(shader); + + unsigned g = 0; + rogue_foreach_block (block, shader) { + struct list_head instr_groups; + list_inithead(&instr_groups); + + rogue_foreach_instr_in_block_safe (instr, block) { + enum rogue_alu group_alu = ROGUE_ALU_INVALID; + switch (instr->type) { + case ROGUE_INSTR_TYPE_ALU: + case ROGUE_INSTR_TYPE_BACKEND: + group_alu = ROGUE_ALU_MAIN; + break; + + case ROGUE_INSTR_TYPE_CTRL: + group_alu = ROGUE_ALU_CONTROL; + break; + + default: + unreachable("Invalid instruction type."); + } + + rogue_instr_group *group = rogue_instr_group_create(block, group_alu); + group->index = g++; + + rogue_move_instr_to_group(instr, group); + rogue_finalise_instr_group(group); + list_addtail(&group->link, &instr_groups); + } + + list_replace(&instr_groups, &block->instrs); + } + + shader->next_instr = g; + shader->is_grouped = true; + + rogue_finalise_shader_offsets(shader); + + return true; +} diff --git a/src/imagination/rogue/passes/rogue_schedule_uvsw.c b/src/imagination/rogue/passes/rogue_schedule_uvsw.c new file mode 100644 index 00000000000..747791be0f8 --- /dev/null +++ b/src/imagination/rogue/passes/rogue_schedule_uvsw.c @@ -0,0 +1,95 @@ +/* + * Copyright © 2022 Imagination Technologies Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "rogue.h" +#include "rogue_builder.h" +#include "util/macros.h" + +#include + +/** + * \file rogue_schedule_uvsw.c + * + * \brief Contains the rogue_schedule_uvsw pass. + */ + +/* TODO: See if maybe this can/should be done in rogue_lower_END instead? */ + +/* Schedules UVSW task control instructions. */ +PUBLIC +bool rogue_schedule_uvsw(rogue_shader *shader, bool latency_hiding) +{ + if (shader->is_grouped) + return false; + + /* TODO: Support for other shader types that write to the unified vertex + * store. */ + if (shader->stage != MESA_SHADER_VERTEX) + return false; + + /* TODO: Add support for delayed scheduling (latency hiding). */ + if (latency_hiding) + unreachable("Latency hiding is unimplemented."); + + rogue_builder b; + rogue_builder_init(&b, shader); + + /* Check whether there are uvsw.writes. */ + bool uvsw_write_present = false; + rogue_foreach_instr_in_shader (instr, shader) { + if (instr->type != ROGUE_INSTR_TYPE_BACKEND) + continue; + + rogue_backend_instr *backend = rogue_instr_as_backend(instr); + if (backend->op != ROGUE_BACKEND_OP_UVSW_WRITE) + continue; + + uvsw_write_present = true; + break; + } + + if (!uvsw_write_present) + return false; + + /* Insert emit/end task before the final instruction (nop.end). */ + rogue_block *final_block = + list_last_entry(&shader->blocks, rogue_block, link); + rogue_instr *final_instr = + list_last_entry(&final_block->instrs, rogue_instr, link); + + if (!rogue_instr_is_nop_end(final_instr)) + unreachable("UVSW emit/end task need to be the final instruction."); + + b.cursor = rogue_cursor_before_instr(final_instr); + + /* TODO: If instruction before nop.end is uvsw.write then + * UVSW_WRITETHENEMITTHENENDTASK. */ + + rogue_UVSW_EMIT(&b); + rogue_UVSW_ENDTASK(&b); + + /* TODO: replace nop.end and add end flag to final uvsw instruction instead. + */ + + return true; +} diff --git a/src/imagination/rogue/rogue_validate.h b/src/imagination/rogue/passes/rogue_schedule_wdf.c similarity index 53% rename from src/imagination/rogue/rogue_validate.h rename to src/imagination/rogue/passes/rogue_schedule_wdf.c index 36268f32210..88ca59ffbb3 100644 --- a/src/imagination/rogue/rogue_validate.h +++ b/src/imagination/rogue/passes/rogue_schedule_wdf.c @@ -21,54 +21,53 @@ * SOFTWARE. */ -#ifndef ROGUE_VALIDATE_H -#define ROGUE_VALIDATE_H - -#include -#include -#include -#include - -#include "rogue_instr.h" -#include "rogue_operand.h" -#include "rogue_shader.h" +#include "rogue.h" +#include "rogue_builder.h" #include "util/macros.h" -/** - * \brief Register rule description. - */ -struct rogue_register_rule { - enum rogue_register_access access; - size_t max; - enum rogue_register_modifier modifiers; -}; +#include /** - * \brief Instruction operand rule description. + * \file rogue_schedule_wdf.c + * + * \brief Contains the rogue_schedule_wdf pass. */ -struct rogue_instr_operand_rule { - uint64_t mask; - ssize_t min; - ssize_t max; - ssize_t align; -}; -/** - * \brief Instruction rule description. - */ -struct rogue_instr_rule { - uint64_t flags; /** A mask of #rogue_instr_flag values. */ - size_t num_operands; - struct rogue_instr_operand_rule *operand_rules; -}; +static bool +rogue_insert_wdf(rogue_builder *b, rogue_drc_trxn *drc_trxn, unsigned num) +{ + assert(drc_trxn->acquire); + if (drc_trxn->release) + return false; + + b->cursor = rogue_cursor_after_instr(drc_trxn->acquire); + drc_trxn->release = &rogue_WDF(b, rogue_ref_drc_trxn(num, drc_trxn))->instr; + + return true; +} + +/* Schedules WDF instructions for DRC transactions. */ PUBLIC -bool rogue_validate_operand(const struct rogue_operand *operand); +bool rogue_schedule_wdf(rogue_shader *shader, bool latency_hiding) +{ + if (shader->is_grouped) + return false; -PUBLIC -bool rogue_validate_instr(const struct rogue_instr *instr); + /* TODO: Add support for delayed scheduling (latency hiding). */ + if (latency_hiding) + unreachable("Latency hiding is unimplemented."); -PUBLIC -bool rogue_validate_shader(const struct rogue_shader *shader); + bool progress = false; -#endif /* ROGUE_VALIDATE_H */ + rogue_builder b; + rogue_builder_init(&b, shader); + + rogue_foreach_drc_trxn (drc_trxn, shader, 0) + progress |= rogue_insert_wdf(&b, drc_trxn, 0); + + rogue_foreach_drc_trxn (drc_trxn, shader, 1) + progress |= rogue_insert_wdf(&b, drc_trxn, 1); + + return progress; +} diff --git a/src/imagination/rogue/passes/rogue_trim.c b/src/imagination/rogue/passes/rogue_trim.c new file mode 100644 index 00000000000..ed4424432d7 --- /dev/null +++ b/src/imagination/rogue/passes/rogue_trim.c @@ -0,0 +1,106 @@ +/* + * Copyright © 2022 Imagination Technologies Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "rogue.h" +#include "util/macros.h" + +#include + +/** + * \file rogue_trim.c + * + * \brief Contains the rogue_trim pass. + */ + +static bool rogue_trim_instrs(rogue_shader *shader) +{ + bool progress = false; + + shader->next_block = 0; + shader->next_instr = 0; + + rogue_foreach_block (block, shader) { + progress |= (block->index != shader->next_block); + block->index = shader->next_block++; + rogue_foreach_instr_in_block (instr, block) { + progress |= (instr->index != shader->next_instr); + instr->index = shader->next_instr++; + } + } + + return progress; +} + +static bool rogue_trim_regs(rogue_shader *shader) +{ + bool progress = false; + + rogue_reset_reg_usage(shader, ROGUE_REG_CLASS_SSA); + rogue_reset_reg_usage(shader, ROGUE_REG_CLASS_TEMP); + + unsigned index[ROGUE_REG_CLASS_COUNT] = { 0 }; + + rogue_foreach_regarray (regarray, shader) { + enum rogue_reg_class class = regarray->regs[0]->class; + if (class != ROGUE_REG_CLASS_SSA && class != ROGUE_REG_CLASS_TEMP) + continue; + + if (regarray->parent) + continue; + + for (unsigned u = 0; u < regarray->size; ++u) + progress |= + rogue_reg_set(shader, regarray->regs[u], class, index[class]++); + } + + rogue_foreach_reg (reg, shader, ROGUE_REG_CLASS_SSA) { + if (reg->dirty) + continue; + + progress |= rogue_reg_set(shader, reg, reg->class, index[reg->class]++); + } + + rogue_foreach_reg (reg, shader, ROGUE_REG_CLASS_TEMP) { + if (reg->dirty) + continue; + + progress |= rogue_reg_set(shader, reg, reg->class, index[reg->class]++); + } + + return progress; +} + +/* Renumbers instructions, blocks, and temp/ssa registers. */ +PUBLIC +bool rogue_trim(rogue_shader *shader) +{ + if (shader->is_grouped) + return false; + + bool progress = false; + + progress |= rogue_trim_instrs(shader); + progress |= rogue_trim_regs(shader); + + return progress; +} diff --git a/src/imagination/rogue/rogue.c b/src/imagination/rogue/rogue.c index 5481bc905f1..8bd10225534 100644 --- a/src/imagination/rogue/rogue.c +++ b/src/imagination/rogue/rogue.c @@ -21,744 +21,973 @@ * SOFTWARE. */ -#include -#include -#include -#include - -#include "compiler/shader_enums.h" -#include "compiler/spirv/nir_spirv.h" -#include "nir/nir.h" +#include "compiler/glsl_types.h" #include "rogue.h" -#include "rogue_build_data.h" -#include "rogue_compiler.h" -#include "rogue_constreg.h" -#include "rogue_encode.h" -#include "rogue_nir.h" -#include "rogue_nir_helpers.h" -#include "rogue_operand.h" -#include "rogue_regalloc.h" -#include "rogue_shader.h" -#include "rogue_validate.h" +#include "util/list.h" #include "util/macros.h" -#include "util/memstream.h" #include "util/ralloc.h" +#include "util/sparse_array.h" + +#include /** * \file rogue.c * - * \brief Contains the top-level Rogue compiler interface for Vulkan driver and - * the offline compiler. + * \brief Contains general Rogue IR functions. */ +/* TODO: Tweak these? */ +#define ROGUE_REG_CACHE_NODE_SIZE 512 +#define ROGUE_REGARRAY_CACHE_NODE_SIZE 512 + /** - * \brief Converts a SPIR-V shader to NIR. + * \brief Sets an existing register to a (new) class and/or index. * - * \param[in] ctx Shared multi-stage build context. - * \param[in] stage Shader stage. - * \param[in] spirv_size SPIR-V data length in DWORDs. - * \param[in] spirv_data SPIR-V data. - * \param[in] num_spec Number of SPIR-V specializations. - * \param[in] spec SPIR-V specializations. - * \return A nir_shader* if successful, or NULL if unsuccessful. + * \param[in] shader The shader containing the register. + * \param[in] reg The register being changed. + * \param[in] class The new register class. + * \param[in] index The new register index. + * \return True if the register was updated, else false. */ -nir_shader *rogue_spirv_to_nir(struct rogue_build_ctx *ctx, - gl_shader_stage stage, - const char *entry, - size_t spirv_size, - const uint32_t *spirv_data, - unsigned num_spec, - struct nir_spirv_specialization *spec) +PUBLIC +bool rogue_reg_set(rogue_shader *shader, + rogue_reg *reg, + enum rogue_reg_class class, + unsigned index) { - nir_shader *nir; + bool changed = true; - nir = spirv_to_nir(spirv_data, - spirv_size, - spec, - num_spec, - stage, - entry, - rogue_get_spirv_options(ctx->compiler), - rogue_get_compiler_options(ctx->compiler)); - if (!nir) - return NULL; + if (reg->class == class && reg->index == index) + changed = false; - ralloc_steal(ctx, nir); + const rogue_reg_info *info = &rogue_reg_infos[class]; - /* Apply passes. */ - if (!rogue_nir_passes(ctx, nir, stage)) { - ralloc_free(nir); - return NULL; + if (info->num) { + assert(index < info->num); + BITSET_SET(shader->regs_used[class], index); } - /* Collect I/O data to pass back to the driver. */ - if (!rogue_collect_io_data(ctx, nir)) { - ralloc_free(nir); - return NULL; + if (reg->class != class) { + list_del(®->link); + list_addtail(®->link, &shader->regs[class]); } - return nir; + reg->class = class; + reg->index = index; + reg->dirty = true; + + /* Clear the old cache entry. */ + if (reg->cached && *reg->cached == reg) + *reg->cached = NULL; + + /* Set new cache entry. */ + rogue_reg **reg_cached = + util_sparse_array_get(&shader->reg_cache[class], index); + *reg_cached = reg; + reg->cached = reg_cached; + + /* If this is the first member of a top-level regarray, update its cache + * entry. */ + rogue_regarray *regarray = reg->regarray; + if (regarray && !regarray->parent && regarray->regs[0] == reg) { + if (regarray->cached && *regarray->cached == regarray) + *regarray->cached = NULL; + + uint64_t key = + rogue_regarray_cache_key(regarray->size, class, index, false, 0); + + rogue_regarray **regarray_cached = + util_sparse_array_get(&shader->regarray_cache, key); + *regarray_cached = regarray; + regarray->cached = regarray_cached; + } + + return changed; } /** - * \brief Converts a Rogue shader to binary. + * \brief Sets an existing register to a (new) class and/or index, and updates + * its usage bitset. * - * \param[in] ctx Shared multi-stage build context. - * \param[in] shader Rogue shader. - * \return A rogue_shader_binary* if successful, or NULL if unsuccessful. + * \param[in] shader The shader containing the register. + * \param[in] reg The register being changed. + * \param[in] class The new register class. + * \param[in] index The new register index. + * \return True if the register was updated, else false. */ -struct rogue_shader_binary *rogue_to_binary(struct rogue_build_ctx *ctx, - const struct rogue_shader *shader) +PUBLIC +bool rogue_reg_rewrite(rogue_shader *shader, + rogue_reg *reg, + enum rogue_reg_class class, + unsigned index) { - struct rogue_shader_binary *binary; - struct u_memstream mem; - size_t buf_size; - char *buf; - - if (!rogue_validate_shader(shader)) - return NULL; - - if (!u_memstream_open(&mem, &buf, &buf_size)) - return NULL; - - if (!rogue_encode_shader(shader, u_memstream_get(&mem))) { - u_memstream_close(&mem); - free(buf); - return NULL; + const rogue_reg_info *info = &rogue_reg_infos[reg->class]; + if (info->num) { + assert(BITSET_TEST(reg->shader->regs_used[reg->class], reg->index) && + "Register not in use!"); + BITSET_CLEAR(reg->shader->regs_used[reg->class], reg->index); } - u_memstream_close(&mem); - - binary = rzalloc_size(ctx, sizeof(*binary) + buf_size); - if (!binary) { - free(buf); - return NULL; - } - - binary->size = buf_size; - memcpy(binary->data, buf, buf_size); - - free(buf); - - return binary; + return rogue_reg_set(shader, reg, class, index); } -static bool -setup_alu_dest(struct rogue_instr *instr, size_t dest_index, nir_alu_instr *alu) +static void rogue_shader_destructor(void *ptr) { - assert(dest_index == 0); + rogue_shader *shader = ptr; + for (unsigned u = 0; u < ARRAY_SIZE(shader->reg_cache); ++u) + util_sparse_array_finish(&shader->reg_cache[u]); - /* Dest validation. */ - assert(nir_dest_num_components(alu->dest.dest) == 1 || - nir_dest_num_components(alu->dest.dest) == 4); - assert(nir_dest_bit_size(alu->dest.dest) == 32); - - size_t nir_dest_reg = nir_alu_dest_regindex(alu); - - if (nir_dest_num_components(alu->dest.dest) == 1) { - CHECK(rogue_instr_set_operand_vreg(instr, dest_index, nir_dest_reg)); - } else { - size_t comp = nir_alu_dest_comp(alu); - CHECK(rogue_instr_set_operand_vreg_vec(instr, - dest_index, - comp, - nir_dest_reg)); - } - - return true; -} - -static bool trans_constreg_operand(struct rogue_instr *instr, - size_t operand_index, - uint32_t const_value) -{ - size_t const_reg = rogue_constreg_lookup(const_value); - - /* Only values that can be sourced from const regs should be left from the - * rogue_nir_constreg pass. - */ - assert(const_reg != ROGUE_NO_CONST_REG); - - CHECK(rogue_instr_set_operand_reg(instr, - operand_index, - ROGUE_OPERAND_TYPE_REG_CONST, - const_reg)); - - return true; -} - -static bool trans_nir_alu_fmax(struct rogue_shader *shader, nir_alu_instr *alu) -{ - /* Src validation. */ - assert(nir_src_num_components(alu->src[0].src) == 1); - assert(nir_src_bit_size(alu->src[0].src) == 32); - - assert(nir_src_num_components(alu->src[1].src) == 1); - assert(nir_src_bit_size(alu->src[1].src) == 32); - - struct rogue_instr *instr = rogue_shader_insert(shader, ROGUE_OP_MAX); - - CHECK(setup_alu_dest(instr, 0, alu)); - - for (size_t u = 0; u < nir_op_infos[nir_op_fmax].num_inputs; ++u) { - /* Handle values that can be pulled from const regs. */ - if (nir_alu_src_is_const(alu, u)) { - CHECK(trans_constreg_operand(instr, u + 1, nir_alu_src_const(alu, u))); - continue; - } - - size_t nir_src_reg = nir_alu_src_regindex(alu, u); - - CHECK(rogue_instr_set_operand_vreg(instr, u + 1, nir_src_reg)); - } - - return true; -} - -static bool trans_nir_alu_fmin(struct rogue_shader *shader, nir_alu_instr *alu) -{ - /* Src validation. */ - assert(nir_src_num_components(alu->src[0].src) == 1); - assert(nir_src_bit_size(alu->src[0].src) == 32); - - assert(nir_src_num_components(alu->src[1].src) == 1); - assert(nir_src_bit_size(alu->src[1].src) == 32); - - struct rogue_instr *instr = rogue_shader_insert(shader, ROGUE_OP_MIN); - - CHECK(setup_alu_dest(instr, 0, alu)); - - for (size_t u = 0; u < nir_op_infos[nir_op_fmin].num_inputs; ++u) { - /* Handle values that can be pulled from const regs. */ - if (nir_alu_src_is_const(alu, u)) { - CHECK(trans_constreg_operand(instr, u + 1, nir_alu_src_const(alu, u))); - continue; - } - - size_t nir_src_reg = nir_alu_src_regindex(alu, u); - - CHECK(rogue_instr_set_operand_vreg(instr, u + 1, nir_src_reg)); - } - - return true; -} - -static bool trans_nir_alu_mov_imm(struct rogue_shader *shader, - nir_alu_instr *alu) -{ - /* Src validation. */ - assert(nir_src_num_components(alu->src[0].src) == 1); - assert(nir_src_bit_size(alu->src[0].src) == 32); - - uint32_t value = nir_alu_src_const(alu, 0); - - struct rogue_instr *instr = rogue_shader_insert(shader, ROGUE_OP_MOV_IMM); - - CHECK(setup_alu_dest(instr, 0, alu)); - CHECK(rogue_instr_set_operand_imm(instr, 1, value)); - - return true; -} - -static bool trans_nir_alu_mov(struct rogue_shader *shader, nir_alu_instr *alu) -{ - /* Constant value that isn't in constregs. */ - if (nir_alu_src_is_const(alu, 0) && - nir_dest_num_components(alu->dest.dest) == 1) - return trans_nir_alu_mov_imm(shader, alu); - - /* Src validation. */ - assert(nir_src_num_components(alu->src[0].src) == 1); - assert(nir_src_bit_size(alu->src[0].src) == 32); - - struct rogue_instr *instr = rogue_shader_insert(shader, ROGUE_OP_MOV); - - CHECK(setup_alu_dest(instr, 0, alu)); - - /* Handle values that can be pulled from const regs. */ - if (nir_alu_src_is_const(alu, 0)) { - return trans_constreg_operand(instr, 1, nir_alu_src_const(alu, 0)); - } - - size_t nir_src_reg = nir_alu_src_regindex(alu, 0); - CHECK(rogue_instr_set_operand_vreg(instr, 1, nir_src_reg)); - - return true; -} - -static bool trans_nir_alu_pack_unorm_4x8(struct rogue_shader *shader, - nir_alu_instr *alu) -{ - /* Src/dest validation. */ - assert(nir_dest_num_components(alu->dest.dest) == 1); - assert(nir_dest_bit_size(alu->dest.dest) == 32); - - assert(nir_src_num_components(alu->src[0].src) == 4); - assert(nir_src_bit_size(alu->src[0].src) == 32); - - size_t nir_src_reg = nir_alu_src_regindex(alu, 0); - size_t nir_dest_reg = nir_alu_dest_regindex(alu); - - struct rogue_instr *instr = rogue_shader_insert(shader, ROGUE_OP_PACK_U8888); - - CHECK(rogue_instr_set_operand_vreg(instr, 0, nir_dest_reg)); - - /* Ensure all 4 components are being sourced in order. */ - for (size_t u = 0; u < nir_src_num_components(alu->src[0].src); ++u) - assert(alu->src->swizzle[u] == u); - - CHECK(rogue_instr_set_operand_vreg_vec(instr, - 1, - ROGUE_COMPONENT_ALL, - nir_src_reg)); - - return true; -} - -static bool trans_nir_alu_fmul(struct rogue_shader *shader, nir_alu_instr *alu) -{ - /* Src validation. */ - assert(nir_src_num_components(alu->src[0].src) == 1); - assert(nir_src_bit_size(alu->src[0].src) == 32); - - assert(nir_src_num_components(alu->src[1].src) == 1); - assert(nir_src_bit_size(alu->src[1].src) == 32); - - size_t nir_in_reg_a = nir_alu_src_regindex(alu, 0); - size_t nir_in_reg_b = nir_alu_src_regindex(alu, 1); - - struct rogue_instr *instr = rogue_shader_insert(shader, ROGUE_OP_MUL); - - CHECK(setup_alu_dest(instr, 0, alu)); - CHECK(rogue_instr_set_operand_vreg(instr, 1, nir_in_reg_a)); - CHECK(rogue_instr_set_operand_vreg(instr, 2, nir_in_reg_b)); - - return true; -} - -static bool trans_nir_alu_ffma(struct rogue_shader *shader, nir_alu_instr *alu) -{ - /* Src validation. */ - assert(nir_src_num_components(alu->src[0].src) == 1); - assert(nir_src_bit_size(alu->src[0].src) == 32); - - assert(nir_src_num_components(alu->src[1].src) == 1); - assert(nir_src_bit_size(alu->src[1].src) == 32); - - assert(nir_src_num_components(alu->src[2].src) == 1); - assert(nir_src_bit_size(alu->src[2].src) == 32); - - size_t nir_in_reg_a = nir_alu_src_regindex(alu, 0); - size_t nir_in_reg_b = nir_alu_src_regindex(alu, 1); - size_t nir_in_reg_c = nir_alu_src_regindex(alu, 2); - - struct rogue_instr *instr = rogue_shader_insert(shader, ROGUE_OP_FMA); - - CHECK(setup_alu_dest(instr, 0, alu)); - CHECK(rogue_instr_set_operand_vreg(instr, 1, nir_in_reg_a)); - CHECK(rogue_instr_set_operand_vreg(instr, 2, nir_in_reg_b)); - CHECK(rogue_instr_set_operand_vreg(instr, 3, nir_in_reg_c)); - - return true; -} - -static bool trans_nir_alu(struct rogue_shader *shader, nir_alu_instr *alu) -{ - switch (alu->op) { - case nir_op_fmax: - return trans_nir_alu_fmax(shader, alu); - - case nir_op_fmin: - return trans_nir_alu_fmin(shader, alu); - - case nir_op_pack_unorm_4x8: - return trans_nir_alu_pack_unorm_4x8(shader, alu); - - case nir_op_mov: - return trans_nir_alu_mov(shader, alu); - - case nir_op_fmul: - return trans_nir_alu_fmul(shader, alu); - - case nir_op_ffma: - return trans_nir_alu_ffma(shader, alu); - - default: - break; - } - - unreachable("Unimplemented NIR ALU instruction."); -} - -static bool trans_nir_intrinsic_load_input_fs(struct rogue_shader *shader, - nir_intrinsic_instr *intr) -{ - struct rogue_fs_build_data *fs_data = &shader->ctx->stage_data.fs; - - /* Src/dest validation. */ - assert(nir_dest_num_components(intr->dest) == 1); - assert(nir_dest_bit_size(intr->dest) == 32); - - assert(nir_src_num_components(intr->src[0]) == 1); - assert(nir_src_bit_size(intr->src[0]) == 32); - assert(nir_intr_src_is_const(intr, 0)); - - /* Intrinsic index validation. */ - assert(nir_intrinsic_dest_type(intr) == nir_type_float32); - - struct nir_io_semantics io_semantics = nir_intrinsic_io_semantics(intr); - size_t component = nir_intrinsic_component(intr); - size_t coeff_index = rogue_coeff_index_fs(&fs_data->iterator_args, - io_semantics.location, - component); - size_t wcoeff_index = rogue_coeff_index_fs(&fs_data->iterator_args, ~0, 0); - size_t drc_num = rogue_acquire_drc(shader); - uint64_t source_count = nir_dest_num_components(intr->dest); - - size_t nir_dest_reg = nir_intr_dest_regindex(intr); - - /* pixiter.w instruction. */ - struct rogue_instr *instr = rogue_shader_insert(shader, ROGUE_OP_PIX_ITER_W); - - CHECK(rogue_instr_set_operand_vreg(instr, 0, nir_dest_reg)); - CHECK(rogue_instr_set_operand_drc(instr, 1, drc_num)); - CHECK(rogue_instr_set_operand_reg(instr, - 2, - ROGUE_OPERAND_TYPE_REG_COEFF, - coeff_index)); - CHECK(rogue_instr_set_operand_reg(instr, - 3, - ROGUE_OPERAND_TYPE_REG_COEFF, - wcoeff_index)); - CHECK(rogue_instr_set_operand_imm(instr, 4, source_count)); - - /* wdf instruction must follow the pixiter.w. */ - instr = rogue_shader_insert(shader, ROGUE_OP_WDF); - - CHECK(rogue_instr_set_operand_drc(instr, 0, drc_num)); - rogue_release_drc(shader, drc_num); - - return true; -} - -static bool trans_nir_intrinsic_load_input_vs(struct rogue_shader *shader, - nir_intrinsic_instr *intr) -{ - /* Src/dest validation. */ - assert(nir_dest_num_components(intr->dest) == 1); - assert(nir_dest_bit_size(intr->dest) == 32); - - assert(nir_src_num_components(intr->src[0]) == 1); - assert(nir_src_bit_size(intr->src[0]) == 32); - assert(nir_intr_src_is_const(intr, 0)); - - /* Intrinsic index validation. */ - assert(nir_intrinsic_dest_type(intr) == nir_type_float32); - - size_t component = nir_intrinsic_component(intr); - struct nir_io_semantics io_semantics = nir_intrinsic_io_semantics(intr); - size_t vi_reg_index = ((io_semantics.location - VERT_ATTRIB_GENERIC0) * 3) + - component; /* TODO: get these properly with the - * intrinsic index (ssa argument) - */ - - size_t nir_dest_reg = nir_intr_dest_regindex(intr); - - struct rogue_instr *instr = rogue_shader_insert(shader, ROGUE_OP_MOV); - - CHECK(rogue_instr_set_operand_vreg(instr, 0, nir_dest_reg)); - CHECK(rogue_instr_set_operand_reg(instr, - 1, - ROGUE_OPERAND_TYPE_REG_VERTEX_IN, - vi_reg_index)); - - return true; -} - -static bool trans_nir_intrinsic_load_input(struct rogue_shader *shader, - nir_intrinsic_instr *intr) -{ - switch (shader->stage) { - case MESA_SHADER_FRAGMENT: - return trans_nir_intrinsic_load_input_fs(shader, intr); - - case MESA_SHADER_VERTEX: - return trans_nir_intrinsic_load_input_vs(shader, intr); - - default: - break; - } - - unreachable("Unimplemented NIR load_input variant."); -} - -static bool trans_nir_intrinsic_store_output_fs(struct rogue_shader *shader, - nir_intrinsic_instr *intr) -{ - /* Src/dest validation. */ - assert(nir_src_num_components(intr->src[0]) == 1); - assert(nir_src_bit_size(intr->src[0]) == 32); - assert(!nir_intr_src_is_const(intr, 0)); - - assert(nir_src_num_components(intr->src[1]) == 1); - assert(nir_src_bit_size(intr->src[1]) == 32); - assert(nir_intr_src_is_const(intr, 1)); - - /* Intrinsic index validation. */ - assert(nir_intrinsic_src_type(intr) == nir_type_uint32); - - /* Fetch the output offset. */ - /* TODO: Is this really the right value to use for pixel out reg. num? */ - size_t offset = nir_intr_src_const(intr, 1); - - /* Fetch the components. */ - size_t src_reg = nir_intr_src_regindex(intr, 0); - - /* mov.olchk instruction. */ - struct rogue_instr *instr = rogue_shader_insert(shader, ROGUE_OP_MOV); - - CHECK(rogue_instr_set_operand_reg(instr, - 0, - ROGUE_OPERAND_TYPE_REG_PIXEL_OUT, - offset)); - CHECK(rogue_instr_set_operand_vreg(instr, 1, src_reg)); - CHECK(rogue_instr_set_flag(instr, ROGUE_INSTR_FLAG_OLCHK)); - - return true; -} - -static bool trans_nir_intrinsic_store_output_vs(struct rogue_shader *shader, - nir_intrinsic_instr *intr) -{ - struct rogue_vs_build_data *vs_data = &shader->ctx->stage_data.vs; - - /* Src/dest validation. */ - assert(nir_src_num_components(intr->src[0]) == 1); - assert(nir_src_bit_size(intr->src[0]) == 32); - assert(!nir_intr_src_is_const(intr, 0)); - - assert(nir_src_num_components(intr->src[1]) == 1); - assert(nir_src_bit_size(intr->src[1]) == 32); - assert(nir_intr_src_is_const(intr, 1)); - - /* Intrinsic index validation. */ - assert(nir_intrinsic_src_type(intr) == nir_type_float32); - assert(util_bitcount(nir_intrinsic_write_mask(intr)) == 1); - - struct nir_io_semantics io_semantics = nir_intrinsic_io_semantics(intr); - size_t component = nir_intrinsic_component(intr); - size_t vo_index = rogue_output_index_vs(&vs_data->outputs, - io_semantics.location, - component); - - size_t src_reg = nir_intr_src_regindex(intr, 0); - - struct rogue_instr *instr = rogue_shader_insert(shader, ROGUE_OP_VTXOUT); - - CHECK(rogue_instr_set_operand_imm(instr, 0, vo_index)); - CHECK(rogue_instr_set_operand_vreg(instr, 1, src_reg)); - - return true; -} - -static bool trans_nir_intrinsic_store_output(struct rogue_shader *shader, - nir_intrinsic_instr *intr) -{ - switch (shader->stage) { - case MESA_SHADER_FRAGMENT: - return trans_nir_intrinsic_store_output_fs(shader, intr); - - case MESA_SHADER_VERTEX: - return trans_nir_intrinsic_store_output_vs(shader, intr); - - default: - break; - } - - unreachable("Unimplemented NIR store_output variant."); -} - -static bool trans_nir_intrinsic_load_ubo(struct rogue_shader *shader, - nir_intrinsic_instr *intr) -{ - struct rogue_ubo_data *ubo_data = - &shader->ctx->common_data[shader->stage].ubo_data; - - /* Src/dest validation. */ - assert(nir_dest_num_components(intr->dest) == 1); - assert(nir_dest_bit_size(intr->dest) == 32); - - assert(nir_src_num_components(intr->src[0]) == 2); - assert(nir_src_bit_size(intr->src[0]) == 32); - assert(nir_intr_src_is_const(intr, 0)); - - assert(nir_src_num_components(intr->src[1]) == 1); - assert(nir_src_bit_size(intr->src[1]) == 32); - assert(nir_intr_src_is_const(intr, 1)); - - /* Intrinsic index validation. */ - assert((nir_intrinsic_range_base(intr) % ROGUE_REG_SIZE_BYTES) == 0); - assert(nir_intrinsic_range(intr) == ROGUE_REG_SIZE_BYTES); - - size_t nir_dest_reg = nir_intr_dest_regindex(intr); - - size_t desc_set = nir_intr_src_comp_const(intr, 0, 0); - size_t binding = nir_intr_src_comp_const(intr, 0, 1); - size_t offset = nir_intrinsic_range_base(intr); - - size_t sh_num = rogue_ubo_reg(ubo_data, desc_set, binding, offset); - - struct rogue_instr *instr = rogue_shader_insert(shader, ROGUE_OP_MOV); - - CHECK(rogue_instr_set_operand_vreg(instr, 0, nir_dest_reg)); - CHECK(rogue_instr_set_operand_reg(instr, - 1, - ROGUE_OPERAND_TYPE_REG_SHARED, - sh_num)); - return true; -} - -static bool trans_nir_intrinsic(struct rogue_shader *shader, - nir_intrinsic_instr *intr) -{ - switch (intr->intrinsic) { - case nir_intrinsic_load_input: - return trans_nir_intrinsic_load_input(shader, intr); - - case nir_intrinsic_store_output: - return trans_nir_intrinsic_store_output(shader, intr); - - case nir_intrinsic_load_ubo: - return trans_nir_intrinsic_load_ubo(shader, intr); - - default: - break; - } - - unreachable("Unimplemented NIR intrinsic instruction."); -} - -static bool trans_nir_load_const(struct rogue_shader *shader, - nir_load_const_instr *load_const) -{ - /* Src/dest validation. */ - assert(load_const->def.bit_size == 32); - - /* Ensure that two-component load_consts are used only by load_ubos. */ - if (load_const->def.num_components == 2) { - nir_foreach_use (use_src, &load_const->def) { - nir_instr *instr = use_src->parent_instr; - assert(instr->type == nir_instr_type_intrinsic); - - ASSERTED nir_intrinsic_instr *intr = nir_instr_as_intrinsic(instr); - assert(intr->intrinsic == nir_intrinsic_load_ubo); - } - } else { - assert(load_const->def.num_components == 1); - } - - /* TODO: This is currently done in MOV_IMM, but instead now would be the - * time to lookup the constant value, see if it lives in const regs, or if - * it needs to generate a MOV_IMM (or be constant calc-ed). - */ - return true; -} - -static bool trans_nir_jump_return(struct rogue_shader *shader, - nir_jump_instr *jump) -{ - enum rogue_opcode return_op; - - switch (shader->stage) { - case MESA_SHADER_FRAGMENT: - return_op = ROGUE_OP_END_FRAG; - break; - - case MESA_SHADER_VERTEX: - return_op = ROGUE_OP_END_VERT; - break; - - default: - unreachable("Unimplemented NIR return instruction type."); - } - - rogue_shader_insert(shader, return_op); - - return true; -} - -static bool trans_nir_jump(struct rogue_shader *shader, nir_jump_instr *jump) -{ - switch (jump->type) { - case nir_jump_return: - return trans_nir_jump_return(shader, jump); - - default: - break; - } - - unreachable("Unimplemented NIR jump instruction type."); + util_sparse_array_finish(&shader->regarray_cache); } /** - * \brief Converts a NIR shader to Rogue. + * \brief Allocates and initializes a new rogue_shader object. * - * \param[in] ctx Shared multi-stage build context. - * \param[in] nir NIR shader. - * \return A rogue_shader* if successful, or NULL if unsuccessful. + * \param[in] mem_ctx The new shader's memory context. + * \param[in] stage The new shader's stage. + * \return The new shader. */ -struct rogue_shader *rogue_nir_to_rogue(struct rogue_build_ctx *ctx, - const nir_shader *nir) +PUBLIC +rogue_shader *rogue_shader_create(void *mem_ctx, gl_shader_stage stage) { - gl_shader_stage stage = nir->info.stage; - struct rogue_shader *shader = rogue_shader_create(ctx, stage); - if (!shader) - return NULL; + rogue_debug_init(); - /* Make sure we only have a single function. */ - assert(exec_list_length(&nir->functions) == 1); + rogue_shader *shader = rzalloc_size(mem_ctx, sizeof(*shader)); - /* Translate shader entrypoint. */ - nir_function_impl *entry = nir_shader_get_entrypoint((nir_shader *)nir); - nir_foreach_block (block, entry) { - nir_foreach_instr (instr, block) { - switch (instr->type) { - case nir_instr_type_alu: - /* TODO: Cleanup on failure. */ - CHECKF(trans_nir_alu(shader, nir_instr_as_alu(instr)), - "Failed to translate NIR ALU instruction."); + shader->stage = stage; + + list_inithead(&shader->blocks); + + for (enum rogue_reg_class class = 0; class < ROGUE_REG_CLASS_COUNT; + ++class) { + list_inithead(&shader->regs[class]); + + const rogue_reg_info *info = &rogue_reg_infos[class]; + if (info->num) { + unsigned bitset_size = + sizeof(*shader->regs_used[class]) * BITSET_WORDS(info->num); + shader->regs_used[class] = rzalloc_size(shader, bitset_size); + } + } + + for (unsigned u = 0; u < ARRAY_SIZE(shader->reg_cache); ++u) + util_sparse_array_init(&shader->reg_cache[u], + sizeof(rogue_reg *), + ROGUE_REG_CACHE_NODE_SIZE); + + list_inithead(&shader->regarrays); + + util_sparse_array_init(&shader->regarray_cache, + sizeof(rogue_regarray *), + ROGUE_REGARRAY_CACHE_NODE_SIZE); + + for (unsigned u = 0; u < ARRAY_SIZE(shader->drc_trxns); ++u) + list_inithead(&shader->drc_trxns[u]); + + list_inithead(&shader->imm_uses); + + ralloc_set_destructor(shader, rogue_shader_destructor); + + return shader; +} + +/** + * \brief Allocates and initializes a new rogue_reg object. + * + * \param[in] shader The shader which will contain the register. + * \param[in] class The register class. + * \param[in] index The register index. + * \param[in] reg_cached The shader register cache. + * \return The new register. + */ +static rogue_reg *rogue_reg_create(rogue_shader *shader, + enum rogue_reg_class class, + uint32_t index, + rogue_reg **reg_cached) +{ + rogue_reg *reg = rzalloc_size(shader, sizeof(*reg)); + + reg->shader = shader; + reg->class = class; + reg->index = index; + reg->cached = reg_cached; + + list_addtail(®->link, &shader->regs[class]); + list_inithead(®->writes); + list_inithead(®->uses); + + const rogue_reg_info *info = &rogue_reg_infos[class]; + if (info->num) { + assert(index < info->num); + assert(!BITSET_TEST(shader->regs_used[class], index) && + "Register already in use!"); + BITSET_SET(shader->regs_used[class], index); + } + + return reg; +} + +/** + * \brief Deletes and frees a Rogue register. + * + * \param[in] reg The register to delete. + */ +PUBLIC +void rogue_reg_delete(rogue_reg *reg) +{ + assert(rogue_reg_is_unused(reg)); + const rogue_reg_info *info = &rogue_reg_infos[reg->class]; + if (info->num) { + assert(BITSET_TEST(reg->shader->regs_used[reg->class], reg->index) && + "Register not in use!"); + BITSET_CLEAR(reg->shader->regs_used[reg->class], reg->index); + } + + if (reg->cached && *reg->cached == reg) + *reg->cached = NULL; + + list_del(®->link); + ralloc_free(reg); +} + +static inline rogue_reg *rogue_reg_cached_common(rogue_shader *shader, + enum rogue_reg_class class, + uint32_t index, + uint8_t component, + bool vec) +{ + uint32_t key = rogue_reg_cache_key(index, vec, component); + + rogue_reg **reg_cached = + util_sparse_array_get(&shader->reg_cache[class], key); + if (!*reg_cached) + *reg_cached = rogue_reg_create(shader, class, key, reg_cached); + + return *reg_cached; +} + +static inline rogue_reg *rogue_reg_cached(rogue_shader *shader, + enum rogue_reg_class class, + uint32_t index) +{ + return rogue_reg_cached_common(shader, class, index, 0, false); +} + +static inline rogue_reg *rogue_vec_reg_cached(rogue_shader *shader, + enum rogue_reg_class class, + unsigned index, + unsigned component) +{ + return rogue_reg_cached_common(shader, class, index, component, true); +} + +/* TODO: Static inline in rogue.h? */ +PUBLIC +rogue_reg *rogue_ssa_reg(rogue_shader *shader, unsigned index) +{ + return rogue_reg_cached(shader, ROGUE_REG_CLASS_SSA, index); +} + +PUBLIC +rogue_reg *rogue_temp_reg(rogue_shader *shader, unsigned index) +{ + return rogue_reg_cached(shader, ROGUE_REG_CLASS_TEMP, index); +} + +PUBLIC +rogue_reg *rogue_coeff_reg(rogue_shader *shader, unsigned index) +{ + return rogue_reg_cached(shader, ROGUE_REG_CLASS_COEFF, index); +} + +PUBLIC +rogue_reg *rogue_shared_reg(rogue_shader *shader, unsigned index) +{ + return rogue_reg_cached(shader, ROGUE_REG_CLASS_SHARED, index); +} + +PUBLIC +rogue_reg *rogue_const_reg(rogue_shader *shader, unsigned index) +{ + return rogue_reg_cached(shader, ROGUE_REG_CLASS_CONST, index); +} + +PUBLIC +rogue_reg *rogue_pixout_reg(rogue_shader *shader, unsigned index) +{ + return rogue_reg_cached(shader, ROGUE_REG_CLASS_PIXOUT, index); +} + +PUBLIC +rogue_reg *rogue_vtxin_reg(rogue_shader *shader, unsigned index) +{ + return rogue_reg_cached(shader, ROGUE_REG_CLASS_VTXIN, index); +} + +PUBLIC +rogue_reg *rogue_vtxout_reg(rogue_shader *shader, unsigned index) +{ + return rogue_reg_cached(shader, ROGUE_REG_CLASS_VTXOUT, index); +} + +PUBLIC +rogue_reg * +rogue_ssa_vec_reg(rogue_shader *shader, unsigned index, unsigned component) +{ + return rogue_vec_reg_cached(shader, ROGUE_REG_CLASS_SSA, index, component); +} + +static rogue_regarray *rogue_find_common_regarray(rogue_regarray *regarray, + bool *is_parent, + rogue_reg ***parent_regptr) +{ + rogue_regarray *common_regarray = NULL; + + for (unsigned u = 0; u < regarray->size; ++u) { + if (regarray->regs[u]->regarray) { + if (common_regarray && regarray->regs[u]->regarray != common_regarray) + unreachable("Can't have overlapping regarrays."); + else if (!common_regarray) + common_regarray = regarray->regs[u]->regarray; + } + } + + if (common_regarray) { + unsigned min_index = regarray->regs[0]->index; + unsigned max_index = min_index + regarray->size - 1; + + unsigned min_common_index = common_regarray->regs[0]->index; + unsigned max_common_index = min_common_index + common_regarray->size - 1; + + /* TODO: Create a new parent array that encapsulates both ranges? */ + /* Ensure that the new regarray doesn't occupy only part of its parent, + * and also registers *beyond* its parent. */ + if ((min_index > min_common_index && max_index > max_common_index) || + (min_index < min_common_index && max_index < max_common_index)) + unreachable("Can't have overflowing partial regarrays."); + + *is_parent = regarray->size > common_regarray->size; + const rogue_regarray *parent_regarray = *is_parent ? regarray + : common_regarray; + const rogue_regarray *child_regarray = *is_parent ? common_regarray + : regarray; + + for (unsigned u = 0; u < parent_regarray->size; ++u) { + if (child_regarray->regs[0]->index == + parent_regarray->regs[u]->index) { + *parent_regptr = &parent_regarray->regs[u]; break; - - case nir_instr_type_intrinsic: - CHECKF(trans_nir_intrinsic(shader, nir_instr_as_intrinsic(instr)), - "Failed to translate NIR intrinsic instruction."); - break; - - case nir_instr_type_load_const: - CHECKF(trans_nir_load_const(shader, nir_instr_as_load_const(instr)), - "Failed to translate NIR load_const instruction."); - break; - - case nir_instr_type_jump: - CHECKF(trans_nir_jump(shader, nir_instr_as_jump(instr)), - "Failed to translate NIR jump instruction."); - break; - - default: - unreachable("Unimplemented NIR instruction type."); } } } - /* Perform register allocation. */ - /* TODO: handle failure. */ - if (!rogue_ra_alloc(&shader->instr_list, - shader->ra, - &ctx->common_data[stage].temps, - &ctx->common_data[stage].internals)) + return common_regarray; +} + +static rogue_regarray *rogue_regarray_create(rogue_shader *shader, + unsigned size, + enum rogue_reg_class class, + unsigned start_index, + uint8_t component, + bool vec, + rogue_regarray **regarray_cached) +{ + rogue_regarray *regarray = rzalloc_size(shader, sizeof(*regarray)); + regarray->regs = rzalloc_size(regarray, sizeof(*regarray->regs) * size); + regarray->size = size; + regarray->cached = regarray_cached; + + for (unsigned u = 0; u < size; ++u) { + regarray->regs[u] = + vec ? rogue_vec_reg_cached(shader, class, start_index, component + u) + : rogue_reg_cached(shader, class, start_index + u); + } + + bool is_parent = false; + rogue_reg **parent_regptr = NULL; + rogue_regarray *common_regarray = + rogue_find_common_regarray(regarray, &is_parent, &parent_regptr); + + if (!common_regarray) { + /* We don't share any registers with another regarray. */ + for (unsigned u = 0; u < size; ++u) + regarray->regs[u]->regarray = regarray; + } else { + if (is_parent) { + /* We share registers with another regarray, and it is a subset of us. + */ + for (unsigned u = 0; u < common_regarray->size; ++u) + common_regarray->regs[u]->regarray = regarray; + + common_regarray->parent = regarray; + ralloc_free(common_regarray->regs); + common_regarray->regs = parent_regptr; + } else { + /* We share registers with another regarray, and we are a subset of it. + */ + regarray->parent = common_regarray; + ralloc_free(regarray->regs); + regarray->regs = parent_regptr; + } + } + + list_addtail(®array->link, &shader->regarrays); + + return regarray; +} + +static inline rogue_regarray * +rogue_regarray_cached_common(rogue_shader *shader, + unsigned size, + enum rogue_reg_class class, + uint32_t start_index, + uint8_t component, + bool vec) +{ + uint64_t key = + rogue_regarray_cache_key(size, class, start_index, vec, component); + + rogue_regarray **regarray_cached = + util_sparse_array_get(&shader->regarray_cache, key); + if (!*regarray_cached) + *regarray_cached = rogue_regarray_create(shader, + size, + class, + start_index, + component, + vec, + regarray_cached); + + return *regarray_cached; +} + +static inline rogue_regarray *rogue_regarray_cached(rogue_shader *shader, + unsigned size, + enum rogue_reg_class class, + uint32_t start_index) +{ + return rogue_regarray_cached_common(shader, + size, + class, + start_index, + 0, + false); +} + +static inline rogue_regarray * +rogue_vec_regarray_cached(rogue_shader *shader, + unsigned size, + enum rogue_reg_class class, + uint32_t start_index, + uint8_t component) +{ + return rogue_regarray_cached_common(shader, + size, + class, + start_index, + component, + true); +} + +PUBLIC +rogue_regarray * +rogue_ssa_regarray(rogue_shader *shader, unsigned size, unsigned start_index) +{ + return rogue_regarray_cached(shader, size, ROGUE_REG_CLASS_SSA, start_index); +} + +PUBLIC +rogue_regarray * +rogue_temp_regarray(rogue_shader *shader, unsigned size, unsigned start_index) +{ + return rogue_regarray_cached(shader, size, ROGUE_REG_CLASS_TEMP, start_index); +} + +PUBLIC +rogue_regarray * +rogue_coeff_regarray(rogue_shader *shader, unsigned size, unsigned start_index) +{ + return rogue_regarray_cached(shader, + size, + ROGUE_REG_CLASS_COEFF, + start_index); +} + +PUBLIC +rogue_regarray *rogue_ssa_vec_regarray(rogue_shader *shader, + unsigned size, + unsigned start_index, + unsigned component) +{ + return rogue_vec_regarray_cached(shader, + size, + ROGUE_REG_CLASS_SSA, + start_index, + component); +} + +/** + * \brief Allocates and initializes a new rogue_block object. + * + * \param[in] shader The shader that the new block belongs to. + * \param[in] label The (optional) block label. + * \return The new block. + */ +PUBLIC +rogue_block *rogue_block_create(rogue_shader *shader, const char *label) +{ + rogue_block *block = rzalloc_size(shader, sizeof(*block)); + + block->shader = shader; + list_inithead(&block->instrs); + list_inithead(&block->uses); + block->index = shader->next_block++; + block->label = ralloc_strdup(block, label); + + return block; +} + +/** + * \brief Initialises a Rogue instruction. + * + * \param[in] instr The instruction to initialise. + * \param[in] type The instruction type. + * \param[in] block The block which will contain the instruction. + */ +static inline void rogue_instr_init(rogue_instr *instr, + enum rogue_instr_type type, + rogue_block *block) +{ + instr->type = type; + instr->index = block->shader->next_instr++; + instr->block = block; +} + +/** + * \brief Allocates and initializes a new rogue_alu_instr object. + * + * \param[in] block The block that the new ALU instruction belongs to. + * \param[in] op The ALU operation. + * \return The new ALU instruction. + */ +PUBLIC +rogue_alu_instr *rogue_alu_instr_create(rogue_block *block, + enum rogue_alu_op op) +{ + rogue_alu_instr *alu = rzalloc_size(block, sizeof(*alu)); + rogue_instr_init(&alu->instr, ROGUE_INSTR_TYPE_ALU, block); + alu->op = op; + + return alu; +} + +/** + * \brief Allocates and initializes a new rogue_backend_instr object. + * + * \param[in] block The block that the new backend instruction belongs to. + * \param[in] op The backend operation. + * \return The new backend instruction. + */ +PUBLIC +rogue_backend_instr *rogue_backend_instr_create(rogue_block *block, + enum rogue_backend_op op) +{ + rogue_backend_instr *backend = rzalloc_size(block, sizeof(*backend)); + rogue_instr_init(&backend->instr, ROGUE_INSTR_TYPE_BACKEND, block); + backend->op = op; + + return backend; +} + +/** + * \brief Allocates and initializes a new rogue_ctrl_instr object. + * + * \param[in] block The block that the new control instruction belongs to. + * \param[in] op The control operation. + * \return The new control instruction. + */ +PUBLIC +rogue_ctrl_instr *rogue_ctrl_instr_create(rogue_block *block, + enum rogue_ctrl_op op) +{ + rogue_ctrl_instr *ctrl = rzalloc_size(block, sizeof(*ctrl)); + rogue_instr_init(&ctrl->instr, ROGUE_INSTR_TYPE_CTRL, block); + ctrl->op = op; + + return ctrl; +} + +/** + * \brief Allocates and initializes a new rogue_bitwise_instr object. + * + * \param[in] block The block that the new bitwise instruction belongs to. + * \param[in] op The bitwise operation. + * \return The new bitwise instruction. + */ +PUBLIC +rogue_bitwise_instr *rogue_bitwise_instr_create(rogue_block *block, + enum rogue_bitwise_op op) +{ + rogue_bitwise_instr *bitwise = rzalloc_size(block, sizeof(*bitwise)); + rogue_instr_init(&bitwise->instr, ROGUE_INSTR_TYPE_BITWISE, block); + bitwise->op = op; + + return bitwise; +} + +/** + * \brief Tracks/links objects that are written to/modified by an instruction. + * + * \param[in] instr The instruction. + */ +PUBLIC +void rogue_link_instr_write(rogue_instr *instr) +{ + switch (instr->type) { + case ROGUE_INSTR_TYPE_ALU: { + rogue_alu_instr *alu = rogue_instr_as_alu(instr); + + if (rogue_ref_is_reg(&alu->dst.ref)) { + rogue_reg_write *write = &alu->dst_write.reg; + rogue_reg *reg = alu->dst.ref.reg; + rogue_link_instr_write_reg(instr, write, reg, 0); + } else if (rogue_ref_is_regarray(&alu->dst.ref)) { + struct util_dynarray **writearray = &alu->dst_write.regarray; + rogue_regarray *regarray = alu->dst.ref.regarray; + rogue_link_instr_write_regarray(instr, writearray, regarray, 0); + } else { + unreachable("Invalid destination reference type."); + } + + break; + } + + case ROGUE_INSTR_TYPE_BACKEND: { + rogue_backend_instr *backend = rogue_instr_as_backend(instr); + const unsigned num_dsts = rogue_backend_op_infos[backend->op].num_dsts; + + for (unsigned i = 0; i < num_dsts; ++i) { + if (rogue_ref_is_reg(&backend->dst[i].ref)) { + rogue_reg_write *write = &backend->dst_write[i].reg; + rogue_reg *reg = backend->dst[i].ref.reg; + rogue_link_instr_write_reg(instr, write, reg, i); + } else if (rogue_ref_is_regarray(&backend->dst[i].ref)) { + struct util_dynarray **writearray = &backend->dst_write[i].regarray; + rogue_regarray *regarray = backend->dst[i].ref.regarray; + rogue_link_instr_write_regarray(instr, writearray, regarray, i); + } else { + unreachable("Invalid destination reference type."); + } + } + + break; + } + + case ROGUE_INSTR_TYPE_CTRL: { + rogue_ctrl_instr *ctrl = rogue_instr_as_ctrl(instr); + const unsigned num_dsts = rogue_ctrl_op_infos[ctrl->op].num_dsts; + + for (unsigned i = 0; i < num_dsts; ++i) { + if (rogue_ref_is_reg(&ctrl->dst[i].ref)) { + rogue_reg_write *write = &ctrl->dst_write[i].reg; + rogue_reg *reg = ctrl->dst[i].ref.reg; + rogue_link_instr_write_reg(instr, write, reg, i); + } else if (rogue_ref_is_regarray(&ctrl->dst[i].ref)) { + struct util_dynarray **writearray = &ctrl->dst_write[i].regarray; + rogue_regarray *regarray = ctrl->dst[i].ref.regarray; + rogue_link_instr_write_regarray(instr, writearray, regarray, i); + } else { + unreachable("Invalid destination reference type."); + } + } + + break; + } + + default: + unreachable("Invalid instruction type."); + } +} + +/** + * \brief Tracks/links objects that are used by/read from an instruction. + * + * \param[in] instr The instruction. + */ +PUBLIC +void rogue_link_instr_use(rogue_instr *instr) +{ + switch (instr->type) { + case ROGUE_INSTR_TYPE_ALU: { + rogue_alu_instr *alu = rogue_instr_as_alu(instr); + const unsigned num_srcs = rogue_alu_op_infos[alu->op].num_srcs; + + for (unsigned i = 0; i < num_srcs; ++i) { + if (rogue_ref_is_reg(&alu->src[i].ref)) { + rogue_reg_use *use = &alu->src_use[i].reg; + rogue_reg *reg = alu->src[i].ref.reg; + rogue_link_instr_use_reg(instr, use, reg, i); + } else if (rogue_ref_is_regarray(&alu->src[i].ref)) { + struct util_dynarray **usearray = &alu->src_use[i].regarray; + rogue_regarray *regarray = alu->src[i].ref.regarray; + rogue_link_instr_use_regarray(instr, usearray, regarray, i); + } else if (rogue_ref_is_imm(&alu->src[i].ref)) { + rogue_link_imm_use(instr->block->shader, + instr, + i, + rogue_ref_get_imm(&alu->src[i].ref)); + } + } + + break; + } + + case ROGUE_INSTR_TYPE_BACKEND: { + rogue_backend_instr *backend = rogue_instr_as_backend(instr); + const unsigned num_srcs = rogue_backend_op_infos[backend->op].num_srcs; + + for (unsigned i = 0; i < num_srcs; ++i) { + if (rogue_ref_is_reg(&backend->src[i].ref)) { + rogue_reg_use *use = &backend->src_use[i].reg; + rogue_reg *reg = backend->src[i].ref.reg; + rogue_link_instr_use_reg(instr, use, reg, i); + } else if (rogue_ref_is_regarray(&backend->src[i].ref)) { + struct util_dynarray **usearray = &backend->src_use[i].regarray; + rogue_regarray *regarray = backend->src[i].ref.regarray; + rogue_link_instr_use_regarray(instr, usearray, regarray, i); + } else if (rogue_ref_is_drc(&backend->src[i].ref)) { + rogue_link_drc_trxn(instr->block->shader, + instr, + rogue_ref_get_drc(&backend->src[i].ref)); + } + } + + break; + } + + case ROGUE_INSTR_TYPE_CTRL: { + rogue_ctrl_instr *ctrl = rogue_instr_as_ctrl(instr); + const unsigned num_srcs = rogue_ctrl_op_infos[ctrl->op].num_srcs; + + /* Branch instruction. */ + if (!num_srcs && ctrl->target_block) { + rogue_link_instr_use_block(instr, + &ctrl->block_use, + ctrl->target_block); + break; + } + + for (unsigned i = 0; i < num_srcs; ++i) { + if (rogue_ref_is_reg(&ctrl->src[i].ref)) { + rogue_reg_use *use = &ctrl->src_use[i].reg; + rogue_reg *reg = ctrl->src[i].ref.reg; + rogue_link_instr_use_reg(instr, use, reg, i); + } else if (rogue_ref_is_regarray(&ctrl->src[i].ref)) { + struct util_dynarray **usearray = &ctrl->src_use[i].regarray; + rogue_regarray *regarray = ctrl->src[i].ref.regarray; + rogue_link_instr_use_regarray(instr, usearray, regarray, i); + } else if (rogue_ref_is_drc(&ctrl->src[i].ref)) { + /* WDF instructions consume/release drcs, handled independently. */ + if (ctrl->op != ROGUE_CTRL_OP_WDF) + rogue_link_drc_trxn(instr->block->shader, + instr, + rogue_ref_get_drc(&ctrl->src[i].ref)); + } + } + + break; + } + + default: + unreachable("Invalid instruction type."); + } +} + +/** + * \brief Untracks/unlinks objects that are written to/modified by an + * instruction. + * + * \param[in] instr The instruction. + */ +PUBLIC +void rogue_unlink_instr_write(rogue_instr *instr) +{ + switch (instr->type) { + case ROGUE_INSTR_TYPE_ALU: { + rogue_alu_instr *alu = rogue_instr_as_alu(instr); + + if (rogue_ref_is_reg(&alu->dst.ref)) { + rogue_reg_write *write = &alu->dst_write.reg; + rogue_unlink_instr_write_reg(instr, write); + } else if (rogue_ref_is_regarray(&alu->dst.ref)) { + struct util_dynarray **writearray = &alu->dst_write.regarray; + rogue_unlink_instr_write_regarray(instr, writearray); + } else { + unreachable("Invalid destination reference type."); + } + + break; + } + + case ROGUE_INSTR_TYPE_BACKEND: { + rogue_backend_instr *backend = rogue_instr_as_backend(instr); + const unsigned num_dsts = rogue_backend_op_infos[backend->op].num_dsts; + + for (unsigned i = 0; i < num_dsts; ++i) { + if (rogue_ref_is_reg(&backend->dst[i].ref)) { + rogue_reg_write *write = &backend->dst_write[i].reg; + rogue_unlink_instr_write_reg(instr, write); + } else if (rogue_ref_is_regarray(&backend->dst[i].ref)) { + struct util_dynarray **writearray = &backend->dst_write[i].regarray; + rogue_unlink_instr_write_regarray(instr, writearray); + } else { + unreachable("Invalid destination reference type."); + } + } + + break; + } + + case ROGUE_INSTR_TYPE_CTRL: { + rogue_ctrl_instr *ctrl = rogue_instr_as_ctrl(instr); + const unsigned num_dsts = rogue_ctrl_op_infos[ctrl->op].num_dsts; + + for (unsigned i = 0; i < num_dsts; ++i) { + if (rogue_ref_is_reg(&ctrl->dst[i].ref)) { + rogue_reg_write *write = &ctrl->dst_write[i].reg; + rogue_unlink_instr_write_reg(instr, write); + } else if (rogue_ref_is_regarray(&ctrl->dst[i].ref)) { + struct util_dynarray **writearray = &ctrl->dst_write[i].regarray; + rogue_unlink_instr_write_regarray(instr, writearray); + } else { + unreachable("Invalid destination reference type."); + } + } + + break; + } + + default: + unreachable("Invalid instruction type."); + } +} + +/** + * \brief Untracks/unlinks objects that are used by/read from an instruction. + * + * \param[in] instr The instruction. + */ +PUBLIC +void rogue_unlink_instr_use(rogue_instr *instr) +{ + switch (instr->type) { + case ROGUE_INSTR_TYPE_ALU: { + rogue_alu_instr *alu = rogue_instr_as_alu(instr); + const unsigned num_srcs = rogue_alu_op_infos[alu->op].num_srcs; + + for (unsigned i = 0; i < num_srcs; ++i) { + if (rogue_ref_is_reg(&alu->src[i].ref)) { + rogue_reg_use *use = &alu->src_use[i].reg; + rogue_unlink_instr_use_reg(instr, use); + } else if (rogue_ref_is_regarray(&alu->src[i].ref)) { + struct util_dynarray **usearray = &alu->src_use[i].regarray; + rogue_unlink_instr_use_regarray(instr, usearray); + } else if (rogue_ref_is_imm(&alu->src[i].ref)) { + rogue_unlink_imm_use(instr, + &rogue_ref_get_imm(&alu->src[i].ref)->use); + } + } + + break; + } + + case ROGUE_INSTR_TYPE_BACKEND: { + rogue_backend_instr *backend = rogue_instr_as_backend(instr); + const unsigned num_srcs = rogue_backend_op_infos[backend->op].num_srcs; + + for (unsigned i = 0; i < num_srcs; ++i) { + if (rogue_ref_is_reg(&backend->src[i].ref)) { + rogue_reg_use *use = &backend->src_use[i].reg; + rogue_unlink_instr_use_reg(instr, use); + } else if (rogue_ref_is_regarray(&backend->src[i].ref)) { + struct util_dynarray **usearray = &backend->src_use[i].regarray; + rogue_unlink_instr_use_regarray(instr, usearray); + } else if (rogue_ref_is_drc(&backend->src[i].ref)) { + rogue_unlink_drc_trxn(instr->block->shader, + instr, + rogue_ref_get_drc(&backend->src[i].ref)); + } + } + + break; + } + + case ROGUE_INSTR_TYPE_CTRL: { + rogue_ctrl_instr *ctrl = rogue_instr_as_ctrl(instr); + const unsigned num_srcs = rogue_ctrl_op_infos[ctrl->op].num_srcs; + + /* Branch instruction. */ + if (!num_srcs && ctrl->target_block) { + rogue_unlink_instr_use_block(instr, &ctrl->block_use); + break; + } + + for (unsigned i = 0; i < num_srcs; ++i) { + if (rogue_ref_is_reg(&ctrl->src[i].ref)) { + rogue_reg_use *use = &ctrl->src_use[i].reg; + rogue_unlink_instr_use_reg(instr, use); + } else if (rogue_ref_is_regarray(&ctrl->src[i].ref)) { + struct util_dynarray **usearray = &ctrl->src_use[i].regarray; + rogue_unlink_instr_use_regarray(instr, usearray); + } else if (rogue_ref_is_drc(&ctrl->src[i].ref)) { + /* WDF instructions consume/release drcs, handled independently. */ + if (ctrl->op != ROGUE_CTRL_OP_WDF) + rogue_unlink_drc_trxn(instr->block->shader, + instr, + rogue_ref_get_drc(&ctrl->src[i].ref)); + } + } + + break; + } + + default: + unreachable("Invalid instruction type."); + } +} + +static void rogue_compiler_destructor(UNUSED void *ptr) +{ + glsl_type_singleton_decref(); +} + +/** + * \brief Creates and sets up a Rogue compiler context. + * + * \param[in] dev_info Device info pointer. + * \return A pointer to the new compiler context, or NULL on failure. + */ +PUBLIC +rogue_compiler *rogue_compiler_create(const struct pvr_device_info *dev_info) +{ + rogue_compiler *compiler; + + rogue_debug_init(); + + compiler = rzalloc_size(NULL, sizeof(*compiler)); + if (!compiler) return NULL; - return shader; + compiler->dev_info = dev_info; + + /* TODO: Additional compiler setup (e.g. number of internal registers, BRNs, + * and other hw-specific info). */ + + glsl_type_singleton_init_or_ref(); + + ralloc_set_destructor(compiler, rogue_compiler_destructor); + + return compiler; } /** @@ -767,10 +996,10 @@ struct rogue_shader *rogue_nir_to_rogue(struct rogue_build_ctx *ctx, * \param[in] compiler The compiler context. * \return A pointer to the new build context, or NULL on failure. */ -struct rogue_build_ctx * -rogue_create_build_context(struct rogue_compiler *compiler) +PUBLIC +rogue_build_ctx *rogue_build_context_create(rogue_compiler *compiler) { - struct rogue_build_ctx *ctx; + rogue_build_ctx *ctx; ctx = rzalloc_size(compiler, sizeof(*ctx)); if (!ctx) diff --git a/src/imagination/rogue/rogue.h b/src/imagination/rogue/rogue.h index ca9ff8cbdee..32b536986a7 100644 --- a/src/imagination/rogue/rogue.h +++ b/src/imagination/rogue/rogue.h @@ -24,19 +24,2461 @@ #ifndef ROGUE_H #define ROGUE_H -#include +/** + * \file rogue.h + * + * \brief Main header. + */ + +/* TODO NEXT: Choose different names for Rogue IR src/dest vs e.g. S0-5, W0-1 as + * it's confusing. Maybe read/write? */ + +/* TODO: Look into using linear_alloc. */ +/* TODO NEXT: Make things const that can be. */ + +/* TODO NEXT: Use debug message functions in u_debug.h */ +/* TODO NEXT: for validating `repeat` values, add to rogue instr info which + * srcs/dsts will be affected and will need to be checked */ + +/* TODO NEXT: Reg/src/dst things are often just simple comparisons; write + * per-instr-type comparison function that also checks things like modifiers. */ + +/* TODO NEXT: In "unreachable"s, replace "invalid" with "unsupported". */ + +/* TODO NEXT: go through things that return bool and ensure they return 1 or 0, + * not a mask (whose first bit may be non-zero)! */ + +#include "compiler/nir/nir.h" +#include "compiler/shader_enums.h" +#include "compiler/spirv/nir_spirv.h" +#include "rogue_isa.h" +#include "util/bitscan.h" +#include "util/bitset.h" +#include "util/compiler.h" +#include "util/list.h" +#include "util/sparse_array.h" +#include "util/ralloc.h" +#include "util/u_dynarray.h" +#include "util/u_math.h" + +#include +#include +#include #include -#include "compiler/shader_enums.h" -#include "nir/nir.h" +/* Coefficient registers are typically used in groups of 4. */ +#define ROGUE_COEFF_ALIGN 4 + +#define ROGUE_REG_UNUSED ~0 /* All registers are 32-bit in size. */ #define ROGUE_REG_SIZE_BYTES 4 -#define ROGUE_REG_UNUSED UINT32_MAX -struct nir_spirv_specialization; -struct rogue_build_ctx; -struct rogue_shader; +/** Rogue register classes. */ +enum rogue_reg_class { + ROGUE_REG_CLASS_INVALID = 0, + + ROGUE_REG_CLASS_SSA, /** SSA register. */ + + ROGUE_REG_CLASS_TEMP, /** Temp register. */ + ROGUE_REG_CLASS_COEFF, /** Coefficient register. */ + ROGUE_REG_CLASS_SHARED, /** Shared register. */ + + ROGUE_REG_CLASS_SPECIAL, /** Special register. */ + ROGUE_REG_CLASS_INTERNAL, /** Internal register. */ + ROGUE_REG_CLASS_CONST, /** Constant register. */ + ROGUE_REG_CLASS_PIXOUT, /** Pixel output register. */ + + ROGUE_REG_CLASS_VTXIN, /** Vertex input register. */ + ROGUE_REG_CLASS_VTXOUT, /** Vertex output register. */ + + ROGUE_REG_CLASS_COUNT, +} ENUM_PACKED; + +typedef struct rogue_reg_info { + const char *name; /** Human-readable name. */ + const char *str; /** Register prefix. */ + unsigned num; /** Number of hardware registers available. */ + uint64_t supported_io_srcs; +} rogue_reg_info; + +extern const rogue_reg_info rogue_reg_infos[ROGUE_REG_CLASS_COUNT]; + +static inline enum reg_bank rogue_reg_bank_encoding(enum rogue_reg_class class) +{ + switch (class) { + case ROGUE_REG_CLASS_TEMP: + return BANK_TEMP; + case ROGUE_REG_CLASS_COEFF: + return BANK_COEFF; + case ROGUE_REG_CLASS_SHARED: + return BANK_SHARED; + case ROGUE_REG_CLASS_SPECIAL: + return BANK_SPECIAL; + case ROGUE_REG_CLASS_VTXIN: + return BANK_VTXIN; + + default: + unreachable("Unsupported register class."); + } +} + +enum rogue_regalloc_class { + ROGUE_REGALLOC_CLASS_TEMP_1, + ROGUE_REGALLOC_CLASS_TEMP_4, + + ROGUE_REGALLOC_CLASS_COUNT, +}; + +typedef struct rogue_regalloc_info { + enum rogue_reg_class class; + unsigned stride; +} rogue_regalloc_info; + +extern const rogue_regalloc_info regalloc_info[ROGUE_REGALLOC_CLASS_COUNT]; + +#define ROGUE_ISA_DSTS 2 +#define ROGUE_ISA_SRCS 6 +#define ROGUE_ISA_ISSS 6 + +#define ROGUE_ISA_ICACHE_ALIGN 8 + +typedef struct rogue_reg_dst_info { + unsigned num_dsts; + unsigned bank_bits[ROGUE_ISA_DSTS]; + unsigned index_bits[ROGUE_ISA_DSTS]; + unsigned bytes; +} rogue_reg_dst_info; + +#define ROGUE_REG_DST_VARIANTS 5 +extern const rogue_reg_dst_info rogue_reg_dst_infos[ROGUE_REG_DST_VARIANTS]; + +typedef struct rogue_reg_src_info { + unsigned num_srcs; + unsigned mux_bits; + unsigned bank_bits[ROGUE_ISA_SRCS / 2]; + unsigned index_bits[ROGUE_ISA_SRCS / 2]; + unsigned bytes; +} rogue_reg_src_info; + +#define ROGUE_REG_SRC_VARIANTS 8 +extern const rogue_reg_src_info + rogue_reg_lower_src_infos[ROGUE_REG_SRC_VARIANTS]; +extern const rogue_reg_src_info + rogue_reg_upper_src_infos[ROGUE_REG_SRC_VARIANTS]; + +typedef struct rogue_shader rogue_shader; +typedef struct rogue_reg rogue_reg; +typedef struct rogue_regarray rogue_regarray; + +/** Rogue register. */ +typedef struct rogue_reg { + rogue_shader *shader; /** Pointer back to shader. */ + enum rogue_reg_class class; /** Register class. */ + + struct list_head link; /** Link in rogue_shader::regs. */ + struct list_head writes; /** List of all writes to this register. */ + struct list_head uses; /** List of all register uses. */ + + rogue_regarray *regarray; + + bool dirty; + uint32_t index; /** Register index. */ + + rogue_reg **cached; +} rogue_reg; + +#define rogue_foreach_reg(reg, shader, class) \ + list_for_each_entry (rogue_reg, reg, &(shader)->regs[class], link) + +#define rogue_foreach_reg_safe(reg, shader, class) \ + list_for_each_entry_safe (rogue_reg, reg, &(shader)->regs[class], link) + +#define REG_CACHE_KEY_COMPONENT_BITS 3 +#define REG_CACHE_KEY_INDEX_BITS 28 +#define REG_CACHE_KEY_VEC_BITS 1 + +struct rogue_reg_cache_key { + union { + struct { + uint32_t component : REG_CACHE_KEY_COMPONENT_BITS; + uint32_t index : REG_CACHE_KEY_INDEX_BITS; + uint32_t vec : REG_CACHE_KEY_VEC_BITS; + } PACKED; + + uint32_t val; + } PACKED; +} PACKED; +static_assert(sizeof(struct rogue_reg_cache_key) == sizeof(uint32_t), + "sizeof(struct rogue_reg_cache_key) != sizeof(uint32_t)"); + +static inline uint32_t +rogue_reg_cache_key(unsigned index, bool vec, unsigned component) +{ + assert(util_last_bit(component) <= REG_CACHE_KEY_COMPONENT_BITS); + assert(!vec || util_last_bit(index) <= REG_CACHE_KEY_INDEX_BITS); + assert(vec || util_last_bit(index) <= 32); + assert(util_last_bit(vec) <= REG_CACHE_KEY_VEC_BITS); + + if (!vec) + return index; + + return (struct rogue_reg_cache_key){ .component = component, + .index = index, + .vec = vec } + .val; +} + +static inline bool rogue_reg_is_unused(rogue_reg *reg) +{ + return list_is_empty(®->uses) && list_is_empty(®->writes); +} + +typedef struct rogue_regarray { + struct list_head link; /** Link in rogue_shader::regarrays. */ + unsigned size; /** Number of registers in the array. */ + rogue_regarray *parent; + rogue_reg **regs; /** Registers (allocated array if this is a parent, else + pointer to inside parent regarray->regs). */ + rogue_regarray **cached; +} rogue_regarray; + +#define rogue_foreach_regarray(regarray, shader) \ + list_for_each_entry (rogue_regarray, regarray, &(shader)->regarrays, link) + +#define rogue_foreach_regarray_safe(regarray, shader) \ + list_for_each_entry_safe (rogue_regarray, \ + regarray, \ + &(shader)->regarrays, \ + link) + +struct rogue_regarray_cache_key { + union { + struct { + uint32_t start_index; + enum rogue_reg_class class; + uint16_t size; + } PACKED; + + uint64_t val; + } PACKED; +} PACKED; +static_assert(sizeof(struct rogue_regarray_cache_key) == sizeof(uint64_t), + "sizeof(struct rogue_regarray_cache_key) != sizeof(uint64_t)"); + +static inline uint64_t rogue_regarray_cache_key(unsigned size, + enum rogue_reg_class class, + uint32_t start_index, + bool vec, + uint8_t component) +{ + uint32_t reg_cache_key = rogue_reg_cache_key(start_index, vec, component); + return (struct rogue_regarray_cache_key){ .start_index = reg_cache_key, + .class = class, + .size = size } + .val; +} + +/** Instruction phases, used in bitset. */ +enum rogue_instr_phase { + /** Main/ALU (and backend) instructions. */ + ROGUE_INSTR_PHASE_0, + ROGUE_INSTR_PHASE_1, + ROGUE_INSTR_PHASE_2_PCK, + ROGUE_INSTR_PHASE_2_TST, + ROGUE_INSTR_PHASE_2_MOV, + ROGUE_INSTR_PHASE_BACKEND, + + ROGUE_INSTR_PHASE_COUNT, + + /** Control instructions (no co-issuing). */ + ROGUE_INSTR_PHASE_CTRL = ROGUE_INSTR_PHASE_0, + + /** Bitwise instructions. */ + ROGUE_INSTR_PHASE_0_BITMASK = ROGUE_INSTR_PHASE_0, + ROGUE_INSTR_PHASE_0_SHIFT1 = ROGUE_INSTR_PHASE_1, + ROGUE_INSTR_PHASE_0_COUNT = ROGUE_INSTR_PHASE_2_PCK, + ROGUE_INSTR_PHASE_1_LOGICAL = ROGUE_INSTR_PHASE_2_TST, + ROGUE_INSTR_PHASE_2_SHIFT2 = ROGUE_INSTR_PHASE_2_MOV, + ROGUE_INSTR_PHASE_2_TEST = ROGUE_INSTR_PHASE_BACKEND, + + ROGUE_INSTR_PHASE_INVALID = ~0, +}; + +#define rogue_foreach_phase_in_set(p, phases) \ + for (uint64_t __phases = (phases), p; \ + ((p) = ffsll(__phases) - 1, __phases); \ + __phases &= ~(1ull << (p))) + +#define rogue_foreach_phase_in_set_rev(p, phases) \ + for (uint64_t __phases = (phases), p; \ + ((p) = util_last_bit64(__phases) - 1, __phases); \ + __phases &= ~(1ull << (p))) + +/** Rogue basic block. */ +typedef struct rogue_block { + rogue_shader *shader; /** Shader containing this block. */ + struct list_head instrs; /** Basic block instruction list. */ + struct list_head link; /** Link in rogue_shader::blocks. */ + + struct list_head uses; /** List of all block uses. */ + + unsigned index; /** Block index. */ + const char *label; /** Block label. */ +} rogue_block; + +#define rogue_foreach_block(block, shader) \ + list_for_each_entry (rogue_block, block, &(shader)->blocks, link) + +#define rogue_foreach_block_safe(block, shader) \ + list_for_each_entry_safe (rogue_block, block, &(shader)->blocks, link) + +#define rogue_foreach_block_rev(block, shader) \ + list_for_each_entry_rev (rogue_block, block, &(shader)->blocks, link) + +#define rogue_foreach_block_safe_rev(block, shader) \ + list_for_each_entry_safe_rev (rogue_block, block, &(shader)->blocks, link) + +/** Rogue execution conditions. */ +enum rogue_exec_cond { + ROGUE_EXEC_COND_INVALID = 0, + + ROGUE_EXEC_COND_PE_TRUE, + ROGUE_EXEC_COND_P0_TRUE, + ROGUE_EXEC_COND_PE_ANY, + ROGUE_EXEC_COND_P0_FALSE, + + ROGUE_EXEC_COND_COUNT, +}; + +/** Rogue instruction type. */ +enum rogue_instr_type { + ROGUE_INSTR_TYPE_INVALID = 0, + + ROGUE_INSTR_TYPE_ALU, /** ALU instruction. */ + /* ROGUE_INSTR_TYPE_CMPLX, */ /** TODO: Complex/trig instruction (these take + up the whole pipeline). */ + ROGUE_INSTR_TYPE_BACKEND, /** Backend instruction. */ + ROGUE_INSTR_TYPE_CTRL, /** Control instruction. */ + ROGUE_INSTR_TYPE_BITWISE, /** Bitwise instruction. */ + /* ROGUE_INSTR_TYPE_F16SOP, */ /** TODO: F16 sum-of-products instruction. */ + + ROGUE_INSTR_TYPE_COUNT, +}; + +extern const char *rogue_instr_type_str[ROGUE_INSTR_TYPE_COUNT]; + +enum rogue_alu { + ROGUE_ALU_INVALID = 0, + + ROGUE_ALU_MAIN, + ROGUE_ALU_BITWISE, + ROGUE_ALU_CONTROL, + + ROGUE_ALU_COUNT, +}; + +extern const char *const rogue_alu_str[ROGUE_ALU_COUNT]; + +extern const char + *const rogue_instr_phase_str[ROGUE_ALU_COUNT][ROGUE_INSTR_PHASE_COUNT]; + +typedef struct rogue_instr_group rogue_instr_group; + +/** Rogue instruction. */ +typedef struct rogue_instr { + enum rogue_instr_type type; /** Instruction type. */ + + unsigned repeat; + bool end; + + union { + struct list_head link; /** Link in rogue_block::instrs. */ + rogue_instr_group *group; /** Instruction group containing this + instruction. */ + }; + + rogue_block *block; /** Basic block containing this instruction. */ + + unsigned index; /** Instruction index. */ + char *comment; /** Comment string. */ +} rogue_instr; + +#define rogue_foreach_instr_in_block(instr, block) \ + list_for_each_entry (rogue_instr, instr, &(block)->instrs, link) + +#define rogue_foreach_instr_in_block_safe(instr, block) \ + list_for_each_entry_safe (rogue_instr, instr, &(block)->instrs, link) + +#define rogue_foreach_instr_in_block_rev(instr, block) \ + list_for_each_entry_rev (rogue_instr, instr, &(block)->instrs, link) + +#define rogue_foreach_instr_in_block_safe_rev(instr, block) \ + list_for_each_entry_safe_rev (rogue_instr, instr, &(block)->instrs, link) + +#define rogue_foreach_instr_in_shader(instr, shader) \ + rogue_foreach_block (_block, (shader)) \ + rogue_foreach_instr_in_block ((instr), _block) + +#define rogue_foreach_instr_in_shader_safe(instr, shader) \ + rogue_foreach_block_safe (_block, (shader)) \ + rogue_foreach_instr_in_block_safe ((instr), _block) + +#define rogue_foreach_instr_in_shader_rev(instr, shader) \ + rogue_foreach_block_rev (_block, (shader)) \ + rogue_foreach_instr_in_block_rev ((instr), _block) + +#define rogue_foreach_instr_in_shader_safe_rev(instr, shader) \ + rogue_foreach_block_safe_rev (_block, (shader)) \ + rogue_foreach_instr_in_block_safe_rev ((instr), _block) + +static inline void rogue_set_instr_repeat(rogue_instr *instr, unsigned repeat) +{ + instr->repeat = repeat; +} + +static inline void rogue_add_instr_comment(rogue_instr *instr, + const char *comment) +{ + if (!instr->comment) + instr->comment = ralloc_strdup(instr, comment); + else + ralloc_asprintf_append(&instr->comment, ", %s", comment); +} + +static inline void rogue_copy_instr_comment(rogue_instr *to, + const rogue_instr *from) +{ + if (!from->comment) + return; + + rogue_add_instr_comment(to, from->comment); +} + +static inline void rogue_merge_instr_comment(rogue_instr *to, + const rogue_instr *from, + const char *comment) +{ + rogue_copy_instr_comment(to, from); + rogue_add_instr_comment(to, comment); +} + +typedef union rogue_imm_t { + float f32; + int32_t s32; + uint32_t u32; +} rogue_imm_t; + +enum rogue_io { + ROGUE_IO_INVALID = 0, + + /* Lower sources. */ + ROGUE_IO_S0, + ROGUE_IO_S1, + ROGUE_IO_S2, + + /* Upper sources. */ + ROGUE_IO_S3, + ROGUE_IO_S4, + ROGUE_IO_S5, + + /* Destinations. */ + ROGUE_IO_W0, + ROGUE_IO_W1, + + /* Internal selectors. */ + ROGUE_IO_IS0, + ROGUE_IO_IS1, + ROGUE_IO_IS2, + ROGUE_IO_IS3, + ROGUE_IO_IS4, + ROGUE_IO_IS5, + + /* Feedthroughs. */ + ROGUE_IO_FT0, + ROGUE_IO_FT1, + ROGUE_IO_FT2, + ROGUE_IO_FTE, + + /* Only used for bitwise instructions. */ + ROGUE_IO_FT3, + ROGUE_IO_FT4, + ROGUE_IO_FT5, + + /* Predicate register. */ + ROGUE_IO_P0, + + ROGUE_IO_COUNT, +}; + +static inline bool rogue_io_is_src(enum rogue_io io) +{ + return (io >= ROGUE_IO_S0 && io <= ROGUE_IO_S5); +} + +static inline bool rogue_io_is_dst(enum rogue_io io) +{ + return (io >= ROGUE_IO_W0 && io <= ROGUE_IO_W1); +} + +static inline bool rogue_io_is_iss(enum rogue_io io) +{ + return (io >= ROGUE_IO_IS0 && io <= ROGUE_IO_IS5); +} + +static inline bool rogue_io_is_ft(enum rogue_io io) +{ + return (io >= ROGUE_IO_FT0 && io <= ROGUE_IO_FTE); +} + +typedef struct rogue_io_info { + const char *str; +} rogue_io_info; + +extern const rogue_io_info rogue_io_infos[ROGUE_IO_COUNT]; + +static inline bool rogue_io_supported(enum rogue_io io, uint64_t supported_ios) +{ + return !!(BITFIELD64_BIT(io - 1) & supported_ios); +} + +#define ROGUE_DRCS 2 + +typedef struct rogue_drc_trxn { + rogue_instr *acquire; + rogue_instr *release; + struct list_head link; /** Link in rogue_shader::drc_trxns[0/1]. */ +} rogue_drc_trxn; + +#define rogue_foreach_drc_trxn(drc_trxn, shader, index) \ + list_for_each_entry (rogue_drc_trxn, \ + drc_trxn, \ + &(shader)->drc_trxns[index], \ + link) + +#define rogue_foreach_drc_trxn_safe(drc_trxn, shader, index) \ + list_for_each_entry_safe (rogue_drc_trxn, \ + drc_trxn, \ + &(shader)->drc_trxns[index], \ + link) + +enum rogue_ref_type { + ROGUE_REF_TYPE_INVALID = 0, + + ROGUE_REF_TYPE_VAL, /* Immediate that is not going to be replaced with a + register reference. */ + + ROGUE_REF_TYPE_REG, + ROGUE_REF_TYPE_REGARRAY, + + ROGUE_REF_TYPE_IMM, /* Immediate that is going to be replaced with a register + reference. */ + + ROGUE_REF_TYPE_IO, + + ROGUE_REF_TYPE_DRC, + + ROGUE_REF_TYPE_COUNT, +}; + +typedef struct rogue_drc { + unsigned index; + union { + rogue_drc_trxn trxn; + rogue_drc_trxn *trxn_ptr; + }; +} rogue_drc; + +typedef struct rogue_imm_use { + rogue_instr *instr; + unsigned src_index; + rogue_imm_t *imm; + struct list_head link; /** Link in rogue_shader::imm_uses. */ +} rogue_imm_use; + +#define rogue_foreach_imm_use(imm_use, shader) \ + list_for_each_entry (rogue_imm_use, imm_use, &(shader)->imm_uses, link) + +#define rogue_foreach_imm_use_safe(imm_use, shader) \ + list_for_each_entry_safe (rogue_imm_use, imm_use, &(shader)->imm_uses, link) + +typedef struct rogue_imm { + rogue_imm_t imm; + rogue_imm_use use; +} rogue_imm; + +typedef struct rogue_ref { + enum rogue_ref_type type; + + union { + unsigned val; + rogue_imm imm; + rogue_reg *reg; + rogue_regarray *regarray; + enum rogue_io io; + rogue_drc drc; + }; +} rogue_ref; + +static inline bool rogue_ref_type_supported(enum rogue_ref_type type, + uint64_t supported_types) +{ + return !!(BITFIELD64_BIT(type - 1) & supported_types); +} + +/** + * \brief Returns a reference to a value. + * + * \param[in] val The value. + * \return The reference. + */ +static inline rogue_ref rogue_ref_val(unsigned val) +{ + return (rogue_ref){ + .type = ROGUE_REF_TYPE_VAL, + .val = val, + }; +} + +/** + * \brief Returns a reference to a register. + * + * \param[in] reg The register. + * \return The reference. + */ +static inline rogue_ref rogue_ref_reg(rogue_reg *reg) +{ + return (rogue_ref){ + .type = ROGUE_REF_TYPE_REG, + .reg = reg, + }; +} + +/** + * \brief Returns a reference to a register array. + * + * \param[in] regarray The register array. + * \return The reference. + */ +static inline rogue_ref rogue_ref_regarray(rogue_regarray *regarray) +{ + return (rogue_ref){ + .type = ROGUE_REF_TYPE_REGARRAY, + .regarray = regarray, + }; +} + +static inline rogue_ref rogue_ref_imm(uint32_t imm) +{ + return (rogue_ref){ + .type = ROGUE_REF_TYPE_IMM, + .imm.imm.u32 = imm, + }; +} + +static inline rogue_ref rogue_ref_io(enum rogue_io io) +{ + return (rogue_ref){ + .type = ROGUE_REF_TYPE_IO, + .io = io, + }; +} + +static inline rogue_ref rogue_ref_drc(unsigned index) +{ + return (rogue_ref){ + .type = ROGUE_REF_TYPE_DRC, + .drc.index = index, + }; +} + +static inline rogue_ref rogue_ref_drc_trxn(unsigned index, + rogue_drc_trxn *drc_trxn) +{ + return (rogue_ref){ + .type = ROGUE_REF_TYPE_DRC, + .drc.index = index, + .drc.trxn_ptr = drc_trxn, + }; +} + +static inline rogue_ref rogue_ref_null(void) +{ + return (rogue_ref){}; +} + +static inline bool rogue_ref_is_imm(const rogue_ref *ref) +{ + return ref->type == ROGUE_REF_TYPE_IMM; +} + +static inline bool rogue_ref_is_val(const rogue_ref *ref) +{ + return ref->type == ROGUE_REF_TYPE_VAL; +} + +static inline bool rogue_ref_is_reg(const rogue_ref *ref) +{ + return ref->type == ROGUE_REF_TYPE_REG; +} + +static inline bool rogue_ref_is_regarray(const rogue_ref *ref) +{ + return ref->type == ROGUE_REF_TYPE_REGARRAY; +} + +static inline bool rogue_ref_is_io(const rogue_ref *ref) +{ + return ref->type == ROGUE_REF_TYPE_IO; +} + +static inline bool rogue_ref_is_drc(const rogue_ref *ref) +{ + return ref->type == ROGUE_REF_TYPE_DRC; +} + +static inline bool rogue_ref_is_null(const rogue_ref *ref) +{ + return ref->type == ROGUE_REF_TYPE_INVALID; +} + +static inline enum rogue_reg_class rogue_ref_get_reg_class(const rogue_ref *ref) +{ + if (rogue_ref_is_regarray(ref)) + return ref->regarray->regs[0]->class; + else if (rogue_ref_is_reg(ref)) + return ref->reg->class; + else + unreachable("Ref is not a reg/regarray."); +} + +static inline unsigned rogue_ref_get_reg_index(const rogue_ref *ref) +{ + if (rogue_ref_is_regarray(ref)) + return ref->regarray->regs[0]->index; + else if (rogue_ref_is_reg(ref)) + return ref->reg->index; + else + unreachable("Ref is not a reg/regarray."); +} + +#define ROGUE_INTERNAL0_OFFSET 36 +#define ROGUE_INTERNAL_GROUP 8 + +#define ROGUE_PIXOUT0_OFFSET 32 +#define ROGUE_PIXOUT4_OFFSET 164 +#define ROGUE_PIXOUT_GROUP 4 + +static inline bool rogue_ref_is_pixout(rogue_ref *ref) +{ + enum rogue_reg_class class; + unsigned index; + + if (!rogue_ref_is_reg(ref) && !rogue_ref_is_regarray(ref)) + return false; + + class = rogue_ref_get_reg_class(ref); + + if (class == ROGUE_REG_CLASS_PIXOUT) + return true; + else if (class != ROGUE_REG_CLASS_SPECIAL) + return false; + + index = rogue_ref_get_reg_index(ref); + + return (index >= ROGUE_PIXOUT0_OFFSET && + index < (ROGUE_PIXOUT0_OFFSET + ROGUE_PIXOUT_GROUP)) || + (index >= ROGUE_PIXOUT4_OFFSET && + index < (ROGUE_PIXOUT4_OFFSET + ROGUE_PIXOUT_GROUP)); +} + +static inline enum rogue_io rogue_ref_get_io(const rogue_ref *ref) +{ + assert(rogue_ref_is_io(ref)); + return ref->io; +} + +static inline unsigned rogue_ref_get_drc_index(const rogue_ref *ref) +{ + assert(rogue_ref_is_drc(ref)); + return ref->drc.index; +} + +static inline rogue_drc *rogue_ref_get_drc(rogue_ref *ref) +{ + assert(rogue_ref_is_drc(ref)); + return &ref->drc; +} + +static inline unsigned rogue_ref_get_val(const rogue_ref *ref) +{ + assert(rogue_ref_is_val(ref)); + return ref->val; +} + +static inline rogue_imm *rogue_ref_get_imm(rogue_ref *ref) +{ + assert(rogue_ref_is_imm(ref)); + return &ref->imm; +} + +static inline bool rogue_refs_equal(rogue_ref *a, rogue_ref *b) +{ + if (a->type != b->type) + return false; + + switch (a->type) { + case ROGUE_REF_TYPE_VAL: + return a->val == b->val; + + case ROGUE_REF_TYPE_REG: + return a->reg == b->reg; + + case ROGUE_REF_TYPE_REGARRAY: + return a->regarray == b->regarray; + + case ROGUE_REF_TYPE_IMM: + return a->imm.imm.u32 == b->imm.imm.u32; + + case ROGUE_REF_TYPE_IO: + return a->io == b->io; + + case ROGUE_REF_TYPE_DRC: + return a->drc.index == b->drc.index; + + default: + break; + } + + return false; +} + +typedef struct rogue_alu_dst { + rogue_ref ref; + uint64_t mod; +} rogue_alu_dst; + +typedef struct rogue_alu_src { + rogue_ref ref; + uint64_t mod; + unsigned index; +} rogue_alu_src; + +/* TODO: Comparison functions for other instruction types. */ +static inline bool rogue_alu_dst_src_equal(rogue_alu_dst *alu_dst, + rogue_alu_src *alu_src) +{ + /* TODO: Take modifiers into account. */ + if (alu_dst->mod || alu_src->mod) + return false; + + return rogue_refs_equal(&alu_dst->ref, &alu_src->ref); +} + +typedef struct rogue_reg_write { + rogue_instr *instr; + unsigned dst_index; + struct list_head link; /** Link in rogue_reg::writes. */ +} rogue_reg_write; + +#define rogue_foreach_reg_write(write, reg) \ + list_for_each_entry (rogue_reg_write, write, &(reg)->writes, link) + +#define rogue_foreach_reg_write_safe(write, reg) \ + list_for_each_entry_safe (rogue_reg_write, write, &(reg)->writes, link) + +typedef struct rogue_reg_use { + rogue_instr *instr; + unsigned src_index; + struct list_head link; /** Link in rogue_reg::uses. */ +} rogue_reg_use; + +#define rogue_foreach_reg_use(use, reg) \ + list_for_each_entry (rogue_reg_use, use, &(reg)->uses, link) + +#define rogue_foreach_reg_use_safe(use, reg) \ + list_for_each_entry_safe (rogue_reg_use, use, &(reg)->uses, link) + +typedef struct rogue_block_use { + rogue_instr *instr; + struct list_head link; /** Link in rogue_block::uses. */ +} rogue_block_use; + +#define rogue_foreach_block_use(use, block) \ + list_for_each_entry (rogue_block_use, use, &(block)->uses, link) + +#define rogue_foreach_block_use_safe(use, block) \ + list_for_each_entry_safe (rogue_block_use, use, &(block)->uses, link) + +/** Rogue ALU instruction operations. */ +enum rogue_alu_op { + ROGUE_ALU_OP_INVALID = 0, + + /* Real instructions. */ + + ROGUE_ALU_OP_MBYP, + + ROGUE_ALU_OP_FADD, + ROGUE_ALU_OP_FMUL, + ROGUE_ALU_OP_FMAD, + + ROGUE_ALU_OP_TST, + + ROGUE_ALU_OP_PCK_U8888, + + /* Pseudo-instructions. */ + ROGUE_ALU_OP_PSEUDO, + ROGUE_ALU_OP_MOV = ROGUE_ALU_OP_PSEUDO, + + ROGUE_ALU_OP_FABS, + ROGUE_ALU_OP_FNEG, + ROGUE_ALU_OP_FNABS, + + ROGUE_ALU_OP_FMAX, + ROGUE_ALU_OP_FMIN, + + ROGUE_ALU_OP_SEL, + + ROGUE_ALU_OP_COUNT, +}; + +enum rogue_alu_op_mod { + /* In order of priority */ + ROGUE_ALU_OP_MOD_LP, /* Low-precision modifier (force 13 lsbs of all sources + to zero before op, and of result after op). */ + ROGUE_ALU_OP_MOD_SAT, /* Saturate output. */ + + ROGUE_ALU_OP_MOD_SCALE, /* Scale to [0, 1]. */ + ROGUE_ALU_OP_MOD_ROUNDZERO, /* Round to zero. */ + + ROGUE_ALU_OP_MOD_COUNT, +}; + +typedef struct rogue_alu_op_mod_info { + const char *str; +} rogue_alu_op_mod_info; + +extern const rogue_alu_op_mod_info + rogue_alu_op_mod_infos[ROGUE_ALU_OP_MOD_COUNT]; + +static inline bool rogue_mods_supported(uint64_t mods, uint64_t supported_mods) +{ + return !(mods & ~supported_mods); +} + +enum rogue_alu_dst_mod { + ROGUE_ALU_DST_MOD_E0, + ROGUE_ALU_DST_MOD_E1, + ROGUE_ALU_DST_MOD_E2, + ROGUE_ALU_DST_MOD_E3, + + ROGUE_ALU_DST_MOD_COUNT, +}; + +typedef struct rogue_alu_dst_mod_info { + const char *str; +} rogue_alu_dst_mod_info; + +extern const rogue_alu_dst_mod_info + rogue_alu_dst_mod_infos[ROGUE_ALU_DST_MOD_COUNT]; + +enum rogue_alu_src_mod { + /* In order of priority, i.e. if all NEG, ABS, and FLR are all set, FLR will + happen first, then ABS, then NEG. */ + ROGUE_ALU_SRC_MOD_FLR, + ROGUE_ALU_SRC_MOD_ABS, + ROGUE_ALU_SRC_MOD_NEG, + + ROGUE_ALU_SRC_MOD_COUNT, +}; + +typedef struct rogue_alu_src_mod_info { + const char *str; +} rogue_alu_src_mod_info; + +extern const rogue_alu_src_mod_info + rogue_alu_src_mod_infos[ROGUE_ALU_SRC_MOD_COUNT]; + +enum rogue_ctrl_op { + ROGUE_CTRL_OP_INVALID = 0, + + /* Real instructions. */ + ROGUE_CTRL_OP_NOP, + ROGUE_CTRL_OP_BA, + ROGUE_CTRL_OP_WDF, + + /* Pseudo-instructions. */ + ROGUE_CTRL_OP_PSEUDO, + ROGUE_CTRL_OP_END = ROGUE_CTRL_OP_PSEUDO, + + ROGUE_CTRL_OP_COUNT, +}; + +enum rogue_ctrl_op_mod { + /* In order of priority */ + ROGUE_CTRL_OP_MOD_END, + + ROGUE_CTRL_OP_MOD_COUNT, +}; + +typedef struct rogue_ctrl_op_mod_info { + const char *str; +} rogue_ctrl_op_mod_info; + +extern const rogue_ctrl_op_mod_info + rogue_ctrl_op_mod_infos[ROGUE_CTRL_OP_MOD_COUNT]; + +typedef struct rogue_ctrl_op_info { + const char *str; + + bool has_target; /* Has a block as a target. */ + bool ends_block; /* Can be the instruction at the end of a block. */ + bool has_srcs; /* Has encodable sources. */ + bool has_dsts; /* Has encodable destinations. */ + + unsigned num_dsts; + unsigned num_srcs; + + uint64_t supported_op_mods; +} rogue_ctrl_op_info; + +extern const rogue_ctrl_op_info rogue_ctrl_op_infos[ROGUE_CTRL_OP_COUNT]; + +static inline bool rogue_ctrl_op_has_srcs(enum rogue_ctrl_op op) +{ + const rogue_ctrl_op_info *info = &rogue_ctrl_op_infos[op]; + return info->has_srcs; +} + +static inline bool rogue_ctrl_op_has_dsts(enum rogue_ctrl_op op) +{ + const rogue_ctrl_op_info *info = &rogue_ctrl_op_infos[op]; + return info->has_dsts; +} + +/* ALU instructions have at most 3 sources. */ +#define ROGUE_ALU_OP_MAX_SRCS 3 + +typedef struct rogue_alu_io_info { + enum rogue_io dst; + enum rogue_io src[ROGUE_ALU_OP_MAX_SRCS]; +} rogue_alu_io_info; + +/** Rogue ALU instruction operation info. */ +typedef struct rogue_alu_op_info { + const char *str; + + unsigned num_srcs; + + uint64_t supported_phases; + rogue_alu_io_info phase_io[ROGUE_INSTR_PHASE_COUNT]; + + uint64_t supported_op_mods; + uint64_t supported_dst_mods; + uint64_t supported_src_mods[ROGUE_ALU_OP_MAX_SRCS]; + + /* TODO NEXT: Do the same for other instruction types. */ + uint64_t supported_dst_types; + uint64_t supported_src_types[ROGUE_ALU_OP_MAX_SRCS]; +} rogue_alu_op_info; + +extern const rogue_alu_op_info rogue_alu_op_infos[ROGUE_ALU_OP_COUNT]; + +enum rogue_comp_test { + ROGUE_COMP_TEST_NONE = 0, + + /* 0-source */ + /* ROGUE_COMP_TEST_IC, */ /* Integer carry-out, for INT64 pipeline only. */ + + /* 1-source */ + /* ROGUE_COMP_TEST_Z, */ + /* ROGUE_COMP_TEST_GZ, */ + /* ROGUE_COMP_TEST_GEZ, */ + + /* 2-source */ + ROGUE_COMP_TEST_EQ, + ROGUE_COMP_TEST_GT, + ROGUE_COMP_TEST_GE, + ROGUE_COMP_TEST_NE, + ROGUE_COMP_TEST_LT, + ROGUE_COMP_TEST_LE, + + ROGUE_COMP_TEST_COUNT, +}; + +extern const char *const rogue_comp_test_str[ROGUE_COMP_TEST_COUNT]; + +enum rogue_comp_type { + ROGUE_COMP_TYPE_NONE = 0, + + ROGUE_COMP_TYPE_F32, + ROGUE_COMP_TYPE_U16, + ROGUE_COMP_TYPE_S16, + ROGUE_COMP_TYPE_U8, + ROGUE_COMP_TYPE_S8, + ROGUE_COMP_TYPE_U32, + ROGUE_COMP_TYPE_S32, + + ROGUE_COMP_TYPE_COUNT, +}; + +extern const char *const rogue_comp_type_str[ROGUE_COMP_TYPE_COUNT]; + +/** Rogue ALU instruction. */ +typedef struct rogue_alu_instr { + rogue_instr instr; + + enum rogue_alu_op op; + + enum rogue_comp_test comp_test; + enum rogue_comp_type comp_type; + + uint64_t mod; + + rogue_alu_dst dst; + + union { + rogue_reg_write reg; + struct util_dynarray *regarray; + } dst_write; + + rogue_alu_src src[ROGUE_ALU_OP_MAX_SRCS]; + + union { + rogue_reg_use reg; + struct util_dynarray *regarray; + } src_use[ROGUE_ALU_OP_MAX_SRCS]; +} rogue_alu_instr; + +static inline void rogue_set_alu_op_mod(rogue_alu_instr *alu, + enum rogue_alu_op_mod mod) +{ + alu->mod |= BITFIELD64_BIT(mod); +} + +static inline bool rogue_alu_op_mod_is_set(const rogue_alu_instr *alu, + enum rogue_alu_op_mod mod) +{ + return !!(alu->mod & BITFIELD64_BIT(mod)); +} + +static inline void rogue_set_alu_dst_mod(rogue_alu_instr *alu, + enum rogue_alu_dst_mod mod) +{ + alu->dst.mod |= BITFIELD64_BIT(mod); +} + +static inline void rogue_set_alu_src_mod(rogue_alu_instr *alu, + unsigned src_index, + enum rogue_alu_src_mod mod) +{ + alu->src[src_index].mod |= BITFIELD64_BIT(mod); +} + +static inline bool rogue_alu_src_mod_is_set(const rogue_alu_instr *alu, + unsigned src_index, + enum rogue_alu_src_mod mod) +{ + return !!(alu->src[src_index].mod & BITFIELD64_BIT(mod)); +} + +static inline void rogue_set_alu_comp(rogue_alu_instr *alu, + enum rogue_comp_test test, + enum rogue_comp_type comp_type) +{ + if (alu->op != ROGUE_ALU_OP_TST) + unreachable("Can't set comparisons on non-test instructions."); + + alu->comp_test = test; + alu->comp_type = comp_type; +} + +static inline bool rogue_alu_comp_is_none(const rogue_alu_instr *alu) +{ + return (alu->comp_test == ROGUE_COMP_TEST_NONE) && + (alu->comp_type == ROGUE_COMP_TYPE_NONE); +} + +/** + * \brief Allocates and initializes a new ALU instruction. + * + * \param[in] block The block which will contain the instruction. + * \param[in] op The ALU instruction operation. + * \return The new instruction. + */ +rogue_alu_instr *rogue_alu_instr_create(rogue_block *block, + enum rogue_alu_op op); + +/// + +#define ROGUE_BACKEND_OP_MAX_SRCS 6 +#define ROGUE_BACKEND_OP_MAX_DSTS 2 + +typedef struct rogue_backend_dst { + rogue_ref ref; + unsigned index; +} rogue_backend_dst; + +typedef struct rogue_backend_src { + rogue_ref ref; + unsigned index; +} rogue_backend_src; + +enum rogue_backend_op { + ROGUE_BACKEND_OP_INVALID = 0, + + ROGUE_BACKEND_OP_UVSW_WRITE, + ROGUE_BACKEND_OP_UVSW_EMIT, + /* ROGUE_BACKEND_OP_UVSW_CUT, */ + /* ROGUE_BACKEND_OP_UVSW_EMITTHENCUT, */ + ROGUE_BACKEND_OP_UVSW_ENDTASK, + ROGUE_BACKEND_OP_UVSW_EMITTHENENDTASK, + ROGUE_BACKEND_OP_UVSW_WRITETHENEMITTHENENDTASK, + + /* ROGUE_BACKEND_OP_FITR, */ + /* ROGUE_BACKEND_OP_SAMPLE, */ + /* ROGUE_BACKEND_OP_CENTROID, */ + ROGUE_BACKEND_OP_FITRP_PIXEL, + /* ROGUE_BACKEND_OP_FITRP_SAMPLE, */ + /* ROGUE_BACKEND_OP_FITRP_CENTROID, */ + + ROGUE_BACKEND_OP_PSEUDO, + ROGUE_BACKEND_OP_COUNT = ROGUE_BACKEND_OP_PSEUDO, +}; + +typedef struct rogue_backend_io_info { + enum rogue_io dst[ROGUE_BACKEND_OP_MAX_DSTS]; + enum rogue_io src[ROGUE_BACKEND_OP_MAX_SRCS]; +} rogue_backend_io_info; + +typedef struct rogue_backend_op_info { + const char *str; + + unsigned num_dsts; + unsigned num_srcs; + + /* supported_phases not needed as it's always going to be in the backend + * phase. */ + rogue_backend_io_info phase_io; + + uint64_t supported_op_mods; +} rogue_backend_op_info; + +extern const rogue_backend_op_info + rogue_backend_op_infos[ROGUE_BACKEND_OP_COUNT]; + +enum rogue_backend_op_mod { + /* In order of priority */ + ROGUE_BACKEND_OP_MOD_SAT, /* Saturate output. */ + + ROGUE_BACKEND_OP_MOD_COUNT, +}; + +typedef struct rogue_backend_op_mod_info { + const char *str; +} rogue_backend_op_mod_info; + +extern const rogue_backend_op_mod_info + rogue_backend_op_mod_infos[ROGUE_BACKEND_OP_MOD_COUNT]; + +typedef struct rogue_backend_instr { + rogue_instr instr; + + enum rogue_backend_op op; + + uint64_t mod; + + /* Backend instructions don't have source/dest modifiers. */ + + rogue_backend_dst dst[ROGUE_BACKEND_OP_MAX_DSTS]; + union { + rogue_reg_write reg; + struct util_dynarray *regarray; + } dst_write[ROGUE_BACKEND_OP_MAX_DSTS]; + + rogue_backend_src src[ROGUE_BACKEND_OP_MAX_SRCS]; + union { + rogue_reg_use reg; + struct util_dynarray *regarray; + } src_use[ROGUE_BACKEND_OP_MAX_SRCS]; +} rogue_backend_instr; + +static inline void rogue_set_backend_op_mod(rogue_backend_instr *backend, + enum rogue_backend_op_mod mod) +{ + backend->mod |= BITFIELD64_BIT(mod); +} + +static inline bool +rogue_backend_op_mod_is_set(const rogue_backend_instr *backend, + enum rogue_backend_op_mod mod) +{ + return !!(backend->mod & BITFIELD64_BIT(mod)); +} + +rogue_backend_instr *rogue_backend_instr_create(rogue_block *block, + enum rogue_backend_op op); + +#define ROGUE_CTRL_OP_MAX_SRCS 7 +#define ROGUE_CTRL_OP_MAX_DSTS 2 + +typedef struct rogue_ctrl_dst { + rogue_ref ref; + unsigned index; +} rogue_ctrl_dst; + +typedef struct rogue_ctrl_src { + rogue_ref ref; + unsigned index; +} rogue_ctrl_src; + +typedef struct rogue_ctrl_instr { + rogue_instr instr; + + enum rogue_ctrl_op op; + + uint64_t mod; + + /* Control instructions don't have source/dest modifiers. */ + + rogue_ctrl_dst dst[ROGUE_CTRL_OP_MAX_DSTS]; + union { + rogue_reg_write reg; + struct util_dynarray *regarray; + } dst_write[ROGUE_BACKEND_OP_MAX_DSTS]; + + rogue_ctrl_src src[ROGUE_CTRL_OP_MAX_SRCS]; + union { + rogue_reg_use reg; + struct util_dynarray *regarray; + } src_use[ROGUE_BACKEND_OP_MAX_SRCS]; + + rogue_block *target_block; + rogue_block_use block_use; +} rogue_ctrl_instr; + +static inline void rogue_set_ctrl_op_mod(rogue_ctrl_instr *ctrl, + enum rogue_ctrl_op_mod mod) +{ + ctrl->mod |= BITFIELD64_BIT(mod); +} + +static inline bool rogue_ctrl_op_mod_is_set(const rogue_ctrl_instr *ctrl, + enum rogue_ctrl_op_mod mod) +{ + return !!(ctrl->mod & BITFIELD64_BIT(mod)); +} + +/** + * \brief Allocates and initializes a new control instruction. + * + * \param[in] block The block which will contain the instruction. + * \param[in] op The ALU instruction operation. + * \return The new instruction. + */ +rogue_ctrl_instr *rogue_ctrl_instr_create(rogue_block *block, + enum rogue_ctrl_op op); + +enum rogue_bitwise_op { + ROGUE_BITWISE_OP_INVALID = 0, + + /* Real instructions. */ + ROGUE_BITWISE_OP_BYP, + + /* Pseudo-instructions. */ + ROGUE_BITWISE_OP_PSEUDO, + ROGUE_BITWISE_OP_MOV2 = ROGUE_BITWISE_OP_PSEUDO, + + ROGUE_BITWISE_OP_COUNT, +}; + +typedef struct rogue_bitwise_op_info { + const char *str; + + unsigned num_dsts; + unsigned num_srcs; + + /* uint64_t supported_op_mods; */ +} rogue_bitwise_op_info; + +extern const rogue_bitwise_op_info + rogue_bitwise_op_infos[ROGUE_BITWISE_OP_COUNT]; + +#define ROGUE_BITWISE_OP_MAX_SRCS 7 +#define ROGUE_BITWISE_OP_MAX_DSTS 2 + +typedef struct rogue_bitwise_dst { + rogue_ref ref; + unsigned index; +} rogue_bitwise_dst; + +typedef struct rogue_bitwise_src { + rogue_ref ref; + unsigned index; +} rogue_bitwise_src; + +typedef struct rogue_bitwise_instr { + rogue_instr instr; + + enum rogue_bitwise_op op; + + /* TODO: op mods */ + /* uint64_t mod; */ + + /* TODO NEXT: source/dest modifiers */ + + rogue_bitwise_dst dst[ROGUE_BITWISE_OP_MAX_DSTS]; + union { + rogue_reg_write reg; + struct util_dynarray *regarray; + } dst_write[ROGUE_BACKEND_OP_MAX_DSTS]; + + rogue_bitwise_src src[ROGUE_BITWISE_OP_MAX_SRCS]; + union { + rogue_reg_use reg; + struct util_dynarray *regarray; + } src_use[ROGUE_BACKEND_OP_MAX_SRCS]; +} rogue_bitwise_instr; + +#if 0 +static inline +void +rogue_set_bitwise_op_mod(rogue_bitwise_instr *bitwise, enum rogue_bitwise_op_mod mod) +{ + bitwise->mod |= BITFIELD64_BIT(mod); +} + +static inline +bool +rogue_bitwise_op_mod_is_set(const rogue_bitwise_instr *bitwise, enum rogue_bitwise_op_mod mod) +{ + return !!(bitwise->mod & BITFIELD64_BIT(mod)); +} +#endif + +/** + * \brief Allocates and initializes a new bitwise instruction. + * + * \param[in] op The ALU instruction operation. + * \return The new instruction. + */ +rogue_bitwise_instr *rogue_bitwise_instr_create(rogue_block *block, + enum rogue_bitwise_op op); + +/** Defines a cast function + * + * This macro defines a cast function from in_type to out_type where + * out_type is some structure type that contains a field of type out_type. + * + * Note that you have to be a bit careful as the generated cast function + * destroys constness. + */ +#define ROGUE_DEFINE_CAST(name, \ + in_type, \ + out_type, \ + field, \ + type_field, \ + type_value) \ + static inline out_type *name(const in_type *parent) \ + { \ + assert(parent && parent->type_field == type_value); \ + return list_entry(parent, out_type, field); \ + } + +ROGUE_DEFINE_CAST(rogue_instr_as_alu, + rogue_instr, + rogue_alu_instr, + instr, + type, + ROGUE_INSTR_TYPE_ALU) +ROGUE_DEFINE_CAST(rogue_instr_as_backend, + rogue_instr, + rogue_backend_instr, + instr, + type, + ROGUE_INSTR_TYPE_BACKEND) +ROGUE_DEFINE_CAST(rogue_instr_as_ctrl, + rogue_instr, + rogue_ctrl_instr, + instr, + type, + ROGUE_INSTR_TYPE_CTRL) +ROGUE_DEFINE_CAST(rogue_instr_as_bitwise, + rogue_instr, + rogue_bitwise_instr, + instr, + type, + ROGUE_INSTR_TYPE_BITWISE) + +static inline enum rogue_io rogue_instr_src_io_src(const rogue_instr *instr, + enum rogue_instr_phase phase, + unsigned src_index) +{ + switch (instr->type) { + case ROGUE_INSTR_TYPE_ALU: { + const rogue_alu_instr *alu = rogue_instr_as_alu(instr); + const rogue_alu_op_info *info = &rogue_alu_op_infos[alu->op]; + return info->phase_io[phase].src[src_index]; + } + + case ROGUE_INSTR_TYPE_BACKEND: { + const rogue_backend_instr *backend = rogue_instr_as_backend(instr); + const rogue_backend_op_info *info = &rogue_backend_op_infos[backend->op]; + return info->phase_io.src[src_index]; + } + + case ROGUE_INSTR_TYPE_CTRL: { + /* TODO after phase_io is added to relevant control instructions as well. + */ + break; + } + + default: + unreachable("Unsupported instruction type."); + break; + } + + return ROGUE_IO_INVALID; +} + +/* Maps sources and destinations ("inputs"/"outputs") to registers. */ +typedef struct rogue_instr_group_io_sel { + rogue_ref srcs[ROGUE_ISA_SRCS]; /** Upper + lower sources. */ + rogue_ref dsts[ROGUE_ISA_DSTS]; /** Destinations. */ + rogue_ref iss[ROGUE_ISA_ISSS]; /** Internal source selector (includes + IS0/MUX). */ +} rogue_instr_group_io_sel; + +static inline rogue_ref * +rogue_instr_group_io_sel_ref(rogue_instr_group_io_sel *map, enum rogue_io io) +{ + if (rogue_io_is_src(io)) + return &map->srcs[io - ROGUE_IO_S0]; + else if (rogue_io_is_dst(io)) + return &map->dsts[io - ROGUE_IO_W0]; + else if (rogue_io_is_iss(io)) + return &map->iss[io - ROGUE_IO_IS0]; + unreachable("Invalid io."); +} + +/** Rogue instruction group. */ +typedef struct rogue_instr_group { + rogue_block *block; + struct list_head link; /** Link in rogue_block::instrs. */ + + rogue_instr *instrs[ROGUE_INSTR_PHASE_COUNT]; /** Instructions in group. */ + rogue_instr_group_io_sel io_sel; /** Source, destination, internal source + selector maps. */ + + struct { + uint64_t phases; /** Instructions phases present. */ + + enum rogue_exec_cond exec_cond; + enum rogue_alu alu; + + bool end; /** Shader end flag. */ + unsigned repeat; + } header; + + struct { + unsigned header; + unsigned instrs[ROGUE_INSTR_PHASE_COUNT]; + unsigned lower_srcs; + unsigned upper_srcs; + unsigned iss; + unsigned dsts; + unsigned word_padding; /* Padding to make total size a word (% 2 == 0) */ + unsigned align_padding; /* Padding to align instruction position in memory + */ + unsigned total; + + unsigned offset; + } size; + + struct { + unsigned lower_src_index; + unsigned upper_src_index; + unsigned dst_index; + } encode_info; + + unsigned index; /** For debug purposes. */ +} rogue_instr_group; + +#define rogue_foreach_instr_group_in_block(group, block) \ + list_for_each_entry (rogue_instr_group, group, &(block)->instrs, link) + +#define rogue_foreach_instr_group_in_block_safe(group, block) \ + list_for_each_entry_safe (rogue_instr_group, group, &(block)->instrs, link) + +#define rogue_foreach_instr_group_in_shader(group, shader) \ + rogue_foreach_block (_block, (shader)) \ + rogue_foreach_instr_group_in_block ((group), _block) + +#define rogue_foreach_instr_group_in_shader_safe(group, shader) \ + rogue_foreach_block_safe (_block, (shader)) \ + rogue_foreach_instr_group_in_block_safe ((group), _block) + +static inline rogue_instr_group *rogue_instr_group_create(rogue_block *block, + enum rogue_alu alu) +{ + rogue_instr_group *group = rzalloc_size(block, sizeof(*group)); + group->header.alu = alu; + group->block = block; + return group; +} + +typedef struct rogue_build_ctx rogue_build_ctx; + +/** Rogue shader object. */ +typedef struct rogue_shader { + gl_shader_stage stage; /** Shader stage. */ + + rogue_build_ctx *ctx; /** Build context. */ + + unsigned next_instr; /** Next instruction index. */ + unsigned next_block; /** Next block index. */ + + struct list_head blocks; /** List of basic blocks. */ + struct list_head regs[ROGUE_REG_CLASS_COUNT]; /** List of registers used by + the shader. */ + BITSET_WORD *regs_used[ROGUE_REG_CLASS_COUNT]; /** Bitset of register numbers + used. */ + struct util_sparse_array reg_cache[ROGUE_REG_CLASS_COUNT]; + + struct list_head regarrays; /** List of register arrays used by the shader. + */ + struct util_sparse_array regarray_cache; + + struct list_head drc_trxns[ROGUE_DRCS]; /** List of drc transactions. */ + + struct list_head imm_uses; /** List of immediate value uses. */ + + bool is_grouped; /** Whether the instructions are grouped. */ + + const char *name; /** Shader name. */ +} rogue_shader; + +static inline void rogue_set_shader_name(rogue_shader *shader, const char *name) +{ + shader->name = ralloc_strdup(shader, name); +} + +/** + * \brief Allocates and initializes a new rogue_shader object. + * + * \param[in] mem_ctx The parent memory context. + * \param[in] stage The shader stage. + * \return The new shader. + */ +rogue_shader *rogue_shader_create(void *mem_ctx, gl_shader_stage stage); + +rogue_reg *rogue_ssa_reg(rogue_shader *shader, unsigned index); + +rogue_reg *rogue_temp_reg(rogue_shader *shader, unsigned index); + +rogue_reg *rogue_coeff_reg(rogue_shader *shader, unsigned index); + +rogue_reg *rogue_shared_reg(rogue_shader *shader, unsigned index); + +rogue_reg *rogue_const_reg(rogue_shader *shader, unsigned index); + +rogue_reg *rogue_pixout_reg(rogue_shader *shader, unsigned index); + +rogue_reg *rogue_vtxin_reg(rogue_shader *shader, unsigned index); + +rogue_reg *rogue_vtxout_reg(rogue_shader *shader, unsigned index); + +rogue_reg * +rogue_ssa_vec_reg(rogue_shader *shader, unsigned index, unsigned component); + +void rogue_reg_delete(rogue_reg *reg); + +rogue_regarray * +rogue_ssa_regarray(rogue_shader *shader, unsigned size, unsigned start_index); + +rogue_regarray * +rogue_temp_regarray(rogue_shader *shader, unsigned size, unsigned start_index); + +rogue_regarray * +rogue_coeff_regarray(rogue_shader *shader, unsigned size, unsigned start_index); + +rogue_regarray *rogue_ssa_vec_regarray(rogue_shader *shader, + unsigned size, + unsigned start_index, + unsigned component); + +static inline void rogue_reset_reg_usage(rogue_shader *shader, + enum rogue_reg_class class) +{ + const rogue_reg_info *info = &rogue_reg_infos[class]; + + if (info->num) { + memset(shader->regs_used[class], + 0, + sizeof(*shader->regs_used[class]) * BITSET_WORDS(info->num)); + } + + rogue_foreach_reg (reg, shader, class) { + reg->dirty = false; + } +} + +bool rogue_reg_set(rogue_shader *shader, + rogue_reg *reg, + enum rogue_reg_class class, + unsigned index); + +bool rogue_reg_rewrite(rogue_shader *shader, + rogue_reg *reg, + enum rogue_reg_class class, + unsigned index); + +/** Cursor for Rogue instructions/groups and basic blocks. */ +typedef struct rogue_cursor { + bool block; + struct list_head *prev; /** Linked-list pointer to before the object. */ + bool first; /** Whether the cursor is pointing to the first element. */ +} rogue_cursor; + +/** + * \brief Returns a cursor set to the beginning of the shader. + * + * \param[in] shader The shader. + * \return The cursor. + */ +static inline rogue_cursor rogue_cursor_before_shader(rogue_shader *shader) +{ + return (rogue_cursor){ + .block = true, + .prev = &shader->blocks, + .first = true, + }; +} + +/** + * \brief Returns a cursor set to before a block. + * + * \param[in] block The block. + * \return The cursor. + */ +static inline rogue_cursor rogue_cursor_before_block(rogue_block *block) +{ + return (rogue_cursor){ + .block = true, + .prev = block->link.prev, + .first = (block->link.prev == &block->shader->blocks), + }; +} + +/** + * \brief Returns a cursor set to after a block. + * + * \param[in] block The block. + * \return The cursor. + */ +static inline rogue_cursor rogue_cursor_after_block(rogue_block *block) +{ + return (rogue_cursor){ + .block = true, + .prev = &block->link, + }; +} + +/** + * \brief Returns a cursor set to before an instruction. + * + * \param[in] instr The instruction. + * \return The cursor. + */ +static inline rogue_cursor rogue_cursor_before_instr(rogue_instr *instr) +{ + return (rogue_cursor){ + .block = false, + .prev = instr->link.prev, + .first = (instr->link.prev == &instr->block->instrs), + }; +} + +/** + * \brief Returns a cursor set to after an instruction. + * + * \param[in] instr The instruction. + * \return The cursor. + */ +static inline rogue_cursor rogue_cursor_after_instr(rogue_instr *instr) +{ + return (rogue_cursor){ + .block = false, + .prev = &instr->link, + }; +} + +/** + * \brief Allocates and initializes a new rogue_block object. + * + * \param[in] shader The shader which will contain the block. + * \param[in] label The (optional) block label. + * \return The new block. + */ +rogue_block *rogue_block_create(rogue_shader *shader, const char *label); + +/** + * \brief Returns the block currently being pointed to by the cursor. + * + * If the cursor is currently pointing to a block, this function will + * directly return said block. If it is pointing to an instruction, it + * will return the block that said instruction is a part of. + * + * \param[in] cursor A cursor. + * \return The the block being pointed to. + */ +static inline rogue_block *rogue_cursor_block(rogue_cursor cursor) +{ + rogue_block *block = NULL; + + if (cursor.block) { + assert(!cursor.first && "Cursor is not pointing at a block."); + block = list_entry(cursor.prev, rogue_block, link); + } else { + block = cursor.first ? list_entry(cursor.prev, rogue_block, instrs) + : list_entry(cursor.prev, rogue_instr, link)->block; + } + + return block; +} + +/** + * \brief Inserts a basic block at the specified cursor position. + * + * \param[in] block The basic block to insert. + * \param[in] cursor The cursor. + */ +static inline void rogue_block_insert(rogue_block *block, rogue_cursor cursor) +{ + struct list_head *list = cursor.prev; + + /* If the cursor is pointing at an instruction, the block + * is always going to be inserted *after* the block + * that the instruction is in. + */ + if (!cursor.block) + list = &rogue_cursor_block(cursor)->link; + + list_add(&block->link, list); +} + +void rogue_link_instr_write(rogue_instr *instr); + +void rogue_link_instr_use(rogue_instr *instr); + +void rogue_unlink_instr_write(rogue_instr *instr); + +void rogue_unlink_instr_use(rogue_instr *instr); + +/** + * \brief Inserts an instruction at the specified cursor position. + * + * \param[in] instr The instruction to insert. + * \param[in] cursor The cursor. + */ +static inline void rogue_instr_insert(rogue_instr *instr, rogue_cursor cursor) +{ + struct list_head *list = cursor.prev; + + /* If the cursor is pointing at block, the instruction + * is always going to be inserted at the end of any other + * instructions in the block. + */ + if (cursor.block) + list = rogue_cursor_block(cursor)->instrs.prev; + + list_add(&instr->link, list); + + rogue_link_instr_write(instr); + rogue_link_instr_use(instr); +} + +static inline void rogue_instr_delete(rogue_instr *instr) +{ + rogue_unlink_instr_use(instr); + rogue_unlink_instr_write(instr); + + /* TODO NEXT: If src/dst regs have no uses/writes, + * get rid of them? */ + + list_del(&instr->link); + + ralloc_free(instr); +} + +static inline void +rogue_link_drc_trxn(rogue_shader *shader, rogue_instr *instr, rogue_drc *drc) +{ + unsigned index = drc->index; + assert(index < ROGUE_DRCS); + + drc->trxn.acquire = instr; + list_addtail(&drc->trxn.link, &shader->drc_trxns[index]); +} + +static inline void +rogue_unlink_drc_trxn(rogue_shader *shader, rogue_instr *instr, rogue_drc *drc) +{ + ASSERTED unsigned index = drc->index; + assert(index < ROGUE_DRCS); + assert(drc->trxn.acquire == instr); + + if (drc->trxn.release) + rogue_instr_delete(drc->trxn.release); + + list_del(&drc->trxn.link); +} + +static inline void rogue_link_imm_use(rogue_shader *shader, + rogue_instr *instr, + unsigned src_index, + rogue_imm *imm) +{ + rogue_imm_use *imm_use = &imm->use; + + imm_use->instr = instr; + imm_use->src_index = src_index; + imm_use->imm = &imm->imm; + + list_addtail(&imm_use->link, &shader->imm_uses); +} + +static inline void rogue_unlink_imm_use(rogue_instr *instr, + rogue_imm_use *imm_use) +{ + assert(imm_use->instr == instr); + list_del(&imm_use->link); +} + +static inline void rogue_link_instr_write_reg(rogue_instr *instr, + rogue_reg_write *write, + rogue_reg *reg, + unsigned dst_index) +{ + write->instr = instr; + write->dst_index = dst_index; + list_addtail(&write->link, ®->writes); +} + +static inline void rogue_unlink_instr_write_reg(rogue_instr *instr, + rogue_reg_write *write) +{ + assert(write->instr == instr); + write->instr = NULL; + list_del(&write->link); +} + +static inline void +rogue_link_instr_write_regarray(rogue_instr *instr, + struct util_dynarray **writearray, + rogue_regarray *regarray, + unsigned dst_index) +{ + assert(!*writearray); + *writearray = rzalloc_size(instr, sizeof(**writearray)); + util_dynarray_init(*writearray, instr); + ASSERTED void *mem = + util_dynarray_resize(*writearray, rogue_reg_write, regarray->size); + assert(mem); + + unsigned u = 0; + util_dynarray_foreach (*writearray, rogue_reg_write, write) + rogue_link_instr_write_reg(instr, write, regarray->regs[u++], dst_index); +} + +static inline void +rogue_unlink_instr_write_regarray(rogue_instr *instr, + struct util_dynarray **writearray) +{ + util_dynarray_foreach (*writearray, rogue_reg_write, write) + rogue_unlink_instr_write_reg(instr, write); + + ralloc_free(*writearray); + *writearray = NULL; +} + +static inline void rogue_link_instr_use_reg(rogue_instr *instr, + rogue_reg_use *use, + rogue_reg *reg, + unsigned src_index) +{ + use->instr = instr; + use->src_index = src_index; + list_addtail(&use->link, ®->uses); +} + +static inline void rogue_unlink_instr_use_reg(rogue_instr *instr, + rogue_reg_use *use) +{ + assert(use->instr == instr); + use->instr = NULL; + list_del(&use->link); +} + +static inline void +rogue_link_instr_use_regarray(rogue_instr *instr, + struct util_dynarray **usearray, + rogue_regarray *regarray, + unsigned src_index) +{ + assert(!*usearray); + *usearray = rzalloc_size(instr, sizeof(**usearray)); + util_dynarray_init(*usearray, instr); + ASSERTED void *mem = + util_dynarray_resize(*usearray, rogue_reg_use, regarray->size); + assert(mem); + + unsigned u = 0; + util_dynarray_foreach (*usearray, rogue_reg_use, use) + rogue_link_instr_use_reg(instr, use, regarray->regs[u++], src_index); +} + +static inline void +rogue_unlink_instr_use_regarray(rogue_instr *instr, + struct util_dynarray **usearray) +{ + util_dynarray_foreach (*usearray, rogue_reg_use, use) + rogue_unlink_instr_use_reg(instr, use); + + ralloc_free(*usearray); + *usearray = NULL; +} + +static inline void rogue_link_instr_use_block(rogue_instr *instr, + rogue_block_use *block_use, + rogue_block *target_block) +{ + assert(!block_use->instr); + block_use->instr = instr; + list_addtail(&block_use->link, &target_block->uses); +} + +static inline void rogue_unlink_instr_use_block(rogue_instr *instr, + rogue_block_use *block_use) +{ + assert(block_use->instr == instr); + list_del(&block_use->link); +} + +static inline bool rogue_dst_reg_replace(rogue_reg_write *write, + rogue_reg *new_reg) +{ + unsigned dst_index = write->dst_index; + rogue_instr *instr = write->instr; + rogue_ref *ref; + + switch (instr->type) { + case ROGUE_INSTR_TYPE_ALU: + ref = &rogue_instr_as_alu(instr)->dst.ref; + break; + + case ROGUE_INSTR_TYPE_BACKEND: + ref = &rogue_instr_as_backend(instr)->dst[dst_index].ref; + break; + + case ROGUE_INSTR_TYPE_CTRL: + ref = &rogue_instr_as_ctrl(instr)->dst[dst_index].ref; + break; + + default: + unreachable("Unsupported instruction type."); + return false; + } + + /* We don't want to be modifying regarrays. */ + assert(rogue_ref_is_reg(ref)); + + if (ref->reg == new_reg) + return false; + + rogue_unlink_instr_write_reg(instr, write); + *ref = rogue_ref_reg(new_reg); + rogue_link_instr_write_reg(instr, write, new_reg, dst_index); + + return true; +} + +static inline bool rogue_src_reg_replace(rogue_reg_use *use, rogue_reg *new_reg) +{ + unsigned src_index = use->src_index; + rogue_instr *instr = use->instr; + rogue_ref *ref; + + switch (instr->type) { + case ROGUE_INSTR_TYPE_ALU: + ref = &rogue_instr_as_alu(instr)->src[src_index].ref; + break; + + case ROGUE_INSTR_TYPE_BACKEND: + ref = &rogue_instr_as_backend(instr)->src[src_index].ref; + break; + + case ROGUE_INSTR_TYPE_CTRL: + ref = &rogue_instr_as_ctrl(instr)->src[src_index].ref; + break; + + default: + unreachable("Unsupported instruction type."); + return false; + } + + /* We don't want to be modifying regarrays. */ + assert(rogue_ref_is_reg(ref)); + + if (ref->reg == new_reg) + return false; + + rogue_unlink_instr_use_reg(instr, use); + *ref = rogue_ref_reg(new_reg); + rogue_link_instr_use_reg(instr, use, new_reg, src_index); + + return true; +} + +static inline bool rogue_reg_replace(rogue_reg *old_reg, rogue_reg *new_reg) +{ + bool replaced = true; + + rogue_foreach_reg_write_safe (write, old_reg) { + replaced &= rogue_dst_reg_replace(write, new_reg); + } + + rogue_foreach_reg_use_safe (use, old_reg) { + replaced &= rogue_src_reg_replace(use, new_reg); + } + + if (replaced) + rogue_reg_delete(old_reg); + + return replaced; +} + +static inline bool rogue_src_imm_replace(rogue_imm_use *imm_use, + rogue_reg *new_reg) +{ + unsigned src_index = imm_use->src_index; + rogue_instr *instr = imm_use->instr; + rogue_ref *ref; + rogue_reg_use *reg_use; + + switch (instr->type) { + case ROGUE_INSTR_TYPE_ALU: + ref = &rogue_instr_as_alu(instr)->src[src_index].ref; + reg_use = &rogue_instr_as_alu(instr)->src_use[src_index].reg; + break; + + case ROGUE_INSTR_TYPE_BACKEND: + ref = &rogue_instr_as_backend(instr)->src[src_index].ref; + reg_use = &rogue_instr_as_backend(instr)->src_use[src_index].reg; + break; + + case ROGUE_INSTR_TYPE_CTRL: + ref = &rogue_instr_as_ctrl(instr)->src[src_index].ref; + reg_use = &rogue_instr_as_ctrl(instr)->src_use[src_index].reg; + break; + + default: + unreachable("Unsupported instruction type."); + return false; + } + + assert(rogue_ref_is_imm(ref)); + + rogue_unlink_imm_use(instr, imm_use); + *ref = rogue_ref_reg(new_reg); + rogue_link_instr_use_reg(instr, reg_use, new_reg, src_index); + + return true; +} + +static inline bool rogue_instr_is_nop_end(const rogue_instr *instr) +{ + if (instr->type != ROGUE_INSTR_TYPE_CTRL) + return false; + + const rogue_ctrl_instr *ctrl = rogue_instr_as_ctrl(instr); + + if (ctrl->op != ROGUE_CTRL_OP_NOP) + return false; + + return rogue_ctrl_op_mod_is_set(ctrl, ROGUE_CTRL_OP_MOD_END); +} + +static inline unsigned rogue_instr_supported_phases(const rogue_instr *instr) +{ + uint64_t supported_phases = 0; + + switch (instr->type) { + case ROGUE_INSTR_TYPE_ALU: { + rogue_alu_instr *alu = rogue_instr_as_alu(instr); + if (alu->op >= ROGUE_ALU_OP_PSEUDO) + return 0; + + const rogue_alu_op_info *info = &rogue_alu_op_infos[alu->op]; + supported_phases = info->supported_phases; + break; + } + + case ROGUE_INSTR_TYPE_BACKEND: { + rogue_backend_instr *backend = rogue_instr_as_backend(instr); + if (backend->op >= ROGUE_BACKEND_OP_PSEUDO) + return 0; + + supported_phases = BITFIELD_BIT(ROGUE_INSTR_PHASE_BACKEND); + break; + } + + case ROGUE_INSTR_TYPE_CTRL: { + /* Control instructions can't be co-issued; just make sure this isn't a + * pseudo-instruction. */ + rogue_ctrl_instr *ctrl = rogue_instr_as_ctrl(instr); + if (ctrl->op >= ROGUE_CTRL_OP_PSEUDO) + return 0; + + supported_phases = BITFIELD_BIT(ROGUE_INSTR_PHASE_CTRL); + break; + } + + default: + unreachable("Invalid instruction type."); + } + + return supported_phases; +} + +/* Loop through and find first unused phase that's supported, then return its + * enum. */ +static inline enum rogue_instr_phase +rogue_get_supported_phase(uint64_t supported_phases, uint64_t occupied_phases) +{ + rogue_foreach_phase_in_set (p, supported_phases) { + if (!(BITFIELD_BIT(p) & occupied_phases)) + return p; + } + + return ROGUE_INSTR_PHASE_INVALID; +} + +static inline bool rogue_can_replace_reg_use(rogue_reg_use *use, + const rogue_reg *new_reg) +{ + bool can_replace = false; + const rogue_reg_info *reg_info = &rogue_reg_infos[new_reg->class]; + const rogue_instr *instr = use->instr; + + rogue_foreach_phase_in_set (p, rogue_instr_supported_phases(instr)) { + enum rogue_io io_src = rogue_instr_src_io_src(instr, p, use->src_index); + can_replace &= rogue_io_supported(io_src, reg_info->supported_io_srcs); + } + + return can_replace; +} + +#define ROGUE_NO_CONST_REG ~0 + +unsigned rogue_constreg_lookup(rogue_imm_t imm); + +/* Printing */ + +void rogue_print_color(bool print_color); + +/** + * \brief Prints a shader's Rogue IR/assembly. + * + * \param[in] fp The file pointer to use for printing. + * \param[in] shader The shader to print. + */ +void rogue_print_shader(FILE *fp, const rogue_shader *shader); + +/** + * \brief Prints an instruction. + * + * \param[in] fp The file pointer to use for printing. + * \param[in] instr The instruction to print. + */ +void rogue_print_instr(FILE *fp, const rogue_instr *instr); + +void rogue_print_reg_writes(FILE *fp, const rogue_shader *shader); + +void rogue_print_reg_uses(FILE *fp, const rogue_shader *shader); + +void rogue_print_block_uses(FILE *fp, const rogue_shader *shader); + +void rogue_print_drc_trxns(FILE *fp, const rogue_shader *shader); + +/* Validation */ +bool rogue_validate_shader(rogue_shader *shader, const char *when); + +/* Debug. */ +enum rogue_debug { + ROGUE_DEBUG_NIR = BITFIELD_BIT(0), + ROGUE_DEBUG_NIR_PASSES = BITFIELD_BIT(1), + ROGUE_DEBUG_IR = BITFIELD_BIT(2), + ROGUE_DEBUG_IR_PASSES = BITFIELD_BIT(3), + ROGUE_DEBUG_IR_DETAILS = BITFIELD_BIT(4), + ROGUE_DEBUG_VLD_SKIP = BITFIELD_BIT(5), + ROGUE_DEBUG_VLD_NONFATAL = BITFIELD_BIT(6), +}; + +extern unsigned long rogue_debug; + +#define ROGUE_DEBUG(flag) unlikely(!!(rogue_debug & (ROGUE_DEBUG_##flag))) + +extern bool rogue_color; + +void rogue_debug_init(void); + +static inline void +rogue_print_pass_debug(rogue_shader *shader, const char *pass, FILE *fp) +{ + fprintf(fp, "%s\n", pass); + rogue_print_shader(fp, shader); + if (ROGUE_DEBUG(IR_DETAILS)) { + rogue_print_reg_writes(fp, shader); + rogue_print_reg_uses(fp, shader); + rogue_print_block_uses(fp, shader); + rogue_print_drc_trxns(fp, shader); + } +} + +/* Passes */ +#define ROGUE_PASS(progress, shader, pass, ...) \ + do { \ + if (pass((shader), ##__VA_ARGS__)) { \ + if (ROGUE_DEBUG(IR_PASSES)) \ + rogue_print_pass_debug(shader, #pass, stdout); \ + rogue_validate_shader(shader, #pass); \ + progress = true; \ + } \ + } while (0) + +#define ROGUE_PASS_V(shader, pass, ...) \ + do { \ + if (pass((shader), ##__VA_ARGS__)) { \ + if (ROGUE_DEBUG(IR_PASSES)) \ + rogue_print_pass_debug(shader, #pass, stdout); \ + rogue_validate_shader(shader, #pass); \ + } \ + } while (0) + +bool rogue_constreg(rogue_shader *shader); + +bool rogue_copy_prop(rogue_shader *shader); + +bool rogue_dce(rogue_shader *shader); + +bool rogue_lower_pseudo_ops(rogue_shader *shader); + +bool rogue_regalloc(rogue_shader *shader); + +bool rogue_schedule_instr_groups(rogue_shader *shader, bool multi_instr_groups); + +bool rogue_schedule_uvsw(rogue_shader *shader, bool latency_hiding); + +bool rogue_schedule_wdf(rogue_shader *shader, bool latency_hiding); + +bool rogue_trim(rogue_shader *shader); + +void rogue_shader_passes(rogue_shader *shader); + +struct pvr_device_info; + +/** + * \brief Compiler context. + */ +typedef struct rogue_compiler { + const struct pvr_device_info *dev_info; +} rogue_compiler; + +rogue_compiler *rogue_compiler_create(const struct pvr_device_info *dev_info); + +/* Max number of I/O varying variables. + * Fragment shader: MAX_VARYING + 1 (W coefficient). + * Vertex shader: MAX_VARYING + 1 (position slot). + */ +#define ROGUE_MAX_IO_VARYING_VARS (MAX_VARYING + 1) + +/* VERT_ATTRIB_GENERIC0-15 */ +#define ROGUE_MAX_IO_ATTRIB_VARS 16 + +/* Max buffers entries that can be used. */ +/* TODO: Currently UBOs are the only supported buffers. */ +#define ROGUE_MAX_BUFFERS 24 + +/** + * \brief UBO data. + */ +typedef struct rogue_ubo_data { + unsigned num_ubo_entries; + unsigned desc_set[ROGUE_MAX_BUFFERS]; + unsigned binding[ROGUE_MAX_BUFFERS]; + unsigned dest[ROGUE_MAX_BUFFERS]; + unsigned size[ROGUE_MAX_BUFFERS]; +} rogue_ubo_data; + +/** + * \brief Compile time constants that need uploading. + */ +typedef struct rogue_compile_time_consts_data { + /* TODO: Output these from the compiler. */ + /* TODO: Add the other types. */ + struct { + unsigned num; + unsigned dest; + /* TODO: This should probably be bigger. Big enough to account for all + * available writable special constant regs. + */ + uint32_t value[ROGUE_MAX_BUFFERS]; + } static_consts; +} rogue_compile_time_consts_data; + +/** + * \brief Per-stage common build data. + */ +typedef struct rogue_common_build_data { + unsigned temps; + unsigned internals; + unsigned coeffs; + unsigned shareds; + + rogue_ubo_data ubo_data; + rogue_compile_time_consts_data compile_time_consts_data; +} rogue_common_build_data; + +/** + * \brief Arguments for the FPU iterator(s) + * (produces varyings for the fragment shader). + */ +typedef struct rogue_iterator_args { + uint32_t num_fpu_iterators; + uint32_t fpu_iterators[ROGUE_MAX_IO_VARYING_VARS]; + uint32_t destination[ROGUE_MAX_IO_VARYING_VARS]; + unsigned base[ROGUE_MAX_IO_VARYING_VARS]; + unsigned components[ROGUE_MAX_IO_VARYING_VARS]; +} rogue_iterator_args; + +/** + * \brief Vertex input register allocations. + */ +typedef struct rogue_vertex_inputs { + unsigned num_input_vars; + unsigned base[ROGUE_MAX_IO_ATTRIB_VARS]; + unsigned components[ROGUE_MAX_IO_ATTRIB_VARS]; +} rogue_vertex_inputs; + +/** + * \brief Vertex output allocations. + */ +typedef struct rogue_vertex_outputs { + unsigned num_output_vars; + unsigned base[ROGUE_MAX_IO_VARYING_VARS]; + unsigned components[ROGUE_MAX_IO_VARYING_VARS]; +} rogue_vertex_outputs; enum rogue_msaa_mode { ROGUE_MSAA_MODE_UNDEF = 0, /* explicitly treat 0 as undefined */ @@ -49,27 +2491,76 @@ enum rogue_msaa_mode { }; /** - * \brief Shader binary. + * \brief Stage-specific build data. */ -struct rogue_shader_binary { - size_t size; - uint8_t data[]; -}; +typedef struct rogue_build_data { + struct rogue_fs_build_data { + rogue_iterator_args iterator_args; + enum rogue_msaa_mode msaa_mode; + bool phas; /* Indicates the presence of PHAS instruction. */ + } fs; + struct rogue_vs_build_data { + rogue_vertex_inputs inputs; + unsigned num_vertex_input_regs; /* Final number of inputs. */ -PUBLIC -nir_shader *rogue_spirv_to_nir(struct rogue_build_ctx *ctx, + rogue_vertex_outputs outputs; + unsigned num_vertex_outputs; /* Final number of outputs. */ + + unsigned num_varyings; /* Final number of varyings. */ + } vs; +} rogue_build_data; + +/** + * \brief Shared multi-stage build context. + */ +typedef struct rogue_build_ctx { + rogue_compiler *compiler; + + /* Shaders in various stages of compilations. */ + nir_shader *nir[MESA_SHADER_FRAGMENT + 1]; + rogue_shader *rogue[MESA_SHADER_FRAGMENT + 1]; + struct util_dynarray binary[MESA_SHADER_FRAGMENT + 1]; + + rogue_common_build_data common_data[MESA_SHADER_FRAGMENT + 1]; + rogue_build_data stage_data; +} rogue_build_ctx; + +rogue_build_ctx *rogue_build_context_create(rogue_compiler *compiler); + +void rogue_collect_io_data(rogue_build_ctx *ctx, nir_shader *nir); + +unsigned rogue_coeff_index_fs(rogue_iterator_args *args, + gl_varying_slot location, + unsigned component); + +unsigned rogue_output_index_vs(rogue_vertex_outputs *outputs, + gl_varying_slot location, + unsigned component); + +unsigned rogue_ubo_reg(rogue_ubo_data *ubo_data, + unsigned desc_set, + unsigned binding, + unsigned offset_bytes); + +nir_shader *rogue_spirv_to_nir(rogue_build_ctx *ctx, gl_shader_stage stage, const char *entry, - size_t spirv_size, + unsigned spirv_size, const uint32_t *spirv_data, unsigned num_spec, struct nir_spirv_specialization *spec); -PUBLIC -struct rogue_shader_binary *rogue_to_binary(struct rogue_build_ctx *ctx, - const struct rogue_shader *shader); +/* Custom NIR passes. */ +void rogue_nir_pfo(nir_shader *shader); + +bool rogue_nir_lower_io(nir_shader *shader, void *layout); + +rogue_shader *rogue_nir_to_rogue(rogue_build_ctx *ctx, const nir_shader *nir); + +/* Encode/decode */ + +void rogue_encode_shader(rogue_build_ctx *ctx, + rogue_shader *shader, + struct util_dynarray *binary); -PUBLIC -struct rogue_shader *rogue_nir_to_rogue(struct rogue_build_ctx *ctx, - const nir_shader *nir); #endif /* ROGUE_H */ diff --git a/src/imagination/rogue/rogue_constreg.h b/src/imagination/rogue/rogue_alu_instrs.def similarity index 54% rename from src/imagination/rogue/rogue_constreg.h rename to src/imagination/rogue/rogue_alu_instrs.def index 1d67857a955..371017c9e52 100644 --- a/src/imagination/rogue/rogue_constreg.h +++ b/src/imagination/rogue/rogue_alu_instrs.def @@ -21,31 +21,47 @@ * SOFTWARE. */ -#ifndef ROGUE_CONSTREGS_H -#define ROGUE_CONSTREGS_H - -#include -#include - -#include "util/macros.h" -#include "util/u_math.h" - -#define ROGUE_NO_CONST_REG SIZE_MAX - -PUBLIC -size_t rogue_constreg_lookup(uint32_t value); - /** - * \brief Determines whether a given floating point value exists in a constant - * register. + * \file rogue_alu_instrs.def * - * \param[in] value The value required. - * \return The index of the constant register containing the value, or - * ROGUE_NO_CONST_REG if the value is not found. + * \brief Contains macros defining ALU instructions. */ -static inline size_t rogue_constreg_lookup_float(float value) -{ - return rogue_constreg_lookup(fui(value)); -} -#endif /* ROGUE_CONSTREGS_H */ +/* + * ROGUE_BUILDER_DEFINE_ALUs + * s: Number of sources. + */ + +#ifndef ROGUE_BUILDER_DEFINE_ALU1 +#define ROGUE_BUILDER_DEFINE_ALU1(...) +#endif /* ROGUE_BUILDER_DEFINE_ALU1 */ + +#ifndef ROGUE_BUILDER_DEFINE_ALU2 +#define ROGUE_BUILDER_DEFINE_ALU2(...) +#endif /* ROGUE_BUILDER_DEFINE_ALU2 */ + +#ifndef ROGUE_BUILDER_DEFINE_ALU3 +#define ROGUE_BUILDER_DEFINE_ALU3(...) +#endif /* ROGUE_BUILDER_DEFINE_ALU3 */ + +ROGUE_BUILDER_DEFINE_ALU1(MOV) +ROGUE_BUILDER_DEFINE_ALU1(MBYP) + +ROGUE_BUILDER_DEFINE_ALU1(FABS) +ROGUE_BUILDER_DEFINE_ALU1(FNEG) +ROGUE_BUILDER_DEFINE_ALU1(FNABS) + +ROGUE_BUILDER_DEFINE_ALU1(PCK_U8888) + +ROGUE_BUILDER_DEFINE_ALU2(FADD) +ROGUE_BUILDER_DEFINE_ALU2(FMUL) + +ROGUE_BUILDER_DEFINE_ALU2(FMAX) +ROGUE_BUILDER_DEFINE_ALU2(FMIN) + +ROGUE_BUILDER_DEFINE_ALU3(SEL) +ROGUE_BUILDER_DEFINE_ALU3(FMAD) + +#undef ROGUE_BUILDER_DEFINE_ALU3 +#undef ROGUE_BUILDER_DEFINE_ALU2 +#undef ROGUE_BUILDER_DEFINE_ALU1 diff --git a/src/imagination/rogue/rogue_encoders.h b/src/imagination/rogue/rogue_backend_instrs.def similarity index 54% rename from src/imagination/rogue/rogue_encoders.h rename to src/imagination/rogue/rogue_backend_instrs.def index ea5c94d6ec3..adb6dfe64bd 100644 --- a/src/imagination/rogue/rogue_encoders.h +++ b/src/imagination/rogue/rogue_backend_instrs.def @@ -21,33 +21,39 @@ * SOFTWARE. */ -#ifndef ROGUE_ENCODERS_H -#define ROGUE_ENCODERS_H - -#include -#include -#include - -#include "util/macros.h" - -/* Returns false if input was invalid. */ -typedef bool (*field_encoder_t)(uint64_t *value, size_t inputs, ...); - -bool rogue_encoder_pass(uint64_t *value, size_t inputs, ...); -bool rogue_encoder_drc(uint64_t *value, size_t inputs, ...); -bool rogue_encoder_imm(uint64_t *value, size_t inputs, ...); -bool rogue_encoder_ls_1_16(uint64_t *value, size_t inputs, ...); - /** - * \brief Macro to declare the rogue_encoder_reg variants. + * \file rogue_backend_instrs.def + * + * \brief Contains macros defining backend instructions. */ -#define ROGUE_ENCODER_REG_VARIANT(bank_bits, num_bits) \ - bool rogue_encoder_reg_##bank_bits##_##num_bits(uint64_t *value, \ - size_t inputs, \ - ...); -ROGUE_ENCODER_REG_VARIANT(2, 8) -ROGUE_ENCODER_REG_VARIANT(3, 8) -ROGUE_ENCODER_REG_VARIANT(3, 11) -#undef ROGUE_ENCODER_REG_VARIANT -#endif /* ROGUE_ENCODERS_H */ +/* + * ROGUE_BUILDER_DEFINE_BACKENDds + * d: Number of destinations. + * s: Number of sources. + */ + +#ifndef ROGUE_BUILDER_DEFINE_BACKEND00 +#define ROGUE_BUILDER_DEFINE_BACKEND00(...) +#endif /* ROGUE_BUILDER_DEFINE_BACKEND00 */ + +#ifndef ROGUE_BUILDER_DEFINE_BACKEND11 +#define ROGUE_BUILDER_DEFINE_BACKEND11(...) +#endif /* ROGUE_BUILDER_DEFINE_BACKEND11 */ + +#ifndef ROGUE_BUILDER_DEFINE_BACKEND14 +#define ROGUE_BUILDER_DEFINE_BACKEND14(...) +#endif /* ROGUE_BUILDER_DEFINE_BACKEND14 */ + +ROGUE_BUILDER_DEFINE_BACKEND00(UVSW_EMIT) +ROGUE_BUILDER_DEFINE_BACKEND00(UVSW_ENDTASK) +ROGUE_BUILDER_DEFINE_BACKEND00(UVSW_EMITTHENENDTASK) + +ROGUE_BUILDER_DEFINE_BACKEND11(UVSW_WRITE) +ROGUE_BUILDER_DEFINE_BACKEND11(UVSW_WRITETHENEMITTHENENDTASK) + +ROGUE_BUILDER_DEFINE_BACKEND14(FITRP_PIXEL) + +#undef ROGUE_BUILDER_DEFINE_BACKEND14 +#undef ROGUE_BUILDER_DEFINE_BACKEND11 +#undef ROGUE_BUILDER_DEFINE_BACKEND00 diff --git a/src/imagination/rogue/rogue_operand.c b/src/imagination/rogue/rogue_bitwise_instrs.def similarity index 73% rename from src/imagination/rogue/rogue_operand.c rename to src/imagination/rogue/rogue_bitwise_instrs.def index a0e86f9f8d6..e667a82ad6d 100644 --- a/src/imagination/rogue/rogue_operand.c +++ b/src/imagination/rogue/rogue_bitwise_instrs.def @@ -21,10 +21,23 @@ * SOFTWARE. */ -#include "rogue_operand.h" - /** - * \file rogue_operand.c + * \file rogue_bitwise_instrs.def * - * \brief Contains functions to manipulate Rogue instruction operands. + * \brief Contains macros defining bitwise instructions. */ + +/* + * ROGUE_BUILDER_DEFINE_BITWISEds + * d: Number of destinations. + * s: Number of sources. + */ + +#ifndef ROGUE_BUILDER_DEFINE_BITWISE22 +#define ROGUE_BUILDER_DEFINE_BITWISE22(...) +#endif /* ROGUE_BUILDER_DEFINE_BITWISE22 */ + +ROGUE_BUILDER_DEFINE_BITWISE22(MOV2) +ROGUE_BUILDER_DEFINE_BITWISE22(BYP) + +#undef ROGUE_BUILDER_DEFINE_BITWISE22 diff --git a/src/imagination/rogue/rogue_build_data.c b/src/imagination/rogue/rogue_build_data.c index 2eebf82f455..ae403472ae4 100644 --- a/src/imagination/rogue/rogue_build_data.c +++ b/src/imagination/rogue/rogue_build_data.c @@ -29,9 +29,7 @@ #include "compiler/shader_enums.h" #include "nir/nir.h" -#include "rogue_build_data.h" -#include "rogue_nir_helpers.h" -#include "rogue_operand.h" +#include "rogue.h" #include "util/macros.h" #define __pvr_address_type uint64_t @@ -44,6 +42,14 @@ #undef __pvr_get_address #undef __pvr_address_type +/** + * \file rogue_build_data.c + * + * \brief Contains functions to collect build data for the driver. + */ + +/* N.B. This will all be hoisted into the driver. */ + /** * \brief Allocates the coefficient registers that will contain the iterator * data for the fragment shader input varyings. @@ -51,11 +57,11 @@ * \param[in] args The iterator argument data. * \return The total number of coefficient registers required by the iterators. */ -static size_t alloc_iterator_regs(struct rogue_iterator_args *args) +static unsigned alloc_iterator_regs(struct rogue_iterator_args *args) { - size_t coeffs = 0; + unsigned coeffs = 0; - for (size_t u = 0; u < args->num_fpu_iterators; ++u) { + for (unsigned u = 0; u < args->num_fpu_iterators; ++u) { /* Ensure there aren't any gaps. */ assert(args->base[u] == ~0); @@ -77,10 +83,10 @@ static size_t alloc_iterator_regs(struct rogue_iterator_args *args) * \param[in] components The number of components in the varying. */ static void reserve_iterator(struct rogue_iterator_args *args, - size_t i, + unsigned i, enum glsl_interp_mode type, bool f16, - size_t components) + unsigned components) { struct ROGUE_PDSINST_DOUT_FIELDS_DOUTI_SRC data = { 0 }; @@ -126,6 +132,18 @@ static void reserve_iterator(struct rogue_iterator_args *args, ++args->num_fpu_iterators; } +static inline unsigned nir_count_variables_with_modes(const nir_shader *nir, + nir_variable_mode mode) +{ + unsigned count = 0; + + nir_foreach_variable_with_modes (var, nir, mode) { + ++count; + } + + return count; +} + /** * \brief Collects the fragment shader I/O data to feed-back to the driver. * @@ -134,13 +152,12 @@ static void reserve_iterator(struct rogue_iterator_args *args, * \param[in] common_data Common build data. * \param[in] fs_data Fragment-specific build data. * \param[in] nir NIR fragment shader. - * \return true if successful, otherwise false. */ -static bool collect_io_data_fs(struct rogue_common_build_data *common_data, +static void collect_io_data_fs(struct rogue_common_build_data *common_data, struct rogue_fs_build_data *fs_data, nir_shader *nir) { - size_t num_inputs = nir_count_variables_with_modes(nir, nir_var_shader_in); + unsigned num_inputs = nir_count_variables_with_modes(nir, nir_var_shader_in); assert(num_inputs < (ARRAY_SIZE(fs_data->iterator_args.fpu_iterators) - 1)); /* Process inputs (if present). */ @@ -155,8 +172,8 @@ static bool collect_io_data_fs(struct rogue_common_build_data *common_data, 1); nir_foreach_shader_in_variable (var, nir) { - size_t i = (var->data.location - VARYING_SLOT_VAR0) + 1; - size_t components = glsl_get_components(var->type); + unsigned i = (var->data.location - VARYING_SLOT_VAR0) + 1; + unsigned components = glsl_get_components(var->type); enum glsl_interp_mode interp = var->data.interpolation; bool f16 = glsl_type_is_16bit(var->type); @@ -173,12 +190,10 @@ static bool collect_io_data_fs(struct rogue_common_build_data *common_data, common_data->coeffs = alloc_iterator_regs(&fs_data->iterator_args); assert(common_data->coeffs); - assert(common_data->coeffs < ROGUE_MAX_REG_COEFF); + assert(common_data->coeffs <= rogue_reg_infos[ROGUE_REG_CLASS_COEFF].num); } /* TODO: Process outputs. */ - - return true; } /** @@ -187,11 +202,11 @@ static bool collect_io_data_fs(struct rogue_common_build_data *common_data, * \param[in] inputs The vertex shader input data. * \return The total number of vertex input registers required. */ -static size_t alloc_vs_inputs(struct rogue_vertex_inputs *inputs) +static unsigned alloc_vs_inputs(struct rogue_vertex_inputs *inputs) { - size_t vs_inputs = 0; + unsigned vs_inputs = 0; - for (size_t u = 0; u < inputs->num_input_vars; ++u) { + for (unsigned u = 0; u < inputs->num_input_vars; ++u) { /* Ensure there aren't any gaps. */ assert(inputs->base[u] == ~0); @@ -208,11 +223,11 @@ static size_t alloc_vs_inputs(struct rogue_vertex_inputs *inputs) * \param[in] outputs The vertex shader output data. * \return The total number of vertex outputs required. */ -static size_t alloc_vs_outputs(struct rogue_vertex_outputs *outputs) +static unsigned alloc_vs_outputs(struct rogue_vertex_outputs *outputs) { - size_t vs_outputs = 0; + unsigned vs_outputs = 0; - for (size_t u = 0; u < outputs->num_output_vars; ++u) { + for (unsigned u = 0; u < outputs->num_output_vars; ++u) { /* Ensure there aren't any gaps. */ assert(outputs->base[u] == ~0); @@ -229,12 +244,12 @@ static size_t alloc_vs_outputs(struct rogue_vertex_outputs *outputs) * \param[in] outputs The vertex shader output data. * \return The number of varyings used. */ -static size_t count_vs_varyings(struct rogue_vertex_outputs *outputs) +static unsigned count_vs_varyings(struct rogue_vertex_outputs *outputs) { - size_t varyings = 0; + unsigned varyings = 0; /* Skip the position. */ - for (size_t u = 1; u < outputs->num_output_vars; ++u) + for (unsigned u = 1; u < outputs->num_output_vars; ++u) varyings += outputs->components[u]; return varyings; @@ -248,8 +263,8 @@ static size_t count_vs_varyings(struct rogue_vertex_outputs *outputs) * \param[in] components The number of components in the input. */ static void reserve_vs_input(struct rogue_vertex_inputs *inputs, - size_t i, - size_t components) + unsigned i, + unsigned components) { assert(components >= 1 && components <= 4); @@ -268,8 +283,8 @@ static void reserve_vs_input(struct rogue_vertex_inputs *inputs, * \param[in] components The number of components in the output. */ static void reserve_vs_output(struct rogue_vertex_outputs *outputs, - size_t i, - size_t components) + unsigned i, + unsigned components) { assert(components >= 1 && components <= 4); @@ -288,20 +303,19 @@ static void reserve_vs_output(struct rogue_vertex_outputs *outputs, * \param[in] common_data Common build data. * \param[in] vs_data Vertex-specific build data. * \param[in] nir NIR vertex shader. - * \return true if successful, otherwise false. */ -static bool collect_io_data_vs(struct rogue_common_build_data *common_data, +static void collect_io_data_vs(struct rogue_common_build_data *common_data, struct rogue_vs_build_data *vs_data, nir_shader *nir) { ASSERTED bool out_pos_present = false; - ASSERTED size_t num_outputs = + ASSERTED unsigned num_outputs = nir_count_variables_with_modes(nir, nir_var_shader_out); /* Process inputs. */ nir_foreach_shader_in_variable (var, nir) { - size_t components = glsl_get_components(var->type); - size_t i = var->data.location - VERT_ATTRIB_GENERIC0; + unsigned components = glsl_get_components(var->type); + unsigned i = var->data.location - VERT_ATTRIB_GENERIC0; /* Check that inputs are F32. */ /* TODO: Support other types. */ @@ -317,7 +331,8 @@ static bool collect_io_data_vs(struct rogue_common_build_data *common_data, vs_data->num_vertex_input_regs = alloc_vs_inputs(&vs_data->inputs); assert(vs_data->num_vertex_input_regs); - assert(vs_data->num_vertex_input_regs < ROGUE_MAX_REG_VERTEX_IN); + assert(vs_data->num_vertex_input_regs < + rogue_reg_infos[ROGUE_REG_CLASS_VTXIN].num); /* Process outputs. */ @@ -325,7 +340,7 @@ static bool collect_io_data_vs(struct rogue_common_build_data *common_data, assert(num_outputs > 0 && "Invalid number of vertex shader outputs."); nir_foreach_shader_out_variable (var, nir) { - size_t components = glsl_get_components(var->type); + unsigned components = glsl_get_components(var->type); /* Check that outputs are F32. */ /* TODO: Support other types. */ @@ -339,7 +354,7 @@ static bool collect_io_data_vs(struct rogue_common_build_data *common_data, reserve_vs_output(&vs_data->outputs, 0, components); } else if ((var->data.location >= VARYING_SLOT_VAR0) && (var->data.location <= VARYING_SLOT_VAR31)) { - size_t i = (var->data.location - VARYING_SLOT_VAR0) + 1; + unsigned i = (var->data.location - VARYING_SLOT_VAR0) + 1; reserve_vs_output(&vs_data->outputs, i, components); } else { unreachable("Unsupported vertex output type."); @@ -351,11 +366,10 @@ static bool collect_io_data_vs(struct rogue_common_build_data *common_data, vs_data->num_vertex_outputs = alloc_vs_outputs(&vs_data->outputs); assert(vs_data->num_vertex_outputs); - assert(vs_data->num_vertex_outputs < ROGUE_MAX_VERTEX_OUTPUTS); + assert(vs_data->num_vertex_outputs < + rogue_reg_infos[ROGUE_REG_CLASS_VTXOUT].num); vs_data->num_varyings = count_vs_varyings(&vs_data->outputs); - - return true; } /** @@ -364,11 +378,11 @@ static bool collect_io_data_vs(struct rogue_common_build_data *common_data, * \param[in] ubo_data The UBO data. * \return The total number of coefficient registers required by the iterators. */ -static size_t alloc_ubos(struct rogue_ubo_data *ubo_data) +static unsigned alloc_ubos(struct rogue_ubo_data *ubo_data) { - size_t shareds = 0; + unsigned shareds = 0; - for (size_t u = 0; u < ubo_data->num_ubo_entries; ++u) { + for (unsigned u = 0; u < ubo_data->num_ubo_entries; ++u) { /* Ensure there aren't any gaps. */ assert(ubo_data->dest[u] == ~0); @@ -388,11 +402,11 @@ static size_t alloc_ubos(struct rogue_ubo_data *ubo_data) * \param[in] size The size required by the UBO (in dwords). */ static void reserve_ubo(struct rogue_ubo_data *ubo_data, - size_t desc_set, - size_t binding, - size_t size) + unsigned desc_set, + unsigned binding, + unsigned size) { - size_t i = ubo_data->num_ubo_entries; + unsigned i = ubo_data->num_ubo_entries; assert(i < ARRAY_SIZE(ubo_data->desc_set)); ubo_data->desc_set[i] = desc_set; @@ -407,16 +421,15 @@ static void reserve_ubo(struct rogue_ubo_data *ubo_data, * * \param[in] common_data Common build data. * \param[in] nir NIR shader. - * \return true if successful, otherwise false. */ -static bool collect_ubo_data(struct rogue_common_build_data *common_data, +static void collect_ubo_data(struct rogue_common_build_data *common_data, nir_shader *nir) { /* Iterate over each UBO. */ nir_foreach_variable_with_modes (var, nir, nir_var_mem_ubo) { - size_t desc_set = var->data.driver_location; - size_t binding = var->data.binding; - size_t ubo_size_regs = 0; + unsigned desc_set = var->data.driver_location; + unsigned binding = var->data.binding; + unsigned ubo_size_regs = 0; nir_function_impl *entry = nir_shader_get_entrypoint(nir); /* Iterate over each load_ubo that uses this UBO. */ @@ -430,21 +443,20 @@ static bool collect_ubo_data(struct rogue_common_build_data *common_data, continue; assert(nir_src_num_components(intr->src[0]) == 2); - assert(nir_intr_src_is_const(intr, 0)); - size_t load_desc_set = nir_intr_src_comp_const(intr, 0, 0); - size_t load_binding = nir_intr_src_comp_const(intr, 0, 1); + unsigned load_desc_set = nir_src_comp_as_uint(intr->src[0], 0); + unsigned load_binding = nir_src_comp_as_uint(intr->src[0], 1); if (load_desc_set != desc_set || load_binding != binding) continue; - ASSERTED size_t size_bytes = nir_intrinsic_range(intr); + ASSERTED unsigned size_bytes = nir_intrinsic_range(intr); assert(size_bytes == ROGUE_REG_SIZE_BYTES); - size_t offset_bytes = nir_intrinsic_range_base(intr); + unsigned offset_bytes = nir_intrinsic_range_base(intr); assert(!(offset_bytes % ROGUE_REG_SIZE_BYTES)); - size_t offset_regs = offset_bytes / ROGUE_REG_SIZE_BYTES; + unsigned offset_regs = offset_bytes / ROGUE_REG_SIZE_BYTES; /* TODO: Put offsets in a BITSET_DECLARE and check for gaps. */ @@ -460,9 +472,7 @@ static bool collect_ubo_data(struct rogue_common_build_data *common_data, } common_data->shareds = alloc_ubos(&common_data->ubo_data); - assert(common_data->shareds < ROGUE_MAX_REG_SHARED); - - return true; + assert(common_data->shareds < rogue_reg_infos[ROGUE_REG_CLASS_SHARED].num); } /** @@ -475,16 +485,15 @@ static bool collect_ubo_data(struct rogue_common_build_data *common_data, * * \param[in] ctx Shared multi-stage build context. * \param[in] nir NIR shader. - * \return true if successful, otherwise false. */ -bool rogue_collect_io_data(struct rogue_build_ctx *ctx, nir_shader *nir) +PUBLIC +void rogue_collect_io_data(struct rogue_build_ctx *ctx, nir_shader *nir) { gl_shader_stage stage = nir->info.stage; struct rogue_common_build_data *common_data = &ctx->common_data[stage]; /* Collect stage-agnostic data. */ - if (!collect_ubo_data(common_data, nir)) - return false; + collect_ubo_data(common_data, nir); /* Collect stage-specific data. */ switch (stage) { @@ -498,7 +507,7 @@ bool rogue_collect_io_data(struct rogue_build_ctx *ctx, nir_shader *nir) break; } - return false; + unreachable("Unsupported stage."); } /** @@ -510,11 +519,12 @@ bool rogue_collect_io_data(struct rogue_build_ctx *ctx, nir_shader *nir) * \param[in] component The requested component. * \return The coefficient register index. */ -size_t rogue_coeff_index_fs(struct rogue_iterator_args *args, - gl_varying_slot location, - size_t component) +PUBLIC +unsigned rogue_coeff_index_fs(struct rogue_iterator_args *args, + gl_varying_slot location, + unsigned component) { - size_t i; + unsigned i; /* Special case: W coefficient. */ if (location == ~0) { @@ -542,11 +552,12 @@ size_t rogue_coeff_index_fs(struct rogue_iterator_args *args, * \param[in] component The requested component. * \return The vertex output index. */ -size_t rogue_output_index_vs(struct rogue_vertex_outputs *outputs, - gl_varying_slot location, - size_t component) +PUBLIC +unsigned rogue_output_index_vs(struct rogue_vertex_outputs *outputs, + gl_varying_slot location, + unsigned component) { - size_t i; + unsigned i; if (location == VARYING_SLOT_POS) { /* Always at location 0. */ @@ -575,16 +586,17 @@ size_t rogue_output_index_vs(struct rogue_vertex_outputs *outputs, * \param[in] offset_bytes The UBO offset in bytes. * \return The UBO offset shared register index. */ -size_t rogue_ubo_reg(struct rogue_ubo_data *ubo_data, - size_t desc_set, - size_t binding, - size_t offset_bytes) +PUBLIC +unsigned rogue_ubo_reg(struct rogue_ubo_data *ubo_data, + unsigned desc_set, + unsigned binding, + unsigned offset_bytes) { - size_t ubo_index = ~0; - size_t offset_regs; + unsigned ubo_index = ~0; + unsigned offset_regs; /* Find UBO located at (desc_set, binding). */ - for (size_t u = 0; u < ubo_data->num_ubo_entries; ++u) { + for (unsigned u = 0; u < ubo_data->num_ubo_entries; ++u) { if (ubo_data->dest[u] == ~0) continue; diff --git a/src/imagination/rogue/rogue_build_data.h b/src/imagination/rogue/rogue_build_data.h deleted file mode 100644 index 4d5dc58df56..00000000000 --- a/src/imagination/rogue/rogue_build_data.h +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright © 2022 Imagination Technologies Ltd. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef ROGUE_BUILD_DATA_H -#define ROGUE_BUILD_DATA_H - -#include -#include -#include - -#include "compiler/shader_enums.h" -#include "nir/nir.h" -#include "rogue.h" - -/* Max number of I/O varying variables. - * Fragment shader: MAX_VARYING + 1 (W coefficient). - * Vertex shader: MAX_VARYING + 1 (position slot). - */ -#define ROGUE_MAX_IO_VARYING_VARS (MAX_VARYING + 1) - -/* VERT_ATTRIB_GENERIC0-15 */ -#define ROGUE_MAX_IO_ATTRIB_VARS 16 - -/* Max buffers entries that can be used. */ -/* TODO: Currently UBOs are the only supported buffers. */ -#define ROGUE_MAX_BUFFERS 24 - -struct rogue_compiler; -struct rogue_shader; -struct rogue_shader_binary; - -/** - * \brief UBO data. - */ -struct rogue_ubo_data { - size_t num_ubo_entries; - size_t desc_set[ROGUE_MAX_BUFFERS]; - size_t binding[ROGUE_MAX_BUFFERS]; - size_t dest[ROGUE_MAX_BUFFERS]; - size_t size[ROGUE_MAX_BUFFERS]; -}; - -/** - * \brief Compile time constants that need uploading. - */ -struct rogue_compile_time_consts_data { - /* TODO: Output these from the compiler. */ - /* TODO: Add the other types. */ - struct { - size_t num; - size_t dest; - /* TODO: This should probably be bigger. Big enough to account for all - * available writable special constant regs. - */ - uint32_t value[ROGUE_MAX_BUFFERS]; - } static_consts; -}; - -/** - * \brief Per-stage common build data. - */ -struct rogue_common_build_data { - size_t temps; - size_t internals; - size_t coeffs; - size_t shareds; - - struct rogue_ubo_data ubo_data; - struct rogue_compile_time_consts_data compile_time_consts_data; -}; - -/** - * \brief Arguments for the FPU iterator(s) - * (produces varyings for the fragment shader). - */ -struct rogue_iterator_args { - uint32_t num_fpu_iterators; - uint32_t fpu_iterators[ROGUE_MAX_IO_VARYING_VARS]; - uint32_t destination[ROGUE_MAX_IO_VARYING_VARS]; - size_t base[ROGUE_MAX_IO_VARYING_VARS]; - size_t components[ROGUE_MAX_IO_VARYING_VARS]; -}; - -/** - * \brief Vertex input register allocations. - */ -struct rogue_vertex_inputs { - size_t num_input_vars; - size_t base[ROGUE_MAX_IO_ATTRIB_VARS]; - size_t components[ROGUE_MAX_IO_ATTRIB_VARS]; -}; - -/** - * \brief Vertex output allocations. - */ -struct rogue_vertex_outputs { - size_t num_output_vars; - size_t base[ROGUE_MAX_IO_VARYING_VARS]; - size_t components[ROGUE_MAX_IO_VARYING_VARS]; -}; - -/** - * \brief Stage-specific build data. - */ -struct rogue_build_data { - struct rogue_fs_build_data { - struct rogue_iterator_args iterator_args; - enum rogue_msaa_mode msaa_mode; - bool phas; /* Indicates the presence of PHAS instruction. */ - } fs; - struct rogue_vs_build_data { - struct rogue_vertex_inputs inputs; - size_t num_vertex_input_regs; /* Final number of inputs. */ - - struct rogue_vertex_outputs outputs; - size_t num_vertex_outputs; /* Final number of outputs. */ - - size_t num_varyings; /* Final number of varyings. */ - } vs; -}; - -/** - * \brief Shared multi-stage build context. - */ -struct rogue_build_ctx { - struct rogue_compiler *compiler; - - /* Shaders in various stages of compilations. */ - nir_shader *nir[MESA_SHADER_FRAGMENT + 1]; - struct rogue_shader *rogue[MESA_SHADER_FRAGMENT + 1]; - struct rogue_shader_binary *binary[MESA_SHADER_FRAGMENT + 1]; - - struct rogue_common_build_data common_data[MESA_SHADER_FRAGMENT + 1]; - struct rogue_build_data stage_data; -}; - -PUBLIC -struct rogue_build_ctx * -rogue_create_build_context(struct rogue_compiler *compiler); - -PUBLIC -bool rogue_collect_io_data(struct rogue_build_ctx *ctx, nir_shader *nir); - -PUBLIC -size_t rogue_coeff_index_fs(struct rogue_iterator_args *args, - gl_varying_slot location, - size_t component); - -PUBLIC -size_t rogue_output_index_vs(struct rogue_vertex_outputs *outputs, - gl_varying_slot location, - size_t component); - -PUBLIC -size_t rogue_ubo_reg(struct rogue_ubo_data *ubo_data, - size_t desc_set, - size_t binding, - size_t offset_bytes); - -#endif /* ROGUE_BUILD_DATA_H */ diff --git a/src/imagination/rogue/rogue_builder.c b/src/imagination/rogue/rogue_builder.c new file mode 100644 index 00000000000..87d9ae8809e --- /dev/null +++ b/src/imagination/rogue/rogue_builder.c @@ -0,0 +1,364 @@ +/* + * Copyright © 2022 Imagination Technologies Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "rogue.h" +#include "rogue_builder.h" +#include "util/macros.h" + +/** + * \file rogue_builder.c + * + * \brief Contains helper functions for building Rogue shaders. + */ + +/** + * \brief Inserts an instruction at the current builder context position. + * + * \param[in] b The builder context. + * \param[in] instr The instruction to insert. + */ +static inline void rogue_builder_insert_instr(rogue_builder *b, + rogue_instr *instr) +{ + rogue_instr_insert(instr, b->cursor); + b->cursor = rogue_cursor_after_instr(instr); +} + +/* ALU instructions */ + +static inline rogue_alu_instr *rogue_build_alu(rogue_builder *b, + enum rogue_alu_op op, + rogue_ref dst, + unsigned num_srcs, + rogue_ref srcs[num_srcs]) +{ + rogue_alu_instr *alu = + rogue_alu_instr_create(rogue_cursor_block(b->cursor), op); + + alu->dst.ref = dst; + for (unsigned i = 0; i < num_srcs; ++i) { + alu->src[i].ref = srcs[i]; + alu->src[i].index = i; + } + + rogue_builder_insert_instr(b, &alu->instr); + return alu; +} + +static inline rogue_alu_instr *rogue_build_alu1(rogue_builder *b, + enum rogue_alu_op op, + rogue_ref dst, + rogue_ref src0) +{ + rogue_ref srcs[] = { src0 }; + return rogue_build_alu(b, op, dst, 1, srcs); +} + +static inline rogue_alu_instr *rogue_build_alu2(rogue_builder *b, + enum rogue_alu_op op, + rogue_ref dst, + rogue_ref src0, + rogue_ref src1) +{ + rogue_ref srcs[] = { src0, src1 }; + return rogue_build_alu(b, op, dst, 2, srcs); +} + +static inline rogue_alu_instr *rogue_build_alu3(rogue_builder *b, + enum rogue_alu_op op, + rogue_ref dst, + rogue_ref src0, + rogue_ref src1, + rogue_ref src2) +{ + rogue_ref srcs[] = { src0, src1, src2 }; + return rogue_build_alu(b, op, dst, 3, srcs); +} + +/* TODO: Static inline in rogue.h? */ +#define ROGUE_BUILDER_DEFINE_ALU1(op) \ + PUBLIC \ + rogue_alu_instr *rogue_##op(rogue_builder *b, \ + rogue_ref dst, \ + rogue_ref src0) \ + { \ + assert(rogue_alu_op_infos[ROGUE_ALU_OP_##op].num_srcs == 1); \ + return rogue_build_alu1(b, ROGUE_ALU_OP_##op, dst, src0); \ + } + +#define ROGUE_BUILDER_DEFINE_ALU2(op) \ + PUBLIC \ + rogue_alu_instr *rogue_##op(rogue_builder *b, \ + rogue_ref dst, \ + rogue_ref src0, \ + rogue_ref src1) \ + { \ + assert(rogue_alu_op_infos[ROGUE_ALU_OP_##op].num_srcs == 2); \ + return rogue_build_alu2(b, ROGUE_ALU_OP_##op, dst, src0, src1); \ + } + +#define ROGUE_BUILDER_DEFINE_ALU3(op) \ + PUBLIC \ + rogue_alu_instr *rogue_##op(rogue_builder *b, \ + rogue_ref dst, \ + rogue_ref src0, \ + rogue_ref src1, \ + rogue_ref src2) \ + { \ + assert(rogue_alu_op_infos[ROGUE_ALU_OP_##op].num_srcs == 3); \ + return rogue_build_alu3(b, ROGUE_ALU_OP_##op, dst, src0, src1, src2); \ + } + +#include "rogue_alu_instrs.def" + +static inline rogue_backend_instr *rogue_build_backend(rogue_builder *b, + enum rogue_backend_op op, + unsigned num_dsts, + rogue_ref dsts[num_dsts], + unsigned num_srcs, + rogue_ref srcs[num_srcs]) +{ + rogue_backend_instr *backend = + rogue_backend_instr_create(rogue_cursor_block(b->cursor), op); + + for (unsigned i = 0; i < num_dsts; ++i) { + backend->dst[i].ref = dsts[i]; + backend->dst[i].index = i; + } + + for (unsigned i = 0; i < num_srcs; ++i) { + backend->src[i].ref = srcs[i]; + backend->src[i].index = i; + } + + rogue_builder_insert_instr(b, &backend->instr); + return backend; +} + +static inline rogue_backend_instr * +rogue_build_backend00(rogue_builder *b, enum rogue_backend_op op) +{ + return rogue_build_backend(b, op, 0, NULL, 0, NULL); +} + +static inline rogue_backend_instr * +rogue_build_backend11(rogue_builder *b, + enum rogue_backend_op op, + rogue_ref dst0, + rogue_ref src0) +{ + rogue_ref dsts[] = { dst0 }; + rogue_ref srcs[] = { src0 }; + return rogue_build_backend(b, op, 1, dsts, 1, srcs); +} + +static inline rogue_backend_instr * +rogue_build_backend14(rogue_builder *b, + enum rogue_backend_op op, + rogue_ref dst0, + rogue_ref src0, + rogue_ref src1, + rogue_ref src2, + rogue_ref src3) +{ + rogue_ref dsts[] = { dst0 }; + rogue_ref srcs[] = { src0, src1, src2, src3 }; + return rogue_build_backend(b, op, 1, dsts, 4, srcs); +} + +#define ROGUE_BUILDER_DEFINE_BACKEND00(op) \ + PUBLIC \ + rogue_backend_instr *rogue_##op(rogue_builder *b) \ + { \ + assert(rogue_backend_op_infos[ROGUE_BACKEND_OP_##op].num_dsts == 0); \ + assert(rogue_backend_op_infos[ROGUE_BACKEND_OP_##op].num_srcs == 0); \ + return rogue_build_backend00(b, ROGUE_BACKEND_OP_##op); \ + } + +#define ROGUE_BUILDER_DEFINE_BACKEND11(op) \ + PUBLIC \ + rogue_backend_instr *rogue_##op(rogue_builder *b, \ + rogue_ref dst0, \ + rogue_ref src0) \ + { \ + assert(rogue_backend_op_infos[ROGUE_BACKEND_OP_##op].num_dsts == 1); \ + assert(rogue_backend_op_infos[ROGUE_BACKEND_OP_##op].num_srcs == 1); \ + return rogue_build_backend11(b, ROGUE_BACKEND_OP_##op, dst0, src0); \ + } + +#define ROGUE_BUILDER_DEFINE_BACKEND14(op) \ + PUBLIC \ + rogue_backend_instr *rogue_##op(rogue_builder *b, \ + rogue_ref dst0, \ + rogue_ref src0, \ + rogue_ref src1, \ + rogue_ref src2, \ + rogue_ref src3) \ + { \ + assert(rogue_backend_op_infos[ROGUE_BACKEND_OP_##op].num_dsts == 1); \ + assert(rogue_backend_op_infos[ROGUE_BACKEND_OP_##op].num_srcs == 4); \ + return rogue_build_backend14(b, \ + ROGUE_BACKEND_OP_##op, \ + dst0, \ + src0, \ + src1, \ + src2, \ + src3); \ + } + +#include "rogue_backend_instrs.def" + +static inline rogue_ctrl_instr *rogue_build_ctrl(rogue_builder *b, + enum rogue_ctrl_op op, + rogue_block *target_block, + unsigned num_dsts, + rogue_ref dsts[num_dsts], + unsigned num_srcs, + rogue_ref srcs[num_srcs]) +{ + rogue_ctrl_instr *ctrl = + rogue_ctrl_instr_create(rogue_cursor_block(b->cursor), op); + + ctrl->target_block = target_block; + + for (unsigned i = 0; i < num_dsts; ++i) { + ctrl->dst[i].ref = dsts[i]; + ctrl->dst[i].index = i; + } + + for (unsigned i = 0; i < num_srcs; ++i) { + ctrl->src[i].ref = srcs[i]; + ctrl->src[i].index = i; + } + + rogue_builder_insert_instr(b, &ctrl->instr); + return ctrl; +} + +static inline rogue_ctrl_instr * +rogue_build_ctrlb(rogue_builder *b, enum rogue_ctrl_op op, rogue_block *block) +{ + return rogue_build_ctrl(b, op, block, 0, NULL, 0, NULL); +} + +static inline rogue_ctrl_instr *rogue_build_ctrl00(rogue_builder *b, + enum rogue_ctrl_op op) +{ + return rogue_build_ctrl(b, op, NULL, 0, NULL, 0, NULL); +} + +static inline rogue_ctrl_instr * +rogue_build_ctrl01(rogue_builder *b, enum rogue_ctrl_op op, rogue_ref src0) +{ + rogue_ref srcs[] = { src0 }; + return rogue_build_ctrl(b, op, NULL, 0, NULL, 1, srcs); +} + +#define ROGUE_BUILDER_DEFINE_CTRLB(op) \ + PUBLIC \ + rogue_ctrl_instr *rogue_##op(rogue_builder *b, rogue_block *block) \ + { \ + assert(rogue_ctrl_op_infos[ROGUE_CTRL_OP_##op].has_target); \ + assert(rogue_ctrl_op_infos[ROGUE_CTRL_OP_##op].num_dsts == 0); \ + assert(rogue_ctrl_op_infos[ROGUE_CTRL_OP_##op].num_srcs == 0); \ + return rogue_build_ctrlb(b, ROGUE_CTRL_OP_##op, block); \ + } + +#define ROGUE_BUILDER_DEFINE_CTRL00(op) \ + PUBLIC \ + rogue_ctrl_instr *rogue_##op(rogue_builder *b) \ + { \ + assert(!rogue_ctrl_op_infos[ROGUE_CTRL_OP_##op].has_target); \ + assert(rogue_ctrl_op_infos[ROGUE_CTRL_OP_##op].num_dsts == 0); \ + assert(rogue_ctrl_op_infos[ROGUE_CTRL_OP_##op].num_srcs == 0); \ + return rogue_build_ctrl00(b, ROGUE_CTRL_OP_##op); \ + } + +#define ROGUE_BUILDER_DEFINE_CTRL01(op) \ + PUBLIC \ + rogue_ctrl_instr *rogue_##op(rogue_builder *b, rogue_ref src0) \ + { \ + assert(!rogue_ctrl_op_infos[ROGUE_CTRL_OP_##op].has_target); \ + assert(rogue_ctrl_op_infos[ROGUE_CTRL_OP_##op].num_dsts == 0); \ + assert(rogue_ctrl_op_infos[ROGUE_CTRL_OP_##op].num_srcs == 1); \ + return rogue_build_ctrl01(b, ROGUE_CTRL_OP_##op, src0); \ + } + +#include "rogue_ctrl_instrs.def" + +static inline rogue_bitwise_instr *rogue_build_bitwise(rogue_builder *b, + enum rogue_bitwise_op op, + unsigned num_dsts, + rogue_ref dsts[num_dsts], + unsigned num_srcs, + rogue_ref srcs[num_srcs]) +{ + rogue_bitwise_instr *bitwise = + rogue_bitwise_instr_create(rogue_cursor_block(b->cursor), op); + + for (unsigned i = 0; i < num_dsts; ++i) { + bitwise->dst[i].ref = dsts[i]; + bitwise->dst[i].index = i; + } + + for (unsigned i = 0; i < num_srcs; ++i) { + bitwise->src[i].ref = srcs[i]; + bitwise->src[i].index = i; + } + + rogue_builder_insert_instr(b, &bitwise->instr); + return bitwise; +} + +static inline rogue_bitwise_instr * +rogue_build_bitwise22(rogue_builder *b, + enum rogue_bitwise_op op, + rogue_ref dst0, + rogue_ref dst1, + rogue_ref src0, + rogue_ref src1) +{ + rogue_ref dsts[] = { dst0, dst1 }; + rogue_ref srcs[] = { src0, src1 }; + return rogue_build_bitwise(b, op, 2, dsts, 2, srcs); +} + +#define ROGUE_BUILDER_DEFINE_BITWISE22(op) \ + PUBLIC \ + rogue_bitwise_instr *rogue_##op(rogue_builder *b, \ + rogue_ref dst0, \ + rogue_ref dst1, \ + rogue_ref src0, \ + rogue_ref src1) \ + { \ + assert(rogue_bitwise_op_infos[ROGUE_BITWISE_OP_##op].num_dsts == 2); \ + assert(rogue_bitwise_op_infos[ROGUE_BITWISE_OP_##op].num_srcs == 2); \ + return rogue_build_bitwise22(b, \ + ROGUE_BITWISE_OP_##op, \ + dst0, \ + dst1, \ + src0, \ + src1); \ + } + +#include "rogue_bitwise_instrs.def" diff --git a/src/imagination/rogue/rogue_builder.h b/src/imagination/rogue/rogue_builder.h new file mode 100644 index 00000000000..b3061541d3d --- /dev/null +++ b/src/imagination/rogue/rogue_builder.h @@ -0,0 +1,153 @@ +/* + * Copyright © 2022 Imagination Technologies Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef ROGUE_BUILDER_H +#define ROGUE_BUILDER_H + +/** + * \file rogue_builder.h + * + * \brief Contains helper functions for building Rogue shaders. + */ + +#include "rogue.h" + +/** Rogue builder context. */ +typedef struct rogue_builder { + rogue_shader *shader; /** The shader being built. */ + rogue_cursor cursor; /** The current position in the shader. */ +} rogue_builder; + +/** + * \brief Initialises a Rogue builder context. + * + * \param[in] b The builder context. + * \param[in] shader The shader. + */ +static inline void rogue_builder_init(rogue_builder *b, rogue_shader *shader) +{ + b->shader = shader; + b->cursor = rogue_cursor_before_shader(shader); +} + +/** + * \brief Inserts a basic block at the current builder context position. + * + * \param[in] b The builder context. + * \param[in] block The basic block to insert. + */ +static inline void rogue_builder_insert_block(rogue_builder *b, + rogue_block *block) +{ + rogue_block_insert(block, b->cursor); + b->cursor = rogue_cursor_after_block(block); +} + +/** + * \brief Inserts a new basic block at the current builder context position. + * + * \param[in] b The builder context. + * \param[in] label The (optional) basic block label. + * \return The new block. + */ +static inline rogue_block *rogue_push_block_labelled(rogue_builder *b, + const char *label) +{ + rogue_block *block = rogue_block_create(b->shader, label); + rogue_builder_insert_block(b, block); + + return block; +} + +/** + * \brief Inserts a new basic block at the current builder context position. + * + * \param[in] b The builder context. + * \return The new block. + */ +static inline rogue_block *rogue_push_block(rogue_builder *b) +{ + return rogue_push_block_labelled(b, NULL); +} + +/* ALU instructions. */ +#define ROGUE_BUILDER_DEFINE_ALU1(op) \ + rogue_alu_instr *rogue_##op(rogue_builder *b, rogue_ref dst, rogue_ref src0); + +#define ROGUE_BUILDER_DEFINE_ALU2(op) \ + rogue_alu_instr *rogue_##op(rogue_builder *b, \ + rogue_ref dst, \ + rogue_ref src0, \ + rogue_ref src1); + +#define ROGUE_BUILDER_DEFINE_ALU3(op) \ + rogue_alu_instr *rogue_##op(rogue_builder *b, \ + rogue_ref dst, \ + rogue_ref src0, \ + rogue_ref src1, \ + rogue_ref src2); + +#include "rogue_alu_instrs.def" + +/* Backend instructions. */ +#define ROGUE_BUILDER_DEFINE_BACKEND00(op) \ + rogue_backend_instr *rogue_##op(rogue_builder *b); + +#define ROGUE_BUILDER_DEFINE_BACKEND11(op) \ + rogue_backend_instr *rogue_##op(rogue_builder *b, \ + rogue_ref dst0, \ + rogue_ref src0); + +#define ROGUE_BUILDER_DEFINE_BACKEND14(op) \ + rogue_backend_instr *rogue_##op(rogue_builder *b, \ + rogue_ref dst0, \ + rogue_ref src0, \ + rogue_ref src1, \ + rogue_ref src2, \ + rogue_ref src3); + +#include "rogue_backend_instrs.def" + +/* Ctrl instructions. */ +#define ROGUE_BUILDER_DEFINE_CTRLB(op) \ + rogue_ctrl_instr *rogue_##op(rogue_builder *b, rogue_block *block); + +#define ROGUE_BUILDER_DEFINE_CTRL00(op) \ + rogue_ctrl_instr *rogue_##op(rogue_builder *b); + +#define ROGUE_BUILDER_DEFINE_CTRL01(op) \ + rogue_ctrl_instr *rogue_##op(rogue_builder *b, rogue_ref src0); + +#include "rogue_ctrl_instrs.def" + +/* Bitwise instructions. */ +#define ROGUE_BUILDER_DEFINE_BITWISE22(op) \ + rogue_bitwise_instr *rogue_##op(rogue_builder *b, \ + rogue_ref dst0, \ + rogue_ref dst1, \ + rogue_ref src0, \ + rogue_ref src1); + +#include "rogue_bitwise_instrs.def" + +#endif /* ROGUE_BUILDER_H */ diff --git a/src/imagination/rogue/rogue_compile.c b/src/imagination/rogue/rogue_compile.c new file mode 100644 index 00000000000..df1fb0f4a13 --- /dev/null +++ b/src/imagination/rogue/rogue_compile.c @@ -0,0 +1,403 @@ +/* + * Copyright © 2022 Imagination Technologies Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "compiler/spirv/nir_spirv.h" +#include "nir/nir.h" +#include "rogue.h" +#include "rogue_builder.h" +#include "util/macros.h" + +/** + * \file rogue_compile.c + * + * \brief Contains NIR to Rogue translation functions, and Rogue passes. + */ + +static void trans_nir_jump_return(rogue_builder *b, nir_jump_instr *jump) +{ + rogue_END(b); +} + +static void trans_nir_jump(rogue_builder *b, nir_jump_instr *jump) +{ + switch (jump->type) { + case nir_jump_return: + return trans_nir_jump_return(b, jump); + + default: + break; + } + + unreachable("Unimplemented NIR jump instruction type."); +} + +static void trans_nir_intrinsic_load_input_fs(rogue_builder *b, + nir_intrinsic_instr *intr) +{ + struct rogue_fs_build_data *fs_data = &b->shader->ctx->stage_data.fs; + + unsigned load_size = nir_dest_num_components(intr->dest); + assert(load_size == 1); /* TODO: We can support larger load sizes. */ + + rogue_reg *dst = rogue_ssa_reg(b->shader, intr->dest.reg.reg->index); + + struct nir_io_semantics io_semantics = nir_intrinsic_io_semantics(intr); + unsigned component = nir_intrinsic_component(intr); + unsigned coeff_index = rogue_coeff_index_fs(&fs_data->iterator_args, + io_semantics.location, + component); + unsigned wcoeff_index = rogue_coeff_index_fs(&fs_data->iterator_args, ~0, 0); + + rogue_regarray *coeffs = rogue_coeff_regarray(b->shader, + ROGUE_COEFF_ALIGN * load_size, + coeff_index); + rogue_regarray *wcoeffs = + rogue_coeff_regarray(b->shader, ROGUE_COEFF_ALIGN, wcoeff_index); + + rogue_instr *instr = &rogue_FITRP_PIXEL(b, + rogue_ref_reg(dst), + rogue_ref_drc(0), + rogue_ref_regarray(coeffs), + rogue_ref_regarray(wcoeffs), + rogue_ref_val(load_size)) + ->instr; + rogue_add_instr_comment(instr, "load_input_fs"); +} + +static void trans_nir_intrinsic_load_input_vs(rogue_builder *b, + nir_intrinsic_instr *intr) +{ + ASSERTED unsigned load_size = nir_dest_num_components(intr->dest); + assert(load_size == 1); /* TODO: We can support larger load sizes. */ + + rogue_reg *dst = rogue_ssa_reg(b->shader, intr->dest.reg.reg->index); + + struct nir_io_semantics io_semantics = nir_intrinsic_io_semantics(intr); + unsigned component = nir_intrinsic_component(intr); + /* TODO: Get these properly with the intrinsic index (ssa argument) */ + unsigned vtxin_index = + ((io_semantics.location - VERT_ATTRIB_GENERIC0) * 3) + component; + + rogue_reg *src = rogue_vtxin_reg(b->shader, vtxin_index); + rogue_instr *instr = + &rogue_MOV(b, rogue_ref_reg(dst), rogue_ref_reg(src))->instr; + rogue_add_instr_comment(instr, "load_input_vs"); +} + +static void trans_nir_intrinsic_load_input(rogue_builder *b, + nir_intrinsic_instr *intr) +{ + switch (b->shader->stage) { + case MESA_SHADER_FRAGMENT: + return trans_nir_intrinsic_load_input_fs(b, intr); + + case MESA_SHADER_VERTEX: + return trans_nir_intrinsic_load_input_vs(b, intr); + + default: + break; + } + + unreachable("Unimplemented NIR load_input variant."); +} + +static void trans_nir_intrinsic_store_output_fs(rogue_builder *b, + nir_intrinsic_instr *intr) +{ + ASSERTED unsigned store_size = nir_src_num_components(intr->src[0]); + assert(store_size == 1); + + nir_const_value *const_value = nir_src_as_const_value(intr->src[1]); + /* TODO: When hoisting I/O allocation to the driver, check if this is + * correct. + */ + unsigned pixout_index = nir_const_value_as_uint(*const_value, 32); + + rogue_reg *dst = rogue_pixout_reg(b->shader, pixout_index); + rogue_reg *src = rogue_ssa_reg(b->shader, intr->src[0].reg.reg->index); + + rogue_instr *instr = + &rogue_MOV(b, rogue_ref_reg(dst), rogue_ref_reg(src))->instr; + rogue_add_instr_comment(instr, "store_output_fs"); +} + +static void trans_nir_intrinsic_store_output_vs(rogue_builder *b, + nir_intrinsic_instr *intr) +{ + struct rogue_vs_build_data *vs_data = &b->shader->ctx->stage_data.vs; + + ASSERTED unsigned store_size = nir_src_num_components(intr->src[0]); + assert(store_size == 1); + + struct nir_io_semantics io_semantics = nir_intrinsic_io_semantics(intr); + unsigned component = nir_intrinsic_component(intr); + unsigned vtxout_index = rogue_output_index_vs(&vs_data->outputs, + io_semantics.location, + component); + + rogue_reg *dst = rogue_vtxout_reg(b->shader, vtxout_index); + rogue_reg *src = rogue_ssa_reg(b->shader, intr->src[0].reg.reg->index); + + rogue_instr *instr = + &rogue_MOV(b, rogue_ref_reg(dst), rogue_ref_reg(src))->instr; + rogue_add_instr_comment(instr, "store_output_vs"); +} + +static void trans_nir_intrinsic_store_output(rogue_builder *b, + nir_intrinsic_instr *intr) +{ + switch (b->shader->stage) { + case MESA_SHADER_FRAGMENT: + return trans_nir_intrinsic_store_output_fs(b, intr); + + case MESA_SHADER_VERTEX: + return trans_nir_intrinsic_store_output_vs(b, intr); + + default: + break; + } + + unreachable("Unimplemented NIR store_output variant."); +} + +static void trans_nir_intrinsic_load_ubo(rogue_builder *b, + nir_intrinsic_instr *intr) +{ + struct rogue_ubo_data *ubo_data = + &b->shader->ctx->common_data[b->shader->stage].ubo_data; + + unsigned desc_set = nir_src_comp_as_uint(intr->src[0], 0); + unsigned binding = nir_src_comp_as_uint(intr->src[0], 1); + unsigned offset = nir_intrinsic_range_base(intr); + + unsigned sh_index = rogue_ubo_reg(ubo_data, desc_set, binding, offset); + + rogue_reg *dst = rogue_ssa_reg(b->shader, intr->dest.reg.reg->index); + rogue_reg *src = rogue_shared_reg(b->shader, sh_index); + rogue_instr *instr = + &rogue_MOV(b, rogue_ref_reg(dst), rogue_ref_reg(src))->instr; + rogue_add_instr_comment(instr, "load_ubo"); +} + +static void trans_nir_intrinsic(rogue_builder *b, nir_intrinsic_instr *intr) +{ + switch (intr->intrinsic) { + case nir_intrinsic_load_input: + return trans_nir_intrinsic_load_input(b, intr); + + case nir_intrinsic_store_output: + return trans_nir_intrinsic_store_output(b, intr); + + case nir_intrinsic_load_ubo: + return trans_nir_intrinsic_load_ubo(b, intr); + + default: + break; + } + + unreachable("Unimplemented NIR intrinsic instruction."); +} + +static void trans_nir_alu_pack_unorm_4x8(rogue_builder *b, nir_alu_instr *alu) +{ + rogue_reg *dst = rogue_ssa_reg(b->shader, alu->dest.dest.reg.reg->index); + rogue_regarray *src_array = + rogue_ssa_vec_regarray(b->shader, 4, alu->src[0].src.reg.reg->index, 0); + + rogue_alu_instr *pck_u8888 = + rogue_PCK_U8888(b, rogue_ref_reg(dst), rogue_ref_regarray(src_array)); + rogue_set_instr_repeat(&pck_u8888->instr, 4); + rogue_set_alu_op_mod(pck_u8888, ROGUE_ALU_OP_MOD_SCALE); +} + +static void trans_nir_alu_mov(rogue_builder *b, nir_alu_instr *alu) +{ + rogue_reg *dst; + + unsigned dst_index = alu->dest.dest.reg.reg->index; + if (alu->dest.dest.reg.reg->num_components > 1) { + assert(util_is_power_of_two_nonzero(alu->dest.write_mask)); + dst = + rogue_ssa_vec_reg(b->shader, dst_index, ffs(alu->dest.write_mask) - 1); + } else { + dst = rogue_ssa_reg(b->shader, dst_index); + } + + if (alu->src[0].src.is_ssa) { + /* Immediate/constant source. */ + nir_const_value *const_value = nir_src_as_const_value(alu->src[0].src); + unsigned imm = nir_const_value_as_uint(*const_value, 32); + rogue_MOV(b, rogue_ref_reg(dst), rogue_ref_imm(imm)); + } else { + /* Register source. */ + rogue_reg *src = rogue_ssa_reg(b->shader, alu->src[0].src.reg.reg->index); + rogue_MOV(b, rogue_ref_reg(dst), rogue_ref_reg(src)); + } +} + +static void trans_nir_alu_fmul(rogue_builder *b, nir_alu_instr *alu) +{ + rogue_reg *dst = rogue_ssa_reg(b->shader, alu->dest.dest.reg.reg->index); + rogue_reg *src0 = rogue_ssa_reg(b->shader, alu->src[0].src.reg.reg->index); + rogue_reg *src1 = rogue_ssa_reg(b->shader, alu->src[1].src.reg.reg->index); + + rogue_FMUL(b, rogue_ref_reg(dst), rogue_ref_reg(src0), rogue_ref_reg(src1)); +} + +static void trans_nir_alu_ffma(rogue_builder *b, nir_alu_instr *alu) +{ + rogue_reg *dst = rogue_ssa_reg(b->shader, alu->dest.dest.reg.reg->index); + rogue_reg *src0 = rogue_ssa_reg(b->shader, alu->src[0].src.reg.reg->index); + rogue_reg *src1 = rogue_ssa_reg(b->shader, alu->src[1].src.reg.reg->index); + rogue_reg *src2 = rogue_ssa_reg(b->shader, alu->src[2].src.reg.reg->index); + + rogue_FMAD(b, + rogue_ref_reg(dst), + rogue_ref_reg(src0), + rogue_ref_reg(src1), + rogue_ref_reg(src2)); +} + +static void trans_nir_alu(rogue_builder *b, nir_alu_instr *alu) +{ + switch (alu->op) { + case nir_op_pack_unorm_4x8: + return trans_nir_alu_pack_unorm_4x8(b, alu); + return; + + case nir_op_mov: + return trans_nir_alu_mov(b, alu); + + case nir_op_fmul: + return trans_nir_alu_fmul(b, alu); + + case nir_op_ffma: + return trans_nir_alu_ffma(b, alu); + + default: + break; + } + + unreachable("Unimplemented NIR ALU instruction."); +} + +static inline void rogue_feedback_used_regs(rogue_build_ctx *ctx, + const rogue_shader *shader) +{ + /* TODO NEXT: Use this counting method elsewhere as well. */ + ctx->common_data[shader->stage].temps = + __bitset_count(shader->regs_used[ROGUE_REG_CLASS_TEMP], + BITSET_WORDS(rogue_reg_infos[ROGUE_REG_CLASS_TEMP].num)); + ctx->common_data[shader->stage].internals = __bitset_count( + shader->regs_used[ROGUE_REG_CLASS_INTERNAL], + BITSET_WORDS(rogue_reg_infos[ROGUE_REG_CLASS_INTERNAL].num)); +} + +/** + * \brief Translates a NIR shader to Rogue. + * + * \param[in] ctx Shared multi-stage build context. + * \param[in] nir NIR shader. + * \return A rogue_shader* if successful, or NULL if unsuccessful. + */ +PUBLIC +rogue_shader *rogue_nir_to_rogue(rogue_build_ctx *ctx, const nir_shader *nir) +{ + gl_shader_stage stage = nir->info.stage; + struct rogue_shader *shader = rogue_shader_create(ctx, stage); + if (!shader) + return NULL; + + shader->ctx = ctx; + + /* Make sure we only have a single function. */ + assert(exec_list_length(&nir->functions) == 1); + + rogue_builder b; + rogue_builder_init(&b, shader); + + /* Translate shader entrypoint. */ + nir_function_impl *entry = nir_shader_get_entrypoint((nir_shader *)nir); + nir_foreach_block (block, entry) { + rogue_push_block(&b); + + nir_foreach_instr (instr, block) { + switch (instr->type) { + case nir_instr_type_alu: + trans_nir_alu(&b, nir_instr_as_alu(instr)); + break; + + case nir_instr_type_intrinsic: + trans_nir_intrinsic(&b, nir_instr_as_intrinsic(instr)); + break; + + case nir_instr_type_load_const: + /* trans_nir_load_const(&b, nir_instr_as_load_const(instr)); */ + break; + + case nir_instr_type_jump: + trans_nir_jump(&b, nir_instr_as_jump(instr)); + break; + + default: + unreachable("Unimplemented NIR instruction type."); + } + } + } + + /* Apply passes. */ + rogue_shader_passes(shader); + + rogue_feedback_used_regs(ctx, shader); + + return shader; +} + +/** + * \brief Performs Rogue passes on a shader. + * + * \param[in] shader The shader. + */ +PUBLIC +void rogue_shader_passes(rogue_shader *shader) +{ + rogue_validate_shader(shader, "before passes"); + + if (ROGUE_DEBUG(IR_PASSES)) + rogue_print_pass_debug(shader, "before passes", stdout); + + /* Passes */ + ROGUE_PASS_V(shader, rogue_constreg); + ROGUE_PASS_V(shader, rogue_copy_prop); + ROGUE_PASS_V(shader, rogue_dce); + ROGUE_PASS_V(shader, rogue_lower_pseudo_ops); + ROGUE_PASS_V(shader, rogue_schedule_wdf, false); + ROGUE_PASS_V(shader, rogue_schedule_uvsw, false); + ROGUE_PASS_V(shader, rogue_trim); + ROGUE_PASS_V(shader, rogue_regalloc); + ROGUE_PASS_V(shader, rogue_dce); + ROGUE_PASS_V(shader, rogue_schedule_instr_groups, false); + + if (ROGUE_DEBUG(IR)) + rogue_print_pass_debug(shader, "after passes", stdout); +} diff --git a/src/imagination/rogue/rogue_compiler.c b/src/imagination/rogue/rogue_compiler.c deleted file mode 100644 index 9ae95b076bb..00000000000 --- a/src/imagination/rogue/rogue_compiler.c +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright © 2022 Imagination Technologies Ltd. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include - -#include "compiler/glsl_types.h" -#include "rogue_compiler.h" -#include "util/ralloc.h" - -/** - * \file rogue_compiler.c - * - * \brief Contains the Rogue compiler interface. - */ - -/** - * \brief Creates and sets up a Rogue compiler context. - * - * \param[in] dev_info Device info pointer. - * \return A pointer to the new compiler context, or NULL on failure. - */ -struct rogue_compiler * -rogue_compiler_create(const struct pvr_device_info *dev_info) -{ - struct rogue_compiler *compiler; - - compiler = rzalloc_size(NULL, sizeof(*compiler)); - if (!compiler) - return NULL; - - compiler->dev_info = dev_info; - - /* TODO: Additional compiler setup (allocators? error message output - * location?). - */ - - glsl_type_singleton_init_or_ref(); - - return compiler; -} - -/** - * \brief Destroys and frees a compiler context. - * - * \param[in] compiler The compiler context. - */ -void rogue_compiler_destroy(struct rogue_compiler *compiler) -{ - glsl_type_singleton_decref(); - - ralloc_free(compiler); -} diff --git a/src/imagination/rogue/rogue_compiler.h b/src/imagination/rogue/rogue_compiler.h deleted file mode 100644 index ed7b6fc720b..00000000000 --- a/src/imagination/rogue/rogue_compiler.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright © 2022 Imagination Technologies Ltd. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef ROGUE_COMPILER_H -#define ROGUE_COMPILER_H - -#include "util/macros.h" - -struct pvr_device_info; - -/** - * \brief Compiler context. - */ -struct rogue_compiler { - const struct pvr_device_info *dev_info; -}; - -PUBLIC -struct rogue_compiler * -rogue_compiler_create(const struct pvr_device_info *dev_info); - -PUBLIC -void rogue_compiler_destroy(struct rogue_compiler *compiler); - -#endif /* ROGUE_COMPILER_H */ diff --git a/src/imagination/rogue/rogue_constreg.c b/src/imagination/rogue/rogue_constreg.c index 14c193cf945..b2c8ce5af56 100644 --- a/src/imagination/rogue/rogue_constreg.c +++ b/src/imagination/rogue/rogue_constreg.c @@ -25,7 +25,7 @@ #include #include -#include "rogue_constreg.h" +#include "rogue.h" #include "util/macros.h" /** @@ -37,10 +37,10 @@ /** * \brief Mapping of constant register values and their indices. */ -struct rogue_constreg { +typedef struct rogue_constreg_map { uint32_t value; - size_t index; -}; + unsigned index; +} rogue_constreg_map; #define CONSTREG(VALUE, INDEX) \ { \ @@ -50,7 +50,7 @@ struct rogue_constreg { /** * \brief Constant register values (sorted for bsearch). */ -static const struct rogue_constreg const_regs[] = { +static const rogue_constreg_map const_regs[] = { CONSTREG(0x00000000U, 0U), /* 0 (INT32) / 0.0 (Float) */ CONSTREG(0x00000001U, 1U), /* 1 (INT32) */ CONSTREG(0x00000002U, 2U), /* 2 (INT32) */ @@ -153,7 +153,7 @@ static const struct rogue_constreg const_regs[] = { #undef CONSTREG /** - * \brief Comparison function for bsearch() to support struct rogue_constreg. + * \brief Comparison function for bsearch() to support rogue_constreg_map. * * \param[in] lhs The left hand side of the comparison. * \param[in] rhs The right hand side of the comparison. @@ -161,8 +161,8 @@ static const struct rogue_constreg const_regs[] = { */ static int constreg_cmp(const void *lhs, const void *rhs) { - const struct rogue_constreg *l = lhs; - const struct rogue_constreg *r = rhs; + const rogue_constreg_map *l = lhs; + const rogue_constreg_map *r = rhs; if (l->value < r->value) return -1; @@ -173,24 +173,24 @@ static int constreg_cmp(const void *lhs, const void *rhs) } /** - * \brief Determines whether a given integer value exists in a constant - * register. + * \brief Determines whether a given value exists in a constant register. * - * \param[in] value The value required. + * \param[in] imm The immediate value required. * \return The index of the constant register containing the value, or * ROGUE_NO_CONST_REG if the value is not found. */ -size_t rogue_constreg_lookup(uint32_t value) +PUBLIC +unsigned rogue_constreg_lookup(rogue_imm_t imm) { - struct rogue_constreg constreg_target = { - .value = value, + rogue_constreg_map constreg_target = { + .value = imm.u32, }; - const struct rogue_constreg *constreg; + const rogue_constreg_map *constreg; constreg = bsearch(&constreg_target, const_regs, ARRAY_SIZE(const_regs), - sizeof(struct rogue_constreg), + sizeof(rogue_constreg_map), constreg_cmp); if (!constreg) return ROGUE_NO_CONST_REG; diff --git a/src/imagination/rogue/rogue_regalloc.h b/src/imagination/rogue/rogue_ctrl_instrs.def similarity index 56% rename from src/imagination/rogue/rogue_regalloc.h rename to src/imagination/rogue/rogue_ctrl_instrs.def index eb831eac426..656a66b9f7b 100644 --- a/src/imagination/rogue/rogue_regalloc.h +++ b/src/imagination/rogue/rogue_ctrl_instrs.def @@ -21,50 +21,45 @@ * SOFTWARE. */ -#ifndef ROGUE_REGALLOC_H -#define ROGUE_REGALLOC_H - -#include -#include - -#include "util/list.h" - /** - * \brief Register classes used for allocation. + * \file rogue_ctrl_instrs.def + * + * \brief Contains macros defining control instructions. */ -enum rogue_reg_class { - ROGUE_REG_CLASS_TEMP, - ROGUE_REG_CLASS_VEC4, - ROGUE_REG_CLASS_COUNT, -}; - -/** - * \brief Register data for each class. +/* + * B - B: Block destination operand. + * XY - X: Num destinations, Y: Num sources. */ -struct rogue_reg_data { - enum rogue_operand_type type; - size_t count; - size_t stride; - size_t offset; - struct ra_class *class; - size_t num_used; -}; - -/** - * \brief Register allocation context. +/* + * ROGUE_BUILDER_DEFINE_CTRLB + * B: Block destination operand. + * + * ROGUE_BUILDER_DEFINE_CTRLds + * d: Number of destinations. + * s: Number of sources. */ -struct rogue_ra { - struct ra_regs *regs; - struct rogue_reg_data reg_data[ROGUE_REG_CLASS_COUNT]; -}; +#ifndef ROGUE_BUILDER_DEFINE_CTRLB +#define ROGUE_BUILDER_DEFINE_CTRLB(...) +#endif /* ROGUE_BUILDER_DEFINE_CTRLB */ -struct rogue_ra *rogue_ra_init(void *mem_ctx); -bool rogue_ra_alloc(struct list_head *instr_list, - struct rogue_ra *ra, - size_t *temps_used, - size_t *internals_used); +#ifndef ROGUE_BUILDER_DEFINE_CTRL00 +#define ROGUE_BUILDER_DEFINE_CTRL00(...) +#endif /* ROGUE_BUILDER_DEFINE_CTRL00 */ -#endif /* ROGUE_REGALLOC_H */ +#ifndef ROGUE_BUILDER_DEFINE_CTRL01 +#define ROGUE_BUILDER_DEFINE_CTRL01(...) +#endif /* ROGUE_BUILDER_DEFINE_CTRL01 */ + +ROGUE_BUILDER_DEFINE_CTRLB(BA) + +ROGUE_BUILDER_DEFINE_CTRL00(END) +ROGUE_BUILDER_DEFINE_CTRL00(NOP) + +ROGUE_BUILDER_DEFINE_CTRL01(WDF) + +#undef ROGUE_BUILDER_DEFINE_CTRL01 +#undef ROGUE_BUILDER_DEFINE_CTRL00 +#undef ROGUE_BUILDER_DEFINE_CTRLB diff --git a/src/imagination/rogue/rogue_debug.c b/src/imagination/rogue/rogue_debug.c new file mode 100644 index 00000000000..2f5bbbda685 --- /dev/null +++ b/src/imagination/rogue/rogue_debug.c @@ -0,0 +1,85 @@ +/* + * Copyright © 2022 Imagination Technologies Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "nir/nir.h" +#include "rogue.h" +#include "util/macros.h" +#include "util/u_debug.h" + +#include +#include +#include + +/** + * \file rogue_debug.c + * + * \brief Contains debugging functions and data. + */ + +static const struct debug_named_value rogue_debug_options[] = { + { "nir", ROGUE_DEBUG_NIR, "Print NIR" }, + { "nir_passes", ROGUE_DEBUG_NIR_PASSES, "Print NIR passes" }, + { "ir", ROGUE_DEBUG_IR, "Print Rogue IR" }, + { "ir_passes", ROGUE_DEBUG_IR_PASSES, "Print Rogue IR passes" }, + { "ir_details", + ROGUE_DEBUG_IR_DETAILS, + "Print Rogue IR details (with ir/ir_passes enabled)" }, + { "vld_skip", ROGUE_DEBUG_VLD_SKIP, "Skip Rogue IR validation" }, + { "vld_nonfatal", ROGUE_DEBUG_VLD_NONFATAL, "Non-fatal Rogue IR validation" }, + DEBUG_NAMED_VALUE_END, +}; + +#define ROGUE_DEBUG_DEFAULT 0U +DEBUG_GET_ONCE_FLAGS_OPTION(rogue_debug, + "ROGUE_DEBUG", + rogue_debug_options, + ROGUE_DEBUG_DEFAULT) + +PUBLIC +unsigned long rogue_debug = ROGUE_DEBUG_DEFAULT; + +DEBUG_GET_ONCE_OPTION(rogue_color, "ROGUE_COLOR", NULL) + +bool rogue_color = false; + +static void rogue_debug_init_once(void) +{ + /* Get debug flags. */ + rogue_debug = debug_get_option_rogue_debug(); + + /* Get/parse color option. */ + const char *color_opt = debug_get_option_rogue_color(); + if (!color_opt || !strcmp(color_opt, "auto") || !strcmp(color_opt, "a")) + rogue_color = isatty(fileno(stdout)); + else if (!strcmp(color_opt, "on") || !strcmp(color_opt, "1")) + rogue_color = true; + else if (!strcmp(color_opt, "off") || !strcmp(color_opt, "0")) + rogue_color = false; +} + +PUBLIC +void rogue_debug_init(void) +{ + static once_flag flag = ONCE_FLAG_INIT; + call_once(&flag, rogue_debug_init_once); +} diff --git a/src/imagination/rogue/rogue_dump.c b/src/imagination/rogue/rogue_dump.c deleted file mode 100644 index ee21be04f93..00000000000 --- a/src/imagination/rogue/rogue_dump.c +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright © 2022 Imagination Technologies Ltd. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include - -#include "rogue_dump.h" -#include "rogue_shader.h" -#include "rogue_util.h" -#include "util/bitscan.h" - -/** - * \file rogue_dump.c - * - * \brief Contains functions to dump Rogue data structures into a textual - * format. - */ - -static const char *const rogue_operand_string[ROGUE_OPERAND_TYPE_COUNT] = { - [ROGUE_OPERAND_TYPE_REG_TEMP] = "r", - [ROGUE_OPERAND_TYPE_REG_COEFF] = "cf", - [ROGUE_OPERAND_TYPE_REG_CONST] = "c", - [ROGUE_OPERAND_TYPE_REG_SHARED] = "sh", - [ROGUE_OPERAND_TYPE_REG_PIXEL_OUT] = "po", - [ROGUE_OPERAND_TYPE_REG_VERTEX_IN] = "vi", - [ROGUE_OPERAND_TYPE_REG_INTERNAL] = "i", - [ROGUE_OPERAND_TYPE_IMMEDIATE] = "#", - [ROGUE_OPERAND_TYPE_DRC] = "drc", - [ROGUE_OPERAND_TYPE_VREG] = "V", -}; - -static const char *const rogue_opcode_string[ROGUE_OP_COUNT] = { - [ROGUE_OP_NOP] = "nop", - [ROGUE_OP_END_FRAG] = "end.frag", - [ROGUE_OP_END_VERT] = "end.vert", - [ROGUE_OP_WDF] = "wdf", - [ROGUE_OP_PIX_ITER_W] = "pixiter.w", - [ROGUE_OP_MAX] = "max", - [ROGUE_OP_MIN] = "min", - [ROGUE_OP_PACK_U8888] = "pack.u8888", - [ROGUE_OP_MOV] = "mov", - [ROGUE_OP_MOV_IMM] = "mov.imm", - [ROGUE_OP_FMA] = "fma", - [ROGUE_OP_MUL] = "mul", - [ROGUE_OP_VTXOUT] = "vtxout", -}; - -static const char *const rogue_instr_flag_string[ROGUE_INSTR_FLAG_COUNT] = { - [ROGUE_INSTR_FLAG_SAT] = "sat", - [ROGUE_INSTR_FLAG_LP] = "lp", - [ROGUE_INSTR_FLAG_OLCHK] = "olchk", -}; - -static const char rogue_vector_string[4] = { - 'x', - 'y', - 'z', - 'w', -}; - -/** - * \brief Dumps an operand as text to a file pointer. - * - * \param[in] operand The operand. - * \param[in] fp The file pointer. - * \return true if successful, otherwise false. - */ -bool rogue_dump_operand(const struct rogue_operand *operand, FILE *fp) -{ - ASSERT_OPERAND_RANGE(operand->type); - - fprintf(fp, "%s", rogue_operand_string[operand->type]); - - if (operand->type == ROGUE_OPERAND_TYPE_IMMEDIATE) - fprintf(fp, "%" PRIu64, operand->immediate.value); - else if (operand->type == ROGUE_OPERAND_TYPE_DRC) - fprintf(fp, "%zu", operand->drc.number); - else if (rogue_check_bitset(rogue_onehot(operand->type), ROGUE_MASK_ANY_REG)) - fprintf(fp, "%zu", operand->reg.number); - else if (operand->type == ROGUE_OPERAND_TYPE_VREG) { - fprintf(fp, "%zu", operand->vreg.number); - if (operand->vreg.is_vector) - fprintf(fp, ".%c", rogue_vector_string[operand->vreg.component]); - } - - return true; -} - -/** - * \brief Dumps an instruction as text to a file pointer. - * - * \param[in] instr The instruction. - * \param[in] fp The file pointer. - * \return true if successful, otherwise false. - */ -bool rogue_dump_instr(const struct rogue_instr *instr, FILE *fp) -{ - uint64_t flags = 0U; - - ASSERT_OPCODE_RANGE(instr->opcode); - - flags = instr->flags; - - fprintf(fp, "%s", rogue_opcode_string[instr->opcode]); - - /* Iterate over each flag bit and print its string form. */ - while (flags) { - uint64_t flag = u_bit_scan64(&flags); - ASSERT_INSTR_FLAG_RANGE(flag); - fprintf(fp, ".%s", rogue_instr_flag_string[flag]); - } - - if (instr->num_operands) - fprintf(fp, " "); - - /* Dump each operand. */ - for (size_t u = 0U; u < instr->num_operands; ++u) { - CHECKF(rogue_dump_operand(&instr->operands[u], fp), - "Failed to dump operand."); - if (u < (instr->num_operands - 1)) - fprintf(fp, ", "); - } - - fprintf(fp, ";"); - - return true; -} - -/** - * \brief Dumps a shader as text to a file pointer. - * - * \param[in] shader The shader. - * \param[in] fp The file pointer. - * \return true if successful, otherwise false. - */ -bool rogue_dump_shader(const struct rogue_shader *shader, FILE *fp) -{ - /* Dump the shader stage. */ - fprintf(fp, "# %s shader\n", _mesa_shader_stage_to_string(shader->stage)); - - /* Dump each instruction. */ - foreach_instr (instr, &shader->instr_list) { - CHECKF(rogue_dump_instr(instr, fp), "Failed to dump instruction."); - fprintf(fp, "\n"); - } - fprintf(fp, "\n"); - - return true; -} diff --git a/src/imagination/rogue/rogue_encode.c b/src/imagination/rogue/rogue_encode.c index 4550ddaebae..e932d5219b8 100644 --- a/src/imagination/rogue/rogue_encode.c +++ b/src/imagination/rogue/rogue_encode.c @@ -21,831 +21,787 @@ * SOFTWARE. */ -#include -#include -#include -#include -#include - -#include "hwdef/rogue_hw_defs.h" -#include "rogue_encode.h" -#include "rogue_encoders.h" -#include "rogue_operand.h" -#include "rogue_shader.h" -#include "rogue_util.h" -#include "util/bitscan.h" +#include "rogue.h" +#include "rogue_isa.h" #include "util/macros.h" +#include "util/u_dynarray.h" -static size_t rogue_encode_reg_bank(const struct rogue_operand *operand) +#include + +/** + * \file rogue_encode.c + * + * \brief Contains hardware encoding functions. + */ + +#define util_dynarray_append_mem(buf, size, mem) \ + memcpy(util_dynarray_grow_bytes((buf), 1, size), mem, size) + +static unsigned rogue_calc_da(const rogue_instr_group *group) { - switch (operand->type) { - case ROGUE_OPERAND_TYPE_REG_INTERNAL: - case ROGUE_OPERAND_TYPE_REG_PIXEL_OUT: - case ROGUE_OPERAND_TYPE_REG_CONST: - return 0; - case ROGUE_OPERAND_TYPE_REG_TEMP: - return 1; - case ROGUE_OPERAND_TYPE_REG_VERTEX_IN: - return 2; - case ROGUE_OPERAND_TYPE_REG_COEFF: - return 3; - case ROGUE_OPERAND_TYPE_REG_SHARED: - return 4; - default: - break; + unsigned da = group->size.header; + + if (group->header.alu == ROGUE_ALU_MAIN) { + for (unsigned u = ROGUE_INSTR_PHASE_COUNT; u > 0; --u) { + enum rogue_instr_phase p = u - 1; + if (p > ROGUE_INSTR_PHASE_1) + da += group->size.instrs[p]; + } + } else if (group->header.alu == ROGUE_ALU_BITWISE) { + for (unsigned u = ROGUE_INSTR_PHASE_COUNT; u > 0; --u) { + enum rogue_instr_phase p = u - 1; + da += group->size.instrs[p]; + } + } else if (group->header.alu == ROGUE_ALU_CONTROL) { + const rogue_instr *instr = group->instrs[ROGUE_INSTR_PHASE_CTRL]; + const rogue_ctrl_instr *ctrl = rogue_instr_as_ctrl(instr); + + if (!rogue_ctrl_op_has_srcs(ctrl->op) && + !rogue_ctrl_op_has_dsts(ctrl->op)) { + da = 0; + } else { + da += group->size.instrs[ROGUE_INSTR_PHASE_CTRL]; + } + + } else { + unreachable("Invalid instruction group ALU."); } - unreachable("Unimplemented register bank."); + return da; } -/** - * \brief Field mapping type. - */ -enum rogue_map_type { - ROGUE_MAP_TYPE_INSTR_FLAG = 0, - ROGUE_MAP_TYPE_OPERAND_FLAG, - ROGUE_MAP_TYPE_OPERAND, - - ROGUE_MAP_TYPE_COUNT, -}; - -/** - * \brief Field mapping rule description. - */ -struct rogue_field_mapping { - /* Type of mapping being performed. */ - enum rogue_map_type type; - - /* Index of the source operand/flag being mapped. */ - size_t index; - - /* List of ranges to perform mapping. */ - struct rogue_rangelist rangelist; - - /* Function used to encode the input into the value to be mapped. */ - field_encoder_t encoder_fn; -}; - -/** - * \brief Instruction encoding rule description. - */ -struct rogue_instr_encoding { - /* Number of bytes making up the base mask. */ - size_t num_bytes; - /* Base mask bytes. */ - uint8_t *bytes; - - /* Number of field mappings for this instruction. */ - size_t num_mappings; - /* Field mappings. */ - struct rogue_field_mapping *mappings; -}; - -static const -struct rogue_instr_encoding instr_encodings[ROGUE_OP_COUNT] = { - [ROGUE_OP_NOP] = { - .num_bytes = 8, - .bytes = (uint8_t []) { 0x04, 0x80, 0x6e, 0x00, 0xf2, 0xff, 0xff, 0xff }, - }, - - [ROGUE_OP_END_FRAG] = { - .num_bytes = 8, - .bytes = (uint8_t []) { 0x04, 0x80, 0xee, 0x00, 0xf2, 0xff, 0xff, 0xff }, - }, - - [ROGUE_OP_END_VERT] = { - .num_bytes = 8, - .bytes = (uint8_t []) { 0x44, 0xa0, 0x80, 0x05, 0x00, 0x00, 0x00, 0xff }, - }, - - [ROGUE_OP_WDF] = { - .num_bytes = 8, - .bytes = (uint8_t []) { 0x04, 0x80, 0x6a, 0xff, 0xf2, 0xff, 0xff, 0xff }, - .num_mappings = 1, - .mappings = (struct rogue_field_mapping []) { - { - .type = ROGUE_MAP_TYPE_OPERAND, - .index = 0, - .rangelist = { - .num_ranges = 1, - .ranges = (struct rogue_bitrange []) { - { .start = 47, .num = 1, }, - }, - }, - .encoder_fn = &rogue_encoder_drc, - }, - }, - }, - - [ROGUE_OP_PIX_ITER_W] = { - .num_bytes = 16, - .bytes = (uint8_t []) { 0x48, 0x20, 0xb0, 0x01, 0x80, 0x40, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xff, 0xf1, 0xff }, - .num_mappings = 6, - .mappings = (struct rogue_field_mapping []) { - /* Instruction flag mappings. */ - { - .type = ROGUE_MAP_TYPE_INSTR_FLAG, - .index = ROGUE_INSTR_FLAG_SAT, - .rangelist = { - .num_ranges = 1, - .ranges = (struct rogue_bitrange []) { - { .start = 100, .num = 1, }, - }, - }, - .encoder_fn = NULL, - }, - /* Operand mappings. */ - { - .type = ROGUE_MAP_TYPE_OPERAND, - .index = 0, - .rangelist = { - .num_ranges = 5, - .ranges = (struct rogue_bitrange []) { - { .start = 43, .num = 2, }, /* SB3(2..1) */ - { .start = 54, .num = 1, }, /* SB3(0) */ - { .start = 34, .num = 3, }, /* S3(10..8) */ - { .start = 41, .num = 2, }, /* S3(7..6) */ - { .start = 53, .num = 6, }, /* S3(5..0) */ - }, - }, - .encoder_fn = &rogue_encoder_reg_3_11, - }, - { - .type = ROGUE_MAP_TYPE_OPERAND, - .index = 1, - .rangelist = { - .num_ranges = 1, - .ranges = (struct rogue_bitrange []) { - { .start = 59, .num = 1, }, - }, - }, - .encoder_fn = &rogue_encoder_drc, - }, - { - .type = ROGUE_MAP_TYPE_OPERAND, - .index = 2, - .rangelist = { - .num_ranges = 6, - .ranges = (struct rogue_bitrange []) { - { .start = 59, .num = 1, }, /* SB0(2) */ - { .start = 76, .num = 1, }, /* SB0(1) */ - { .start = 94, .num = 1, }, /* SB0(0) */ - { .start = 57, .num = 1, }, /* S0(7) */ - { .start = 74, .num = 1, }, /* S0(6) */ - { .start = 93, .num = 6, }, /* S0(5..0) */ - }, - }, - .encoder_fn = &rogue_encoder_reg_3_8, - }, - { - .type = ROGUE_MAP_TYPE_OPERAND, - .index = 3, - .rangelist = { - .num_ranges = 4, - .ranges = (struct rogue_bitrange []) { - { .start = 63, .num = 1, }, /* SB2(2) */ - { .start = 71, .num = 2, }, /* SB2(1..0) */ - { .start = 62, .num = 2, }, /* S2(7..6) */ - { .start = 69, .num = 6, }, /* S2(5..0) */ - }, - }, - .encoder_fn = &rogue_encoder_reg_3_8, - }, - { - .type = ROGUE_MAP_TYPE_OPERAND, - .index = 4, - .rangelist = { - .num_ranges = 1, - .ranges = (struct rogue_bitrange []) { - { .start = 99, .num = 4, }, - }, - }, - .encoder_fn = &rogue_encoder_ls_1_16, - }, - }, - }, - - [ROGUE_OP_MAX] = { - .num_bytes = 16, - .bytes = (uint8_t []) { 0x68, 0x42, 0xd0, 0x3c, 0xfa, 0x10, 0x87, 0x80, 0xc0, 0x80, 0x10, 0x00, 0x32, 0x80, 0x00, 0xff }, - .num_mappings = 3, - .mappings = (struct rogue_field_mapping []) { - /* Operand mappings. */ - { - .type = ROGUE_MAP_TYPE_OPERAND, - .index = 0, - .rangelist = { - .num_ranges = 5, - .ranges = (struct rogue_bitrange []) { - { .start = 11, .num = 2, }, /* DBn(2..1) */ - { .start = 22, .num = 1, }, /* DBn(0) */ - { .start = 14, .num = 3, }, /* Dn(10..8) */ - { .start = 9, .num = 2, }, /* Dn(7..6) */ - { .start = 21, .num = 6, }, /* Dn(5..0) */ - }, - }, - .encoder_fn = &rogue_encoder_reg_3_11, - }, - { - .type = ROGUE_MAP_TYPE_OPERAND, - .index = 1, - .rangelist = { - .num_ranges = 7, - .ranges = (struct rogue_bitrange []) { - { .start = 43, .num = 1, }, /* SB0(2) */ - { .start = 52, .num = 1, }, /* SB0(1) */ - { .start = 70, .num = 1, }, /* SB0(0) */ - { .start = 47, .num = 3, }, /* S0(10..8) */ - { .start = 41, .num = 1, }, /* S0(7) */ - { .start = 50, .num = 1, }, /* S0(6) */ - { .start = 69, .num = 6, }, /* S0(5..0) */ - }, - }, - .encoder_fn = &rogue_encoder_reg_3_11, - }, - { - .type = ROGUE_MAP_TYPE_OPERAND, - .index = 2, - .rangelist = { - .num_ranges = 5, - .ranges = (struct rogue_bitrange []) { - { .start = 51, .num = 1, }, /* SB1(1) */ - { .start = 61, .num = 1, }, /* SB1(0) */ - { .start = 40, .num = 1, }, /* S1(7) */ - { .start = 49, .num = 2, }, /* S1(6..5) */ - { .start = 60, .num = 5, }, /* S1(4..0) */ - }, - }, - .encoder_fn = &rogue_encoder_reg_2_8, - }, - }, - }, - - [ROGUE_OP_MIN] = { - .num_bytes = 16, - .bytes = (uint8_t []) { 0x68, 0x42, 0xd0, 0x3c, 0xf0, 0x11, 0x87, 0x80, 0xc0, 0x80, 0x10, 0x00, 0x32, 0x80, 0x00, 0xff }, - .num_mappings = 3, - .mappings = (struct rogue_field_mapping []) { - /* Operand mappings. */ - { - .type = ROGUE_MAP_TYPE_OPERAND, - .index = 0, - .rangelist = { - .num_ranges = 5, - .ranges = (struct rogue_bitrange []) { - { .start = 11, .num = 2, }, /* DBn(2..1) */ - { .start = 22, .num = 1, }, /* DBn(0) */ - { .start = 14, .num = 3, }, /* Dn(10..8) */ - { .start = 9, .num = 2, }, /* Dn(7..6) */ - { .start = 21, .num = 6, }, /* Dn(5..0) */ - }, - }, - .encoder_fn = &rogue_encoder_reg_3_11, - }, - { - .type = ROGUE_MAP_TYPE_OPERAND, - .index = 1, - .rangelist = { - .num_ranges = 7, - .ranges = (struct rogue_bitrange []) { - { .start = 43, .num = 1, }, /* SB0(2) */ - { .start = 52, .num = 1, }, /* SB0(1) */ - { .start = 70, .num = 1, }, /* SB0(0) */ - { .start = 47, .num = 3, }, /* S0(10..8) */ - { .start = 41, .num = 1, }, /* S0(7) */ - { .start = 50, .num = 1, }, /* S0(6) */ - { .start = 69, .num = 6, }, /* S0(5..0) */ - }, - }, - .encoder_fn = &rogue_encoder_reg_3_11, - }, - { - .type = ROGUE_MAP_TYPE_OPERAND, - .index = 2, - .rangelist = { - .num_ranges = 5, - .ranges = (struct rogue_bitrange []) { - { .start = 51, .num = 1, }, /* SB1(1) */ - { .start = 61, .num = 1, }, /* SB1(0) */ - { .start = 40, .num = 1, }, /* S1(7) */ - { .start = 49, .num = 2, }, /* S1(6..5) */ - { .start = 60, .num = 5, }, /* S1(4..0) */ - }, - }, - .encoder_fn = &rogue_encoder_reg_2_8, - }, - }, - }, - - [ROGUE_OP_PACK_U8888] = { - .num_bytes = 16, - .bytes = (uint8_t []) { 0x58, 0x92, 0x06, 0x9c, 0x20, 0x80, 0x00, 0x00, 0x00, 0x2c, 0x80, 0x00, 0xf2, 0xff, 0xff, 0xff }, - .num_mappings = 2, - .mappings = (struct rogue_field_mapping []) { - /* Operand mappings. */ - { - .type = ROGUE_MAP_TYPE_OPERAND, - .index = 0, - .rangelist = { - .num_ranges = 5, - .ranges = (struct rogue_bitrange []) { - { .start = 35, .num = 2, }, /* DBn(2..1) */ - { .start = 46, .num = 1, }, /* DBn(0) */ - { .start = 38, .num = 3, }, /* Dn(10..8) */ - { .start = 33, .num = 2, }, /* Dn(7..6) */ - { .start = 45, .num = 6, }, /* Dn(5..0) */ - }, - }, - .encoder_fn = &rogue_encoder_reg_3_11, - }, - { - .type = ROGUE_MAP_TYPE_OPERAND, - .index = 1, - .rangelist = { - .num_ranges = 5, - .ranges = (struct rogue_bitrange []) { - { .start = 75, .num = 2, }, /* SB0(2..1) */ - { .start = 86, .num = 1, }, /* SB0(0) */ - { .start = 66, .num = 3, }, /* S0(10..8) */ - { .start = 73, .num = 2, }, /* S0(7..6) */ - { .start = 85, .num = 6, }, /* S0(5..0) */ - }, - }, - .encoder_fn = &rogue_encoder_reg_3_11, - }, - }, - }, - - [ROGUE_OP_MOV] = { - .num_bytes = 16, - .bytes = (uint8_t []) { 0x48, 0x42, 0xd0, 0x3f, 0x87, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0xf2, 0xff, 0xff, 0xff }, - .num_mappings = 3, - .mappings = (struct rogue_field_mapping []) { - /* Instruction flag mappings. */ - { - .type = ROGUE_MAP_TYPE_INSTR_FLAG, - .index = ROGUE_INSTR_FLAG_OLCHK, - .rangelist = { - .num_ranges = 1, - .ranges = (struct rogue_bitrange []) { - { .start = 115, .num = 1, }, - }, - }, - .encoder_fn = NULL, - }, - /* Operand mappings. */ - { - .type = ROGUE_MAP_TYPE_OPERAND, - .index = 0, - .rangelist = { - .num_ranges = 5, - .ranges = (struct rogue_bitrange []) { - { .start = 35, .num = 2, }, /* DBn(2..1) */ - { .start = 46, .num = 1, }, /* DBn(0) */ - { .start = 38, .num = 3, }, /* Dn(10..8) */ - { .start = 33, .num = 2, }, /* Dn(7..6) */ - { .start = 45, .num = 6, }, /* Dn(5..0) */ - }, - }, - .encoder_fn = &rogue_encoder_reg_3_11, - }, - { - .type = ROGUE_MAP_TYPE_OPERAND, - .index = 1, - .rangelist = { - .num_ranges = 5, - .ranges = (struct rogue_bitrange []) { - { .start = 75, .num = 2, }, /* SB0(2..1) */ - { .start = 86, .num = 1, }, /* SB0(0) */ - { .start = 66, .num = 3, }, /* S0(10..8) */ - { .start = 73, .num = 2, }, /* S0(7..6) */ - { .start = 85, .num = 6, }, /* S0(5..0) */ - }, - }, - .encoder_fn = &rogue_encoder_reg_3_11, - }, - }, - }, - - [ROGUE_OP_MOV_IMM] = { - .num_bytes = 16, - .bytes = (uint8_t []) { 0x88, 0x92, 0x40, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0xf2, 0xff, 0xff, 0xff }, - .num_mappings = 2, - .mappings = (struct rogue_field_mapping []) { - /* Operand mappings. */ - { - .type = ROGUE_MAP_TYPE_OPERAND, - .index = 0, - .rangelist = { - .num_ranges = 5, - .ranges = (struct rogue_bitrange []) { - { .start = 35, .num = 2, }, /* DBn(2..1) */ - { .start = 46, .num = 1, }, /* DBn(0) */ - { .start = 38, .num = 3, }, /* Dn(10..8) */ - { .start = 33, .num = 2, }, /* Dn(7..6) */ - { .start = 45, .num = 6, }, /* Dn(5..0) */ - }, - }, - .encoder_fn = &rogue_encoder_reg_3_11, - }, - { - .type = ROGUE_MAP_TYPE_OPERAND, - .index = 1, - .rangelist = { - .num_ranges = 4, - .ranges = (struct rogue_bitrange []) { - { .start = 71, .num = 8, }, /* imm(31:24) */ - { .start = 79, .num = 8, }, /* imm(23:16) */ - { .start = 87, .num = 8, }, /* imm(15:8) */ - { .start = 95, .num = 8, }, /* imm(7:0) */ - }, - }, - .encoder_fn = &rogue_encoder_imm, - }, - }, - }, - - [ROGUE_OP_FMA] = { - .num_bytes = 16, - .bytes = (uint8_t []) { 0x28, 0x02, 0xd0, 0x00, 0x80, 0x40, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0xff, 0xf1, 0xff }, - .num_mappings = 6, - .mappings = (struct rogue_field_mapping []) { - /* Instruction flag mappings. */ - { - .type = ROGUE_MAP_TYPE_INSTR_FLAG, - .index = ROGUE_INSTR_FLAG_SAT, - .rangelist = { - .num_ranges = 1, - .ranges = (struct rogue_bitrange []) { - { .start = 104, .num = 1, }, - }, - }, - .encoder_fn = NULL, - }, - { - .type = ROGUE_MAP_TYPE_INSTR_FLAG, - .index = ROGUE_INSTR_FLAG_LP, - .rangelist = { - .num_ranges = 1, - .ranges = (struct rogue_bitrange []) { - { .start = 100, .num = 1, }, - }, - }, - .encoder_fn = NULL, - }, - /* Operand mappings. */ - { - .type = ROGUE_MAP_TYPE_OPERAND, - .index = 0, - .rangelist = { - .num_ranges = 5, - .ranges = (struct rogue_bitrange []) { - { .start = 27, .num = 2, }, /* DBn(2..1) */ - { .start = 38, .num = 1, }, /* DBn(0) */ - { .start = 30, .num = 3, }, /* Dn(10..8) */ - { .start = 25, .num = 2, }, /* Dn(7..6) */ - { .start = 37, .num = 6, }, /* Dn(5..0) */ - }, - }, - .encoder_fn = &rogue_encoder_reg_3_11, - }, - { - .type = ROGUE_MAP_TYPE_OPERAND, - .index = 1, - .rangelist = { - .num_ranges = 6, - .ranges = (struct rogue_bitrange []) { - { .start = 59, .num = 1, }, /* SB0(2) */ - { .start = 76, .num = 1, }, /* SB0(1) */ - { .start = 94, .num = 1, }, /* SB0(0) */ - { .start = 57, .num = 1, }, /* S0(7) */ - { .start = 74, .num = 1, }, /* S0(6) */ - { .start = 93, .num = 6, }, /* S0(5..0) */ - }, - }, - .encoder_fn = &rogue_encoder_reg_3_8, - }, - { - .type = ROGUE_MAP_TYPE_OPERAND, - .index = 2, - .rangelist = { - .num_ranges = 5, - .ranges = (struct rogue_bitrange []) { - { .start = 75, .num = 1, }, /* SB1(1) */ - { .start = 85, .num = 1, }, /* SB1(0) */ - { .start = 56, .num = 1, }, /* S1(7) */ - { .start = 73, .num = 2, }, /* S1(6..5) */ - { .start = 84, .num = 5, }, /* S1(4..0) */ - }, - }, - .encoder_fn = &rogue_encoder_reg_2_8, - }, - { - .type = ROGUE_MAP_TYPE_OPERAND, - .index = 3, - .rangelist = { - .num_ranges = 4, - .ranges = (struct rogue_bitrange []) { - { .start = 63, .num = 1, }, /* SB2(2) */ - { .start = 71, .num = 2, }, /* SB2(1..0) */ - { .start = 62, .num = 2, }, /* S2(7..6) */ - { .start = 69, .num = 6, }, /* S2(5..0) */ - }, - }, - .encoder_fn = &rogue_encoder_reg_3_8, - }, - }, - }, - - [ROGUE_OP_MUL] = { - .num_bytes = 16, - .bytes = (uint8_t []) { 0x28, 0x02, 0x40, 0x80, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0xff, 0xf2, 0xff, 0xff, 0xff }, - .num_mappings = 5, - .mappings = (struct rogue_field_mapping []) { - /* Instruction flag mappings. */ - { - .type = ROGUE_MAP_TYPE_INSTR_FLAG, - .index = ROGUE_INSTR_FLAG_SAT, - .rangelist = { - .num_ranges = 1, - .ranges = (struct rogue_bitrange []) { - { .start = 108, .num = 1, }, - }, - }, - .encoder_fn = NULL, - }, - { - .type = ROGUE_MAP_TYPE_INSTR_FLAG, - .index = ROGUE_INSTR_FLAG_LP, - .rangelist = { - .num_ranges = 1, - .ranges = (struct rogue_bitrange []) { - { .start = 109, .num = 1, }, - }, - }, - .encoder_fn = NULL, - }, - /* Operand mappings. */ - { - .type = ROGUE_MAP_TYPE_OPERAND, - .index = 0, - .rangelist = { - .num_ranges = 5, - .ranges = (struct rogue_bitrange []) { - { .start = 43, .num = 2, }, /* DBn(2..1) */ - { .start = 54, .num = 1, }, /* DBn(0) */ - { .start = 46, .num = 3, }, /* Dn(10..8) */ - { .start = 41, .num = 2, }, /* Dn(7..6) */ - { .start = 53, .num = 6, }, /* Dn(5..0) */ - }, - }, - .encoder_fn = &rogue_encoder_reg_3_11, - }, - { - .type = ROGUE_MAP_TYPE_OPERAND, - .index = 1, - .rangelist = { - .num_ranges = 7, - .ranges = (struct rogue_bitrange []) { - { .start = 75, .num = 1, }, /* SB0(2) */ - { .start = 84, .num = 1, }, /* SB0(1) */ - { .start = 102, .num = 1, }, /* SB0(0) */ - { .start = 79, .num = 3, }, /* S0(10..8) */ - { .start = 73, .num = 1, }, /* S0(7) */ - { .start = 82, .num = 1, }, /* S0(6) */ - { .start = 101, .num = 6, }, /* S0(5..0) */ - }, - }, - .encoder_fn = &rogue_encoder_reg_3_11, - }, - { - .type = ROGUE_MAP_TYPE_OPERAND, - .index = 2, - .rangelist = { - .num_ranges = 5, - .ranges = (struct rogue_bitrange []) { - { .start = 83, .num = 1, }, /* SB1(1) */ - { .start = 93, .num = 1, }, /* SB1(0) */ - { .start = 72, .num = 1, }, /* S1(7) */ - { .start = 81, .num = 2, }, /* S1(6..5) */ - { .start = 92, .num = 5, }, /* S1(4..0) */ - }, - }, - .encoder_fn = &rogue_encoder_reg_2_8, - }, - }, - }, - - [ROGUE_OP_VTXOUT] = { - .num_bytes = 16, - .bytes = (uint8_t []) { 0x48, 0x20, 0x08, 0x00, 0x80, 0x00, 0x00, 0x00, 0x30, 0xff, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff }, - .num_mappings = 2, - .mappings = (struct rogue_field_mapping []) { - /* Operand mappings. */ - { - .type = ROGUE_MAP_TYPE_OPERAND, - .index = 0, - .rangelist = { - .num_ranges = 1, - .ranges = (struct rogue_bitrange []) { - { .start = 103, .num = 8, }, /* Immediate address. */ - }, - }, - .encoder_fn = &rogue_encoder_imm, - }, - { - .type = ROGUE_MAP_TYPE_OPERAND, - .index = 1, - .rangelist = { - .num_ranges = 5, - .ranges = (struct rogue_bitrange []) { - { .start = 83, .num = 2, }, /* SB0(2..1) */ - { .start = 94, .num = 1, }, /* SB0(0) */ - { .start = 74, .num = 3, }, /* S0(10..8) */ - { .start = 81, .num = 2, }, /* S0(7..6) */ - { .start = 93, .num = 6, }, /* S0(5..0) */ - }, - }, - .encoder_fn = &rogue_encoder_reg_3_11, - }, - }, - }, -}; - -/** - * \brief Applies a boolean flag encoding onto an instruction mask. - * - * \param[in] set Whether to set/unset the flag. - * \param[in] mapping The field mapping to apply. - * \param[in] instr_size The size of the instruction mask in bytes. - * \param[in] instr_bytes The instruction mask. - * \return true if encoding was successful. - */ -static bool rogue_encode_flag(bool set, - const struct rogue_field_mapping *mapping, - size_t instr_size, - uint8_t instr_bytes[instr_size]) +static void rogue_encode_instr_group_header(rogue_instr_group *group, + struct util_dynarray *binary) { - return rogue_distribute_value((uint64_t)set, - &mapping->rangelist, - instr_size, - instr_bytes); -} + rogue_instr_group_header_encoding h = { 0 }; -/** - * \brief Applies an operand encoding onto an instruction mask. - * - * \param[in] operand The operand to apply. - * \param[in] mapping The field mapping to apply. - * \param[in] instr_size The size of the instruction mask in bytes. - * \param[in] instr_bytes The instruction mask. - * \return true if encoding was successful. - */ -static bool rogue_encode_operand(const struct rogue_operand *operand, - const struct rogue_field_mapping *mapping, - size_t instr_size, - uint8_t instr_bytes[instr_size]) -{ - uint64_t value = 0U; + h.da = rogue_calc_da(group); + h.length = (group->size.total / 2) % 16; + h.ext = (group->size.header == 3); - switch (operand->type) { - case ROGUE_OPERAND_TYPE_REG_PIXEL_OUT: - CHECKF( - mapping->encoder_fn(&value, - 2, - rogue_encode_reg_bank(operand), - operand->reg.number + ROGUE_PIXEL_OUT_REG_OFFSET), - "Failed to encode pixel output register operand."); - break; - case ROGUE_OPERAND_TYPE_REG_INTERNAL: - CHECKF( - mapping->encoder_fn(&value, - 2, - rogue_encode_reg_bank(operand), - operand->reg.number + ROGUE_INTERNAL_REG_OFFSET), - "Failed to encode internal register operand."); - break; - case ROGUE_OPERAND_TYPE_REG_TEMP: - case ROGUE_OPERAND_TYPE_REG_COEFF: - case ROGUE_OPERAND_TYPE_REG_CONST: - case ROGUE_OPERAND_TYPE_REG_SHARED: - case ROGUE_OPERAND_TYPE_REG_VERTEX_IN: - CHECKF(mapping->encoder_fn(&value, - 2, - rogue_encode_reg_bank(operand), - operand->reg.number), - "Failed to encode register operand."); + rogue_ref *w0ref = rogue_instr_group_io_sel_ref(&group->io_sel, ROGUE_IO_W0); + rogue_ref *w1ref = rogue_instr_group_io_sel_ref(&group->io_sel, ROGUE_IO_W1); + + /* TODO: Update this - needs to be set for MOVMSK, and if instruction group + * READS OR WRITES to/from pixout regs. */ + h.olchk = rogue_ref_is_pixout(w0ref) || rogue_ref_is_pixout(w1ref); + h.w1p = !rogue_ref_is_null(w1ref); + h.w0p = !rogue_ref_is_null(w0ref); + + rogue_cc cc = { 0 }; + switch (group->header.exec_cond) { + case ROGUE_EXEC_COND_PE_TRUE: + cc._ = CC_PE_TRUE; break; - case ROGUE_OPERAND_TYPE_IMMEDIATE: - CHECKF(mapping->encoder_fn(&value, 1, operand->immediate.value), - "Failed to encode immediate operand."); + case ROGUE_EXEC_COND_P0_TRUE: + cc._ = CC_P0_TRUE; break; - case ROGUE_OPERAND_TYPE_DRC: - CHECKF(mapping->encoder_fn(&value, 1, (uint64_t)operand->drc.number), - "Failed to encode DRC operand."); + case ROGUE_EXEC_COND_PE_ANY: + cc._ = CC_PE_ANY; + break; + + case ROGUE_EXEC_COND_P0_FALSE: + cc._ = CC_P0_FALSE; break; default: - return false; + unreachable("Invalid condition code."); } - CHECKF(rogue_distribute_value(value, - &mapping->rangelist, - instr_size, - instr_bytes), - "Failed to distribute value."); + h.cc = cc.cc; + h.ccext = cc.ccext; - return true; -} + switch (group->header.alu) { + case ROGUE_ALU_MAIN: + h.alutype = ALUTYPE_MAIN; + /* TODO: Support multiple phase instructions. */ +#define P(type) BITFIELD64_BIT(ROGUE_INSTR_PHASE_##type) + if (group->header.phases & P(0)) + h.oporg = OPORG_P0; + if (group->header.phases & P(2_PCK) || group->header.phases & P(2_TST) || + group->header.phases & P(2_MOV)) + h.oporg = OPORG_P2; + if (group->header.phases & P(BACKEND)) + h.oporg = OPORG_BE; +#undef P + break; -/** - * \brief Applies operand and flag encodings to the base instruction bytes, then - * writes the result to file pointer "fp". - * - * \param[in] instr The instruction to be encoded. - * \param[in] fp The file pointer. - * \return true if encoding was successful. - */ -bool rogue_encode_instr(const struct rogue_instr *instr, FILE *fp) -{ - const struct rogue_instr_encoding *instr_encoding; - size_t instr_size; - uint8_t instr_bytes[ROGUE_MAX_INSTR_BYTES]; + case ROGUE_ALU_BITWISE: + h.alutype = ALUTYPE_BITWISE; +#define P(type) BITFIELD64_BIT(ROGUE_INSTR_PHASE_##type) + if (group->header.phases & P(0_BITMASK) || + group->header.phases & P(0_SHIFT1) || + group->header.phases & P(0_COUNT)) + h.oporg |= OPCNT_P0; + if (group->header.phases & P(1_LOGICAL)) + h.oporg |= OPCNT_P1; + if (group->header.phases & P(2_SHIFT2) || + group->header.phases & P(2_TEST)) + h.oporg |= OPCNT_P2; +#undef P + break; - ASSERT_OPCODE_RANGE(instr->opcode); - - instr_encoding = &instr_encodings[instr->opcode]; - - /* Set up base instruction bytes. */ - instr_size = instr_encoding->num_bytes; - assert(instr_size <= ARRAY_SIZE(instr_bytes)); - memcpy(instr_bytes, instr_encoding->bytes, instr_size); - - /* Encode the operands and flags. */ - for (size_t u = 0U; u < instr_encoding->num_mappings; ++u) { - const struct rogue_field_mapping *mapping = &instr_encoding->mappings[u]; - - switch (mapping->type) { - case ROGUE_MAP_TYPE_INSTR_FLAG: { - uint64_t flag = rogue_onehot(mapping->index); - CHECKF(rogue_encode_flag(!!(instr->flags & flag), - mapping, - instr_size, - instr_bytes), - "Failed to encode instruction flag."); + case ROGUE_ALU_CONTROL: + h.alutype = ALUTYPE_CONTROL; + const rogue_instr *instr = group->instrs[ROGUE_INSTR_PHASE_CTRL]; + const rogue_ctrl_instr *ctrl = rogue_instr_as_ctrl(instr); + switch (ctrl->op) { + case ROGUE_CTRL_OP_WDF: + h.ctrlop = CTRLOP_WDF; + h.miscctl = rogue_ref_get_drc_index(&ctrl->src[0].ref); break; - } - case ROGUE_MAP_TYPE_OPERAND_FLAG: - return false; - - case ROGUE_MAP_TYPE_OPERAND: { - size_t operand_index = mapping->index; - CHECKF(rogue_encode_operand(&instr->operands[operand_index], - mapping, - instr_size, - instr_bytes), - "Failed to encode instruction operand."); + case ROGUE_CTRL_OP_NOP: + h.ctrlop = CTRLOP_NOP; + h.miscctl = rogue_ctrl_op_mod_is_set(ctrl, ROGUE_CTRL_OP_MOD_END); break; - } default: - return false; + unreachable("Invalid ctrl op."); + } + break; + + default: + unreachable("Invalid instruction group ALU."); + } + + if (group->header.alu != ROGUE_ALU_CONTROL) { + h.end = group->header.end; + /* h.crel = ; */ /* Unused for now */ + /* h.atom = ; */ /* Unused for now */ + h.rpt = group->header.repeat ? group->header.repeat - 1 : 0; + } + + util_dynarray_append_mem(binary, group->size.header, &h); +} + +typedef union rogue_instr_encoding { + rogue_alu_instr_encoding alu; + rogue_backend_instr_encoding backend; + rogue_ctrl_instr_encoding ctrl; +} PACKED rogue_instr_encoding; + +#define SM(src_mod) ROGUE_ALU_SRC_MOD_##src_mod +#define DM(dst_mod) ROGUE_ALU_DST_MOD_##dst_mod +#define OM(op_mod) ROGUE_ALU_OP_MOD_##op_mod +static void rogue_encode_alu_instr(const rogue_alu_instr *alu, + unsigned instr_size, + rogue_instr_encoding *instr_encoding) +{ + switch (alu->op) { + case ROGUE_ALU_OP_MBYP: + instr_encoding->alu.op = ALUOP_SNGL; + instr_encoding->alu.sngl.snglop = SNGLOP_BYP; + + if (instr_size == 2) { + instr_encoding->alu.sngl.ext0 = 1; + instr_encoding->alu.sngl.mbyp.s0neg = + rogue_alu_src_mod_is_set(alu, 0, SM(NEG)); + instr_encoding->alu.sngl.mbyp.s0abs = + rogue_alu_src_mod_is_set(alu, 0, SM(ABS)); + } + break; + + case ROGUE_ALU_OP_FMUL: + instr_encoding->alu.op = ALUOP_FMUL; + instr_encoding->alu.fmul.lp = rogue_alu_op_mod_is_set(alu, OM(LP)); + instr_encoding->alu.fmul.sat = rogue_alu_op_mod_is_set(alu, OM(SAT)); + instr_encoding->alu.fmul.s0neg = + rogue_alu_src_mod_is_set(alu, 0, SM(NEG)); + instr_encoding->alu.fmul.s0abs = + rogue_alu_src_mod_is_set(alu, 0, SM(ABS)); + instr_encoding->alu.fmul.s1abs = + rogue_alu_src_mod_is_set(alu, 1, SM(ABS)); + instr_encoding->alu.fmul.s0flr = + rogue_alu_src_mod_is_set(alu, 0, SM(FLR)); + break; + + case ROGUE_ALU_OP_FMAD: + instr_encoding->alu.op = ALUOP_FMAD; + instr_encoding->alu.fmad.s0neg = + rogue_alu_src_mod_is_set(alu, 0, SM(NEG)); + instr_encoding->alu.fmad.s0abs = + rogue_alu_src_mod_is_set(alu, 0, SM(ABS)); + instr_encoding->alu.fmad.s2neg = + rogue_alu_src_mod_is_set(alu, 2, SM(NEG)); + instr_encoding->alu.fmad.sat = rogue_alu_op_mod_is_set(alu, OM(SAT)); + + if (instr_size == 2) { + instr_encoding->alu.fmad.ext = 1; + instr_encoding->alu.fmad.lp = rogue_alu_op_mod_is_set(alu, OM(LP)); + instr_encoding->alu.fmad.s1abs = + rogue_alu_src_mod_is_set(alu, 1, SM(ABS)); + instr_encoding->alu.fmad.s1neg = + rogue_alu_src_mod_is_set(alu, 1, SM(NEG)); + instr_encoding->alu.fmad.s2flr = + rogue_alu_src_mod_is_set(alu, 2, SM(FLR)); + instr_encoding->alu.fmad.s2abs = + rogue_alu_src_mod_is_set(alu, 2, SM(ABS)); + } + break; + + case ROGUE_ALU_OP_PCK_U8888: + instr_encoding->alu.op = ALUOP_SNGL; + instr_encoding->alu.sngl.snglop = SNGLOP_PCK; + instr_encoding->alu.sngl.ext0 = 1; + + instr_encoding->alu.sngl.pck.pck.prog = 0; + instr_encoding->alu.sngl.pck.pck.rtz = + rogue_alu_op_mod_is_set(alu, OM(ROUNDZERO)); + instr_encoding->alu.sngl.pck.pck.scale = + rogue_alu_op_mod_is_set(alu, OM(SCALE)); + instr_encoding->alu.sngl.pck.pck.format = PCK_FMT_U8888; + break; + + default: + unreachable("Invalid alu op."); + } +} +#undef OM +#undef DM +#undef SM + +#define OM(op_mod) BITFIELD64_BIT(ROGUE_BACKEND_OP_MOD_##op_mod) +static void rogue_encode_backend_instr(const rogue_backend_instr *backend, + unsigned instr_size, + rogue_instr_encoding *instr_encoding) +{ + switch (backend->op) { + case ROGUE_BACKEND_OP_FITRP_PIXEL: + instr_encoding->backend.op = BACKENDOP_FITR; + instr_encoding->backend.fitr.p = 1; + instr_encoding->backend.fitr.drc = + rogue_ref_get_drc_index(&backend->src[0].ref); + instr_encoding->backend.fitr.mode = FITR_MODE_PIXEL; + instr_encoding->backend.fitr.sat = + rogue_backend_op_mod_is_set(backend, OM(SAT)); + instr_encoding->backend.fitr.count = + rogue_ref_get_val(&backend->src[3].ref); + break; + + case ROGUE_BACKEND_OP_UVSW_WRITE: + instr_encoding->backend.op = BACKENDOP_UVSW; + instr_encoding->backend.uvsw.writeop = UVSW_WRITEOP_WRITE; + instr_encoding->backend.uvsw.imm = 1; + instr_encoding->backend.uvsw.imm_src.imm_addr = + rogue_ref_get_reg_index(&backend->dst[0].ref); + break; + + case ROGUE_BACKEND_OP_UVSW_EMIT: + instr_encoding->backend.op = BACKENDOP_UVSW; + instr_encoding->backend.uvsw.writeop = UVSW_WRITEOP_EMIT; + break; + + case ROGUE_BACKEND_OP_UVSW_ENDTASK: + instr_encoding->backend.op = BACKENDOP_UVSW; + instr_encoding->backend.uvsw.writeop = UVSW_WRITEOP_END; + break; + + case ROGUE_BACKEND_OP_UVSW_EMITTHENENDTASK: + instr_encoding->backend.op = BACKENDOP_UVSW; + instr_encoding->backend.uvsw.writeop = UVSW_WRITEOP_EMIT_END; + break; + + case ROGUE_BACKEND_OP_UVSW_WRITETHENEMITTHENENDTASK: + instr_encoding->backend.op = BACKENDOP_UVSW; + instr_encoding->backend.uvsw.writeop = UVSW_WRITEOP_WRITE_EMIT_END; + instr_encoding->backend.uvsw.imm = 1; + instr_encoding->backend.uvsw.imm_src.imm_addr = + rogue_ref_get_reg_index(&backend->dst[0].ref); + break; + + default: + unreachable("Invalid backend op."); + } +} +#undef OM + +static void rogue_encode_ctrl_instr(const rogue_ctrl_instr *ctrl, + unsigned instr_size, + rogue_instr_encoding *instr_encoding) +{ + /* Only some control instructions have additional bytes. */ + switch (ctrl->op) { + case ROGUE_CTRL_OP_NOP: + memset(&instr_encoding->ctrl.nop, 0, sizeof(instr_encoding->ctrl.nop)); + break; + + default: + unreachable("Invalid ctrl op."); + } +} + +/* TODO: Add p2end where required. */ +static void rogue_encode_instr_group_instrs(rogue_instr_group *group, + struct util_dynarray *binary) +{ + rogue_instr_encoding instr_encoding; + + /* Reverse order for encoding. */ + rogue_foreach_phase_in_set_rev (p, group->header.phases) { + if (!group->size.instrs[p]) + continue; + + memset(&instr_encoding, 0, sizeof(instr_encoding)); + + const rogue_instr *instr = group->instrs[p]; + switch (instr->type) { + case ROGUE_INSTR_TYPE_ALU: + rogue_encode_alu_instr(rogue_instr_as_alu(instr), + group->size.instrs[p], + &instr_encoding); + break; + + case ROGUE_INSTR_TYPE_BACKEND: + rogue_encode_backend_instr(rogue_instr_as_backend(instr), + group->size.instrs[p], + &instr_encoding); + break; + + case ROGUE_INSTR_TYPE_CTRL: + rogue_encode_ctrl_instr(rogue_instr_as_ctrl(instr), + group->size.instrs[p], + &instr_encoding); + break; + + default: + unreachable("Invalid instruction type."); + } + + util_dynarray_append_mem(binary, group->size.instrs[p], &instr_encoding); + } +} + +static void rogue_encode_source_map(const rogue_instr_group *group, + bool upper_srcs, + rogue_source_map_encoding *e) +{ + unsigned base = upper_srcs ? 3 : 0; + unsigned index = upper_srcs ? group->encode_info.upper_src_index + : group->encode_info.lower_src_index; + const rogue_reg_src_info *info = upper_srcs + ? &rogue_reg_upper_src_infos[index] + : &rogue_reg_lower_src_infos[index]; + const rogue_instr_group_io_sel *io_sel = &group->io_sel; + + rogue_mux mux = { 0 }; + + if (!upper_srcs && rogue_ref_is_io(&io_sel->iss[0])) { + switch (io_sel->iss[0].io) { + case ROGUE_IO_S0: + mux._ = IS0_S0; + break; + case ROGUE_IO_S3: + mux._ = IS0_S3; + break; + case ROGUE_IO_S4: + mux._ = IS0_S4; + break; + case ROGUE_IO_S5: + mux._ = IS0_S5; + break; + case ROGUE_IO_S1: + mux._ = IS0_S1; + break; + case ROGUE_IO_S2: + mux._ = IS0_S2; + break; + + default: + unreachable("IS0 set to invalid value."); } } - CHECKF(fwrite(instr_bytes, 1, instr_size, fp) == instr_size, - "Failed to write encoded instruction bytes."); - fflush(fp); + rogue_sbA sbA = { 0 }; + rogue_sA sA = { 0 }; - return true; + if (!rogue_ref_is_null(&io_sel->srcs[base + 0])) { + sbA._ = rogue_reg_bank_encoding( + rogue_ref_get_reg_class(&io_sel->srcs[base + 0])); + sA._ = rogue_ref_get_reg_index(&io_sel->srcs[base + 0]); + } + + rogue_sbB sbB = { 0 }; + rogue_sB sB = { 0 }; + + if (!rogue_ref_is_null(&io_sel->srcs[base + 1])) { + sbB._ = rogue_reg_bank_encoding( + rogue_ref_get_reg_class(&io_sel->srcs[base + 1])); + sB._ = rogue_ref_get_reg_index(&io_sel->srcs[base + 1]); + } + + rogue_sbC sbC = { 0 }; + rogue_sC sC = { 0 }; + + if (!rogue_ref_is_null(&io_sel->srcs[base + 2])) { + sbC._ = rogue_reg_bank_encoding( + rogue_ref_get_reg_class(&io_sel->srcs[base + 2])); + sC._ = rogue_ref_get_reg_index(&io_sel->srcs[base + 2]); + } + + /* Byte 0 is common for all encodings. */ + e->sbA_0 = sbA._0; + e->sA_5_0 = sA._5_0; + + switch (info->num_srcs) { + case 1: + switch (info->bytes) { + case 3: + /* Byte 1 */ + assert(!upper_srcs || !mux._1_0); + + e->sA_1.mux_1_0 = mux._1_0; + e->sA_1.sbA_2_1 = sbA._2_1; + e->sA_1.sA_7_6 = sA._7_6; + + /* Byte 2 */ + e->sA_2.sA_10_8 = sA._10_8; + + e->ext0 = 1; + FALLTHROUGH; + + case 1: + break; + + default: + unreachable("Invalid source/bytes combination."); + } + break; + + case 2: + e->ext0 = 1; + e->sel = 1; + switch (info->bytes) { + case 4: + /* Byte 3 */ + assert(!upper_srcs || !mux._2); + + e->sB_3.sA_10_8 = sA._10_8; + e->sB_3.mux_2 = mux._2; + e->sB_3.sbA_2 = sbA._2; + e->sB_3.sA_7 = sA._7; + e->sB_3.sB_7 = sB._7; + + e->ext2 = 1; + FALLTHROUGH; + + case 3: + /* Byte 2 */ + assert(!upper_srcs || !mux._1_0); + + e->mux_1_0 = mux._1_0; + e->sbA_1 = sbA._1; + e->sbB_1 = sbB._1; + e->sA_6 = sA._6; + e->sB_6_5 = sB._6_5; + + e->ext1 = 1; + FALLTHROUGH; + + case 2: + /* Byte 1 */ + e->sbB_0 = sbB._0; + e->sB_4_0 = sB._4_0; + break; + + default: + unreachable("Invalid source/bytes combination."); + } + break; + + case 3: + e->ext0 = 1; + e->ext1 = 1; + switch (info->bytes) { + case 6: + /* Byte 5 */ + assert(!upper_srcs || !sC._10_8); + + e->sC_5.sC_10_8 = sC._10_8; + e->sC_5.sA_10_8 = sA._10_8; + + e->sC_4.ext4 = 1; + FALLTHROUGH; + + case 5: + /* Byte 4 */ + assert(!upper_srcs || !mux._2); + assert(!upper_srcs || !sbC._2); + + e->sC_4.sbC_2 = sbC._2; + e->sC_4.sC_7_6 = sC._7_6; + e->sC_4.mux_2 = mux._2; + e->sC_4.sbA_2 = sbA._2; + e->sC_4.sA_7 = sA._7; + e->sC_4.sB_7 = sB._7; + + e->ext2 = 1; + FALLTHROUGH; + + case 4: + /* Byte 1 */ + e->sbB_0 = sbB._0; + e->sB_4_0 = sB._4_0; + + /* Byte 2 */ + assert(!upper_srcs || !mux._1_0); + + e->mux_1_0 = mux._1_0; + e->sbA_1 = sbA._1; + e->sbB_1 = sbB._1; + e->sA_6 = sA._6; + e->sB_6_5 = sB._6_5; + + /* Byte 3 */ + e->sbC_1_0 = sbC._1_0; + e->sC_5_0 = sC._5_0; + break; + + default: + unreachable("Invalid source/bytes combination."); + } + break; + + default: + unreachable("Invalid source/bytes combination."); + } } -/** - * \brief Encodes each instruction in "shader", writing the output to "fp". - * - * \param[in] shader The shader to be encoded. - * \param[in] fp The file pointer. - * \return true if encoding was successful. - */ -bool rogue_encode_shader(const struct rogue_shader *shader, FILE *fp) +static void rogue_encode_dest_map(const rogue_instr_group *group, + rogue_dest_map_encoding *e) { - long bytes_written; + const rogue_reg_dst_info *info = + &rogue_reg_dst_infos[group->encode_info.dst_index]; + const rogue_instr_group_io_sel *io_sel = &group->io_sel; - /* Encode each instruction. */ - foreach_instr (instr, &shader->instr_list) - CHECKF(rogue_encode_instr(instr, fp), "Failed to encode instruction."); + unsigned num_dsts = !rogue_ref_is_null(&io_sel->dsts[0]) + + !rogue_ref_is_null(&io_sel->dsts[1]); - /* Pad end of shader if required. */ - bytes_written = ftell(fp); - if (bytes_written <= 0) - return false; + switch (num_dsts) { + case 1: { + const rogue_ref *dst_ref = !rogue_ref_is_null(&io_sel->dsts[0]) + ? &io_sel->dsts[0] + : &io_sel->dsts[1]; - /* FIXME: Figure out the define for alignment of 16. */ - for (size_t u = 0; u < (bytes_written % 16); ++u) - fputc(0xff, fp); + rogue_dbN dbN = { ._ = rogue_reg_bank_encoding( + rogue_ref_get_reg_class(dst_ref)) }; + rogue_dN dN = { ._ = rogue_ref_get_reg_index(dst_ref) }; - return true; + switch (info->bytes) { + case 2: + e->dN_10_8 = dN._10_8; + e->dbN_2_1 = dbN._2_1; + e->dN_7_6 = dN._7_6; + + e->ext0 = 1; + FALLTHROUGH; + + case 1: + e->dbN_0 = dbN._0; + e->dN_5_0 = dN._5_0; + break; + + default: + unreachable("Invalid dest/bytes combination."); + } + break; + } + case 2: { + rogue_db0 db0 = { ._ = rogue_reg_bank_encoding( + rogue_ref_get_reg_class(&io_sel->dsts[0])) }; + rogue_d0 d0 = { ._ = rogue_ref_get_reg_index(&io_sel->dsts[0]) }; + rogue_db1 db1 = { ._ = rogue_reg_bank_encoding( + rogue_ref_get_reg_class(&io_sel->dsts[1])) }; + rogue_d1 d1 = { ._ = rogue_ref_get_reg_index(&io_sel->dsts[1]) }; + + switch (info->bytes) { + case 4: + e->d1_10_8 = d1._10_8; + e->d0_10_8 = d0._10_8; + + e->ext2 = 1; + FALLTHROUGH; + + case 3: + e->db1_2_1 = db1._2_1; + e->d1_7_6 = d1._7_6; + e->db0_2_1 = db0._2_1; + e->d0_7 = d0._7; + + e->ext1 = 1; + FALLTHROUGH; + + case 2: + e->db0_0 = db0._0; + e->d0_6_0 = d0._6_0; + + e->db1_0 = db1._0; + e->d1_5_0 = d1._5_0; + break; + + default: + unreachable("Invalid dest/bytes combination."); + } + } break; + + default: + unreachable("Invalid dest/bytes combination."); + } +} + +static void rogue_encode_iss_map(const rogue_instr_group *group, + rogue_iss_encoding *e) +{ + const rogue_instr_group_io_sel *io_sel = &group->io_sel; + + if (rogue_ref_is_io(&io_sel->iss[1])) + switch (rogue_ref_get_io(&io_sel->iss[1])) { + case ROGUE_IO_FT0: + e->is1 = IS1_FT0; + break; + case ROGUE_IO_FTE: + e->is1 = IS1_FTE; + break; + + default: + unreachable("Invalid setting for IS1."); + } + + if (rogue_ref_is_io(&io_sel->iss[2])) + switch (rogue_ref_get_io(&io_sel->iss[2])) { + case ROGUE_IO_FT1: + e->is2 = IS2_FT1; + break; + case ROGUE_IO_FTE: + e->is2 = IS2_FTE; + break; + + default: + unreachable("Invalid setting for IS2."); + } + + if (rogue_ref_is_io(&io_sel->iss[3])) + switch (rogue_ref_get_io(&io_sel->iss[3])) { + case ROGUE_IO_FT0: + e->is3 = IS3_FT0; + break; + case ROGUE_IO_FT1: + e->is3 = IS3_FT1; + break; + case ROGUE_IO_S2: + e->is3 = IS3_S2; + break; + case ROGUE_IO_FTE: + e->is3 = IS3_FTE; + break; + + default: + unreachable("Invalid setting for IS3."); + } + + if (rogue_ref_is_io(&io_sel->iss[4])) + switch (rogue_ref_get_io(&io_sel->iss[4])) { + case ROGUE_IO_FT0: + e->is4 = IS4_FT0; + break; + case ROGUE_IO_FT1: + e->is4 = IS4_FT1; + break; + case ROGUE_IO_FT2: + e->is4 = IS4_FT2; + break; + case ROGUE_IO_FTE: + e->is4 = IS4_FTE; + break; + + default: + unreachable("Invalid setting for IS4."); + } + + if (rogue_ref_is_io(&io_sel->iss[5])) + switch (rogue_ref_get_io(&io_sel->iss[5])) { + case ROGUE_IO_FT0: + e->is5 = IS5_FT0; + break; + case ROGUE_IO_FT1: + e->is5 = IS5_FT1; + break; + case ROGUE_IO_FT2: + e->is5 = IS5_FT2; + break; + case ROGUE_IO_FTE: + e->is5 = IS5_FTE; + break; + + default: + unreachable("Invalid setting for IS5."); + } +} + +static void rogue_encode_instr_group_io(const rogue_instr_group *group, + struct util_dynarray *binary) +{ + if (group->size.lower_srcs) { + rogue_source_map_encoding lower_srcs = { 0 }; + rogue_encode_source_map(group, false, &lower_srcs); + util_dynarray_append_mem(binary, group->size.lower_srcs, &lower_srcs); + } + + if (group->size.upper_srcs) { + rogue_source_map_encoding upper_srcs = { 0 }; + rogue_encode_source_map(group, true, &upper_srcs); + util_dynarray_append_mem(binary, group->size.upper_srcs, &upper_srcs); + } + + if (group->size.iss) { + rogue_iss_encoding internal_src_sel = { 0 }; + rogue_encode_iss_map(group, &internal_src_sel); + util_dynarray_append_mem(binary, group->size.iss, &internal_src_sel); + } + + if (group->size.dsts) { + rogue_dest_map_encoding dests = { 0 }; + rogue_encode_dest_map(group, &dests); + util_dynarray_append_mem(binary, group->size.dsts, &dests); + } +} + +static void rogue_encode_instr_group_padding(const rogue_instr_group *group, + struct util_dynarray *binary) +{ + if (group->size.word_padding) + util_dynarray_append(binary, uint8_t, 0xff); + + if (group->size.align_padding) { + assert(!(group->size.align_padding % 2)); + unsigned align_words = group->size.align_padding / 2; + util_dynarray_append(binary, uint8_t, 0xf0 | align_words); + for (unsigned u = 0; u < group->size.align_padding - 1; ++u) + util_dynarray_append(binary, uint8_t, 0xff); + } +} + +static void rogue_encode_instr_group(rogue_instr_group *group, + struct util_dynarray *binary) +{ + rogue_encode_instr_group_header(group, binary); + rogue_encode_instr_group_instrs(group, binary); + rogue_encode_instr_group_io(group, binary); + rogue_encode_instr_group_padding(group, binary); +} + +PUBLIC +void rogue_encode_shader(UNUSED rogue_build_ctx *ctx, + rogue_shader *shader, + struct util_dynarray *binary) +{ + if (!shader->is_grouped) + unreachable("Can't encode shader with ungrouped instructions."); + + util_dynarray_init(binary, shader); + + rogue_foreach_instr_group_in_shader (group, shader) + rogue_encode_instr_group(group, binary); } diff --git a/src/imagination/rogue/rogue_encode.h b/src/imagination/rogue/rogue_encode.h deleted file mode 100644 index 0305e372c08..00000000000 --- a/src/imagination/rogue/rogue_encode.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright © 2022 Imagination Technologies Ltd. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef ROGUE_ENCODE_H -#define ROGUE_ENCODE_H - -#include -#include - -#include "util/macros.h" - -struct rogue_instr; -struct rogue_shader; - -PUBLIC -bool rogue_encode_instr(const struct rogue_instr *instr, FILE *fp); - -PUBLIC -bool rogue_encode_shader(const struct rogue_shader *shader, FILE *fp); - -#endif /* ROGUE_ENCODE_H */ diff --git a/src/imagination/rogue/rogue_encoders.c b/src/imagination/rogue/rogue_encoders.c deleted file mode 100644 index 1da79903a77..00000000000 --- a/src/imagination/rogue/rogue_encoders.c +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright © 2022 Imagination Technologies Ltd. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include - -#include "rogue_encoders.h" -#include "rogue_util.h" -#include "util/bitscan.h" - -/** - * \brief Passes the input value through unchanged. - * - * \param[in] value Pointer to the destination value. - * \param[in] inputs Number of inputs provided. - * \param[in] ... Input value(s). - * \return true if encoding was successful. - */ -bool rogue_encoder_pass(uint64_t *value, size_t inputs, ...) -{ - va_list args; - - assert(inputs == 1); - - va_start(args, inputs); - *value = va_arg(args, uint64_t); - va_end(args); - - return true; -} - -/** - * \brief Encoder for DRC values. - * - * \sa #rogue_encoder_pass() - * - * \param[in] value Pointer to the destination value. - * \param[in] inputs Number of inputs provided. - * \param[in] ... Input value(s). - * \return true if encoding was successful. - */ -bool rogue_encoder_drc(uint64_t *value, size_t inputs, ...) - __attribute__((alias("rogue_encoder_pass"))); - -/** - * \brief Encoder for immediate values. - * - * \sa #rogue_encoder_pass() - * - * \param[in] value Pointer to the destination value. - * \param[in] inputs Number of inputs provided. - * \param[in] ... Input value(s). - * \return true if encoding was successful. - */ -bool rogue_encoder_imm(uint64_t *value, size_t inputs, ...) - __attribute__((alias("rogue_encoder_pass"))); - -/** - * \brief Encodes input ranges {1..15 -> 1-15} and {16 -> 0}. - * - * The input should be in the range 1-16; the function represents 1-15 normally - * and represents 16 by 0. - * - * \param[in] value Pointer to the destination value. - * \param[in] inputs Number of inputs provided. - * \param[in] ... Input value(s). - * \return true if encoding was successful. - */ -bool rogue_encoder_ls_1_16(uint64_t *value, size_t inputs, ...) -{ - va_list args; - uint64_t input; - - assert(inputs == 1); - - va_start(args, inputs); - input = va_arg(args, uint64_t); - va_end(args); - - /* Validate the input range. */ - if (!input || input > 16) { - *value = UINT64_MAX; - return false; - } - - *value = input % 16; - - return true; -} - -/** - * \brief Encodes registers according to the number of bits needed to specify - * the bank number and register number. - * - * \param[in] value Pointer to the destination value. - * \param[in] bank_bits The number of bits used to represent the register bank. - * \param[in] bank the register bank - * \param[in] num_bits The number of bits used to represent the register number. - * \param[in] num The register number. - * \return true if encoding was successful. - */ -static bool rogue_encoder_reg(uint64_t *value, - size_t bank_bits, - size_t bank, - size_t num_bits, - size_t num) -{ - /* Verify "num" fits in "num_bits" and "bank" fits in "bank_bits". */ - assert(util_last_bit64(num) <= num_bits); - assert(util_last_bit64(bank) <= bank_bits); - - *value = num; - *value |= (bank << num_bits); - - return true; -} - -/** - * \brief Macro to define the rogue_encoder_reg variants. - */ -#define ROGUE_ENCODER_REG_VARIANT(bank_bits, num_bits) \ - bool rogue_encoder_reg_##bank_bits##_##num_bits(uint64_t *value, \ - size_t inputs, \ - ...) \ - { \ - va_list args; \ - size_t bank; \ - size_t num; \ - assert(inputs == 2); \ - va_start(args, inputs); \ - bank = va_arg(args, size_t); \ - num = va_arg(args, size_t); \ - va_end(args); \ - return rogue_encoder_reg(value, bank_bits, bank, num_bits, num); \ - } - -ROGUE_ENCODER_REG_VARIANT(2, 8) -ROGUE_ENCODER_REG_VARIANT(3, 8) -ROGUE_ENCODER_REG_VARIANT(3, 11) - -#undef ROGUE_ENCODER_REG_VARIANT diff --git a/src/imagination/rogue/rogue_info.c b/src/imagination/rogue/rogue_info.c new file mode 100644 index 00000000000..9aa7989bf5c --- /dev/null +++ b/src/imagination/rogue/rogue_info.c @@ -0,0 +1,443 @@ +/* + * Copyright © 2022 Imagination Technologies Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "rogue.h" + +/** + * \file rogue_info.c + * + * \brief Contains information and definitions for defined types and structures. + */ + +/* TODO: Adjust according to core configurations. */ +/* TODO: Remaining restrictions, e.g. some registers are only + * usable by a particular instruction (vertex output) etc. */ +#define S(n) BITFIELD64_BIT(ROGUE_IO_S##n - 1) +const rogue_reg_info rogue_reg_infos[ROGUE_REG_CLASS_COUNT] = { + [ROGUE_REG_CLASS_INVALID] = { .name = "!INVALID!", .str = "!INVALID!", }, + [ROGUE_REG_CLASS_SSA] = { .name = "ssa", .str = "R", }, + [ROGUE_REG_CLASS_TEMP] = { .name = "temp", .str = "r", .num = 248, }, + [ROGUE_REG_CLASS_COEFF] = { .name = "coeff", .str = "cf", .num = 4096, .supported_io_srcs = S(0) | S(2) | S(3), }, + [ROGUE_REG_CLASS_SHARED] = { .name = "shared", .str = "sh", .num = 4096, .supported_io_srcs = S(0) | S(2) | S(3), }, + [ROGUE_REG_CLASS_SPECIAL] = { .name = "special", .str = "sr", .num = 240, }, /* TODO NEXT: Only S1, S2, S4. */ + [ROGUE_REG_CLASS_INTERNAL] = { .name = "internal", .str = "i", .num = 8, }, + [ROGUE_REG_CLASS_CONST] = { .name = "const", .str = "sc", .num = 240, }, + [ROGUE_REG_CLASS_PIXOUT] = { .name = "pixout", .str = "po", .num = 8, .supported_io_srcs = S(0) | S(2) | S(3), }, + [ROGUE_REG_CLASS_VTXIN] = { .name = "vtxin", .str = "vi", .num = 248, }, + [ROGUE_REG_CLASS_VTXOUT] = { .name = "vtxout", .str = "vo", .num = 256, }, +}; +#undef S + +const rogue_regalloc_info regalloc_info[ROGUE_REGALLOC_CLASS_COUNT] = { + [ROGUE_REGALLOC_CLASS_TEMP_1] = { .class = ROGUE_REG_CLASS_TEMP, .stride = 1, }, + [ROGUE_REGALLOC_CLASS_TEMP_4] = { .class = ROGUE_REG_CLASS_TEMP, .stride = 4, }, +}; + +const rogue_reg_dst_info rogue_reg_dst_infos[ROGUE_REG_DST_VARIANTS] = { + { + .num_dsts = 1, + .bank_bits = { 1 }, + .index_bits = { 6 }, + .bytes = 1, + }, + { + .num_dsts = 1, + .bank_bits = { 3 }, + .index_bits = { 11 }, + .bytes = 2, + }, + { + .num_dsts = 2, + .bank_bits = { 1, 1 }, + .index_bits = { 7, 6 }, + .bytes = 2, + }, + { + .num_dsts = 2, + .bank_bits = { 3, 3 }, + .index_bits = { 8, 8 }, + .bytes = 3, + }, + { + .num_dsts = 2, + .bank_bits = { 3, 3 }, + .index_bits = { 11, 11 }, + .bytes = 4, + }, +}; + +const rogue_reg_src_info rogue_reg_lower_src_infos[ROGUE_REG_SRC_VARIANTS] = { + { + .num_srcs = 1, + .mux_bits = 0, + .bank_bits = { 1 }, + .index_bits = { 6 }, + .bytes = 1, + }, + { + .num_srcs = 1, + .mux_bits = 2, + .bank_bits = { 3 }, + .index_bits = { 11 }, + .bytes = 3, + }, + { + .num_srcs = 2, + .mux_bits = 0, + .bank_bits = { 1, 1 }, + .index_bits = { 6, 5 }, + .bytes = 2, + }, + { + .num_srcs = 2, + .mux_bits = 2, + .bank_bits = { 2, 2 }, + .index_bits = { 7, 7 }, + .bytes = 3, + }, + { + .num_srcs = 2, + .mux_bits = 3, + .bank_bits = { 3, 2 }, + .index_bits = { 11, 8 }, + .bytes = 4, + }, + { + .num_srcs = 3, + .mux_bits = 2, + .bank_bits = { 2, 2, 2 }, + .index_bits = { 7, 7, 6 }, + .bytes = 4, + }, + { + .num_srcs = 3, + .mux_bits = 3, + .bank_bits = { 3, 2, 3 }, + .index_bits = { 8, 8, 8 }, + .bytes = 5, + }, + { + .num_srcs = 3, + .mux_bits = 3, + .bank_bits = { 3, 2, 3 }, + .index_bits = { 11, 8, 11 }, + .bytes = 6, + }, +}; + +const rogue_reg_src_info rogue_reg_upper_src_infos[ROGUE_REG_SRC_VARIANTS] = { + { + .num_srcs = 1, + .bank_bits = { 1 }, + .index_bits = { 6 }, + .bytes = 1, + }, + { + .num_srcs = 1, + .bank_bits = { 3 }, + .index_bits = { 11 }, + .bytes = 3, + }, + { + .num_srcs = 2, + .bank_bits = { 1, 1 }, + .index_bits = { 6, 5 }, + .bytes = 2, + }, + { + .num_srcs = 2, + .bank_bits = { 2, 2 }, + .index_bits = { 7, 7 }, + .bytes = 3, + }, + { + .num_srcs = 2, + .bank_bits = { 3, 2 }, + .index_bits = { 11, 8 }, + .bytes = 4, + }, + { + .num_srcs = 3, + .bank_bits = { 2, 2, 2 }, + .index_bits = { 7, 7, 6 }, + .bytes = 4, + }, + { + .num_srcs = 3, + .bank_bits = { 3, 2, 2 }, + .index_bits = { 8, 8, 8 }, + .bytes = 5, + }, + { + .num_srcs = 3, + .bank_bits = { 3, 2, 2 }, + .index_bits = { 11, 8, 8 }, + .bytes = 6, + }, +}; + +const rogue_alu_op_mod_info rogue_alu_op_mod_infos[ROGUE_ALU_OP_MOD_COUNT] = { + [ROGUE_ALU_OP_MOD_LP] = { .str = "lp", }, + [ROGUE_ALU_OP_MOD_SAT] = { .str = "sat", }, + [ROGUE_ALU_OP_MOD_SCALE] = { .str = "scale", }, + [ROGUE_ALU_OP_MOD_ROUNDZERO] = { .str = "roundzero", }, +}; + +const rogue_alu_dst_mod_info rogue_alu_dst_mod_infos[ROGUE_ALU_DST_MOD_COUNT] = { + [ROGUE_ALU_DST_MOD_E0] = { .str = "e0", }, + [ROGUE_ALU_DST_MOD_E1] = { .str = "e1", }, + [ROGUE_ALU_DST_MOD_E2] = { .str = "e2", }, + [ROGUE_ALU_DST_MOD_E3] = { .str = "e3", }, +}; + +const rogue_alu_src_mod_info rogue_alu_src_mod_infos[ROGUE_ALU_SRC_MOD_COUNT] = { + [ROGUE_ALU_SRC_MOD_FLR] = { .str = "flr", }, + [ROGUE_ALU_SRC_MOD_ABS] = { .str = "abs", }, + [ROGUE_ALU_SRC_MOD_NEG] = { .str = "neg", }, +}; + +const rogue_ctrl_op_mod_info rogue_ctrl_op_mod_infos[ROGUE_CTRL_OP_MOD_COUNT] = { + [ROGUE_CTRL_OP_MOD_END] = { .str = "end", }, +}; + +#define OM(op_mod) BITFIELD64_BIT(ROGUE_CTRL_OP_MOD_##op_mod) +const rogue_ctrl_op_info rogue_ctrl_op_infos[ROGUE_CTRL_OP_COUNT] = { + [ROGUE_CTRL_OP_INVALID] = { .str = "!INVALID!", }, + [ROGUE_CTRL_OP_END] = { .str = "end", .ends_block = true, }, + [ROGUE_CTRL_OP_NOP] = { .str = "nop", + .supported_op_mods = OM(END), + }, + [ROGUE_CTRL_OP_BA] = { .str = "ba", .has_target = true, .ends_block = true, }, + [ROGUE_CTRL_OP_WDF] = { .str = "wdf", .num_srcs = 1, }, +}; +#undef OM + +#define IO(io) ROGUE_IO_##io +#define OM(op_mod) BITFIELD64_BIT(ROGUE_BACKEND_OP_MOD_##op_mod) +const rogue_backend_op_info rogue_backend_op_infos[ROGUE_BACKEND_OP_COUNT] = { + [ROGUE_BACKEND_OP_INVALID] = { .str = "!INVALID!", }, + [ROGUE_BACKEND_OP_UVSW_WRITE] = { .str = "uvsw.write", .num_dsts = 1, .num_srcs = 1, + .phase_io = { .src[0] = IO(W0), }, + }, + [ROGUE_BACKEND_OP_UVSW_EMIT] = { .str = "uvsw.emit", }, + [ROGUE_BACKEND_OP_UVSW_ENDTASK] = { .str = "uvsw.endtask", }, + + [ROGUE_BACKEND_OP_UVSW_EMITTHENENDTASK] = { .str = "uvsw.emitthenendtask", }, + [ROGUE_BACKEND_OP_UVSW_WRITETHENEMITTHENENDTASK] = { .str = "uvsw.writethenemitthenendtask", .num_dsts = 1, .num_srcs = 1, + .phase_io = { .src[0] = IO(W0), }, + }, + [ROGUE_BACKEND_OP_FITRP_PIXEL] = { .str = "fitrp.pixel", .num_dsts = 1, .num_srcs = 4, + .phase_io = { .dst[0] = IO(S3), .src[1] = IO(S0), .src[2] = IO(S2), }, + .supported_op_mods = OM(SAT), + }, +}; +#undef OM +#undef IO + +const rogue_backend_op_mod_info rogue_backend_op_mod_infos[ROGUE_BACKEND_OP_MOD_COUNT] = { + [ROGUE_BACKEND_OP_MOD_SAT] = { .str = "sat", }, +}; + +const rogue_bitwise_op_info rogue_bitwise_op_infos[ROGUE_BITWISE_OP_COUNT] = { + [ROGUE_BITWISE_OP_INVALID] = { .str = "", }, + [ROGUE_BITWISE_OP_BYP] = { .str = "byp", .num_dsts = 2, .num_srcs = 2, }, + [ROGUE_BITWISE_OP_MOV2] = { .str = "mov2", .num_dsts = 2, .num_srcs = 2, }, +}; + +const rogue_io_info rogue_io_infos[ROGUE_IO_COUNT] = { + [ROGUE_IO_INVALID] = { .str = "!INVALID!", }, + [ROGUE_IO_S0] = { .str = "s0", }, + [ROGUE_IO_S1] = { .str = "s1", }, + [ROGUE_IO_S2] = { .str = "s2", }, + [ROGUE_IO_S3] = { .str = "s3", }, + [ROGUE_IO_S4] = { .str = "s4", }, + [ROGUE_IO_S5] = { .str = "s5", }, + [ROGUE_IO_W0] = { .str = "w0", }, + [ROGUE_IO_W1] = { .str = "w1", }, + [ROGUE_IO_IS0] = { .str = "is0", }, + [ROGUE_IO_IS1] = { .str = "is1", }, + [ROGUE_IO_IS2] = { .str = "is2", }, + [ROGUE_IO_IS3] = { .str = "is3", }, + [ROGUE_IO_IS4] = { .str = "is4/w0", }, + [ROGUE_IO_IS5] = { .str = "is5/w1", }, + [ROGUE_IO_FT0] = { .str = "ft0", }, + [ROGUE_IO_FT1] = { .str = "ft1", }, + [ROGUE_IO_FT2] = { .str = "ft2", }, + [ROGUE_IO_FTE] = { .str = "fte", }, + [ROGUE_IO_FT3] = { .str = "ft3", }, + [ROGUE_IO_FT4] = { .str = "ft4", }, + [ROGUE_IO_FT5] = { .str = "ft5", }, + [ROGUE_IO_P0] = { .str = "p0", }, +}; + +#define SM(src_mod) BITFIELD64_BIT(ROGUE_ALU_SRC_MOD_##src_mod) +#define DM(dst_mod) BITFIELD64_BIT(ROGUE_ALU_DST_MOD_##dst_mod) +#define OM(op_mod) BITFIELD64_BIT(ROGUE_ALU_OP_MOD_##op_mod) +#define P(type) BITFIELD64_BIT(ROGUE_INSTR_PHASE_##type) +#define PH(type) ROGUE_INSTR_PHASE_##type +#define IO(io) ROGUE_IO_##io +#define T(type) BITFIELD64_BIT(ROGUE_REF_TYPE_##type - 1) +const rogue_alu_op_info rogue_alu_op_infos[ROGUE_ALU_OP_COUNT] = { + [ROGUE_ALU_OP_INVALID] = { .str = "!INVALID!", }, + [ROGUE_ALU_OP_MBYP] = { .str = "mbyp", .num_srcs = 1, + .supported_phases = P(0), + .phase_io[PH(0)] = { .dst = IO(FT0), .src[0] = IO(S0), }, + .supported_src_mods = { + [0] = SM(ABS) | SM(NEG), + }, + .supported_dst_types = T(REG), + .supported_src_types = { + [0] = T(REG), + }, + }, + [ROGUE_ALU_OP_FADD] = { .str = "fadd", .num_srcs = 2, + .supported_phases = P(0), + .phase_io[PH(0)] = { .dst = IO(FT0), .src[0] = IO(S0), .src[1] = IO(S1), }, + .supported_op_mods = OM(LP) | OM(SAT), + .supported_src_mods = { + [0] = SM(FLR) | SM(ABS) | SM(NEG), + [1] = SM(ABS), + }, + }, + [ROGUE_ALU_OP_FMUL] = { .str = "fmul", .num_srcs = 2, + .supported_phases = P(0), + .phase_io[PH(0)] = { .dst = IO(FT0), .src[0] = IO(S0), .src[1] = IO(S1), }, + .supported_op_mods = OM(LP) | OM(SAT), + .supported_src_mods = { + [0] = SM(FLR) | SM(ABS) | SM(NEG), + [1] = SM(ABS), + }, + .supported_dst_types = T(REG), + .supported_src_types = { + [0] = T(REG), + [1] = T(REG), + }, + }, + [ROGUE_ALU_OP_FMAD] = { .str = "fmad", .num_srcs = 3, + .supported_phases = P(0), + .phase_io[PH(0)] = { .dst = IO(FT0), .src[0] = IO(S0), .src[1] = IO(S1), .src[2] = IO(S2), }, + .supported_op_mods = OM(LP) | OM(SAT), + .supported_src_mods = { + [0] = SM(ABS) | SM(NEG), + [1] = SM(ABS) | SM(NEG), + [2] = SM(FLR) | SM(ABS) | SM(NEG), + }, + .supported_dst_types = T(REG), + .supported_src_types = { + [0] = T(REG), + [1] = T(REG), + [2] = T(REG), + }, + }, + /* TODO: Implement */ + [ROGUE_ALU_OP_TST] = { .str = "tst", .num_srcs = 2, }, + [ROGUE_ALU_OP_PCK_U8888] = { .str = "pck.u8888", .num_srcs = 1, + .supported_phases = P(2_PCK), + .phase_io[PH(2_PCK)] = { .dst = IO(FT2), .src[0] = IO(IS3), }, + .supported_op_mods = OM(SCALE) | OM(ROUNDZERO), + .supported_dst_types = T(REG), + .supported_src_types = { + [0] = T(REGARRAY), + }, + }, + /* This mov is "fake" since it can be lowered to a MBYP, make a new instruction for real mov (call it MOVD?). */ + [ROGUE_ALU_OP_MOV] = { .str = "mov", .num_srcs = 1, + .supported_dst_types = T(REG), + .supported_src_types = { + [0] = T(REG) | T(IMM), + }, + }, + [ROGUE_ALU_OP_FABS] = { .str = "fabs", .num_srcs = 1, }, + [ROGUE_ALU_OP_FNEG] = { .str = "fneg", .num_srcs = 1, }, + [ROGUE_ALU_OP_FNABS] = { .str = "fnabs", .num_srcs = 1, }, + + [ROGUE_ALU_OP_FMAX] = { .str = "fmax", .num_srcs = 2, }, /* TODO */ + [ROGUE_ALU_OP_FMIN] = { .str = "fmin", .num_srcs = 2, }, /* TODO */ + [ROGUE_ALU_OP_SEL] = { .str = "sel", .num_srcs = 3, }, /* TODO */ +}; +#undef T +#undef IO +#undef PH +#undef P +#undef OM +#undef DM +#undef SM + +const char *const rogue_comp_test_str[ROGUE_COMP_TEST_COUNT] = { + [ROGUE_COMP_TEST_NONE] = "!INVALID!", [ROGUE_COMP_TEST_EQ] = "eq", + [ROGUE_COMP_TEST_GT] = "gt", [ROGUE_COMP_TEST_GE] = "ge", + [ROGUE_COMP_TEST_NE] = "ne", [ROGUE_COMP_TEST_LT] = "lt", + [ROGUE_COMP_TEST_LE] = "le", +}; + +const char *const rogue_comp_type_str[ROGUE_COMP_TYPE_COUNT] = { + [ROGUE_COMP_TYPE_NONE] = "!INVALID!", [ROGUE_COMP_TYPE_F32] = "f32", + [ROGUE_COMP_TYPE_U16] = "u16", [ROGUE_COMP_TYPE_S16] = "s16", + [ROGUE_COMP_TYPE_U8] = "u8", [ROGUE_COMP_TYPE_S8] = "s8", + [ROGUE_COMP_TYPE_U32] = "u32", [ROGUE_COMP_TYPE_S32] = "s32", +}; + +const char *rogue_instr_type_str[ROGUE_INSTR_TYPE_COUNT] = { + [ROGUE_INSTR_TYPE_INVALID] = "!INVALID!", + + [ROGUE_INSTR_TYPE_ALU] = "alu", + /* [ROGUE_INSTR_TYPE_CMPLX] = "cmplx", */ + [ROGUE_INSTR_TYPE_BACKEND] = "backend", + [ROGUE_INSTR_TYPE_CTRL] = "ctrl", + [ROGUE_INSTR_TYPE_BITWISE] = "bitwise", + /* [ROGUE_INSTR_TYPE_F16SOP] = "f16sop", */ +}; + +const char *const rogue_alu_str[ROGUE_ALU_COUNT] = { + [ROGUE_ALU_INVALID] = "!INVALID!", + [ROGUE_ALU_MAIN] = "main", + [ROGUE_ALU_BITWISE] = "bitwise", + [ROGUE_ALU_CONTROL] = "control", +}; + +const char *const rogue_instr_phase_str[ROGUE_ALU_COUNT][ROGUE_INSTR_PHASE_COUNT] = { + /** Main/ALU (and backend) instructions. */ + [ROGUE_ALU_MAIN] = { + [ROGUE_INSTR_PHASE_0] = "p0", + [ROGUE_INSTR_PHASE_1] = "p1", + [ROGUE_INSTR_PHASE_2_PCK] = "p2pck", + [ROGUE_INSTR_PHASE_2_TST] = "p2tst", + [ROGUE_INSTR_PHASE_2_MOV] = "p2mov", + [ROGUE_INSTR_PHASE_BACKEND] = "backend", + }, + + /** Bitwise instructions. */ + [ROGUE_ALU_BITWISE] = { + [ROGUE_INSTR_PHASE_0_BITMASK] = "p0bm", + [ROGUE_INSTR_PHASE_0_SHIFT1] = "p0shf1", + [ROGUE_INSTR_PHASE_0_COUNT] = "p0cnt", + [ROGUE_INSTR_PHASE_1_LOGICAL] = "p1log", + [ROGUE_INSTR_PHASE_2_SHIFT2] = "p2shf2", + [ROGUE_INSTR_PHASE_2_TEST] = "p2tst", + }, + + /** Control instructions (no co-issuing). */ + [ROGUE_ALU_CONTROL] = { + [ROGUE_INSTR_PHASE_CTRL] = "ctrl", + }, +}; diff --git a/src/imagination/rogue/rogue_instr.c b/src/imagination/rogue/rogue_instr.c deleted file mode 100644 index a6988021f15..00000000000 --- a/src/imagination/rogue/rogue_instr.c +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright © 2022 Imagination Technologies Ltd. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include - -#include "rogue_instr.h" -#include "rogue_operand.h" -#include "rogue_util.h" -#include "util/ralloc.h" - -/** - * \file rogue_instr.c - * - * \brief Contains functions to manipulate Rogue instructions. - */ - -/* clang-format off */ - -static const size_t instr_operand_count[ROGUE_OP_COUNT] = { - [ROGUE_OP_NOP] = 0, - [ROGUE_OP_END_FRAG] = 0, - [ROGUE_OP_END_VERT] = 0, - [ROGUE_OP_WDF] = 1, - [ROGUE_OP_PIX_ITER_W] = 5, - [ROGUE_OP_MAX] = 3, - [ROGUE_OP_MIN] = 3, - [ROGUE_OP_PACK_U8888] = 2, - [ROGUE_OP_MOV] = 2, - [ROGUE_OP_MOV_IMM] = 2, - [ROGUE_OP_FMA] = 4, - [ROGUE_OP_MUL] = 3, - [ROGUE_OP_VTXOUT] = 2, -}; - -/* clang-format on */ - -/** - * \brief Returns the number of operands an instruction takes. - * - * \param[in] opcode The instruction opcode. - * \return The number of operands. - */ -static inline size_t rogue_instr_num_operands(enum rogue_opcode opcode) -{ - ASSERT_OPCODE_RANGE(opcode); - - return instr_operand_count[opcode]; -} - -/** - * \brief Allocates and sets up a Rogue instruction. - * - * \param[in] mem_ctx The memory context for the instruction. - * \param[in] opcode The instruction opcode. - * \return A rogue_instr* if successful, or NULL if unsuccessful. - */ -struct rogue_instr *rogue_instr_create(void *mem_ctx, enum rogue_opcode opcode) -{ - struct rogue_instr *instr; - - ASSERT_OPCODE_RANGE(opcode); - - instr = rzalloc_size(mem_ctx, sizeof(*instr)); - if (!instr) - return NULL; - - instr->opcode = opcode; - instr->num_operands = rogue_instr_num_operands(opcode); - - /* Allocate space for operand array. */ - if (instr->num_operands) { - instr->operands = rzalloc_array_size(instr, - sizeof(*instr->operands), - instr->num_operands); - if (!instr->operands) { - ralloc_free(instr); - return NULL; - } - } - - return instr; -} - -/** - * \brief Sets a Rogue instruction flag. - * - * \param[in] instr The instruction. - * \param[in] flag The flag to set. - * \return true if valid, otherwise false. - */ -bool rogue_instr_set_flag(struct rogue_instr *instr, enum rogue_instr_flag flag) -{ - instr->flags = ROH(flag); - - return true; -} - -/** - * \brief Sets a Rogue instruction operand to an immediate value. - * - * \param[in] instr The instruction. - * \param[in] index The operand index. - * \param[in] value The value to set. - * \return true if valid, otherwise false. - */ -bool rogue_instr_set_operand_imm(struct rogue_instr *instr, - size_t index, - uint64_t value) -{ - ASSERT_INSTR_OPERAND_INDEX(instr, index); - - instr->operands[index].type = ROGUE_OPERAND_TYPE_IMMEDIATE; - instr->operands[index].immediate.value = value; - - return true; -} - -/** - * \brief Sets a Rogue instruction operand to a DRC number. - * - * \param[in] instr The instruction. - * \param[in] index The operand index. - * \param[in] number The DRC number to set. - * \return true if valid, otherwise false. - */ -bool rogue_instr_set_operand_drc(struct rogue_instr *instr, - size_t index, - size_t number) -{ - ASSERT_INSTR_OPERAND_INDEX(instr, index); - - instr->operands[index].type = ROGUE_OPERAND_TYPE_DRC; - instr->operands[index].drc.number = number; - - return true; -} - -/** - * \brief Sets a Rogue instruction operand to a register. - * - * \param[in] instr The instruction. - * \param[in] index The operand index. - * \param[in] type The register type to set. - * \param[in] number The register number to set. - * \return true if valid, otherwise false. - */ -bool rogue_instr_set_operand_reg(struct rogue_instr *instr, - size_t index, - enum rogue_operand_type type, - size_t number) -{ - ASSERT_INSTR_OPERAND_INDEX(instr, index); - ASSERT_OPERAND_REG(type); - - instr->operands[index].type = type; - instr->operands[index].reg.number = number; - - return true; -} - -/** - * \brief Sets a Rogue instruction operand to a virtual register. - * - * \param[in] instr The instruction. - * \param[in] index The operand index. - * \param[in] number The register number to set. - * \return true if valid, otherwise false. - */ -bool rogue_instr_set_operand_vreg(struct rogue_instr *instr, - size_t index, - size_t number) -{ - ASSERT_INSTR_OPERAND_INDEX(instr, index); - - instr->operands[index].type = ROGUE_OPERAND_TYPE_VREG; - instr->operands[index].vreg.number = number; - instr->operands[index].vreg.is_vector = false; - - return true; -} - -/** - * \brief Sets a Rogue instruction operand to a virtual register - * that is a vector type. - * - * \param[in] instr The instruction. - * \param[in] index The operand index. - * \param[in] component The vector component. - * \param[in] number The register number to set. - * \return true if valid, otherwise false. - */ -bool rogue_instr_set_operand_vreg_vec(struct rogue_instr *instr, - size_t index, - size_t component, - size_t number) -{ - ASSERT_INSTR_OPERAND_INDEX(instr, index); - - instr->operands[index].type = ROGUE_OPERAND_TYPE_VREG; - instr->operands[index].vreg.number = number; - instr->operands[index].vreg.is_vector = true; - instr->operands[index].vreg.component = component; - - return true; -} diff --git a/src/imagination/rogue/rogue_instr.h b/src/imagination/rogue/rogue_instr.h deleted file mode 100644 index 5b6efa5dde6..00000000000 --- a/src/imagination/rogue/rogue_instr.h +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright © 2022 Imagination Technologies Ltd. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef ROGUE_INSTR_H -#define ROGUE_INSTR_H - -#include -#include -#include - -#include "rogue_operand.h" -#include "util/list.h" - -/** - * \brief Instruction opcodes. - */ -enum rogue_opcode { - ROGUE_OP_NOP = 0, /** No-operation. */ - ROGUE_OP_END_FRAG, /** Fragment shader end. */ - ROGUE_OP_END_VERT, /** Vertex shader end. */ - ROGUE_OP_WDF, /** Write data fence. */ - - ROGUE_OP_PIX_ITER_W, /** Pixel iteration with coefficients. */ - - ROGUE_OP_MAX, /** Returns the largest out of two floats. */ - ROGUE_OP_MIN, /** Returns the smallest out of two floats. */ - - ROGUE_OP_PACK_U8888, /** Scales the four input floats: - * [0.0f, 0.1f] -> [0, 255] and packs them - * into a 32-bit unsigned integer. - */ - - ROGUE_OP_MOV, /** Register move instruction. */ - ROGUE_OP_MOV_IMM, /** Move immediate instruction. */ - - ROGUE_OP_FMA, /** Fused-multiply-add (float). */ - ROGUE_OP_MUL, /** Multiply (float). */ - - ROGUE_OP_VTXOUT, /** Writes the input register - * to the given vertex output index. - */ - - ROGUE_OP_COUNT, -}; - -/** - * \brief Instruction flags. - */ -enum rogue_instr_flag { - ROGUE_INSTR_FLAG_SAT = 0, /** Saturate values to 0.0 ... 1.0. */ - ROGUE_INSTR_FLAG_LP, /** Low-precision modifier. */ - ROGUE_INSTR_FLAG_OLCHK, /** Overlap check (pixel write). */ - - ROGUE_INSTR_FLAG_COUNT, -}; - -/** - * \brief Instruction description. - */ -struct rogue_instr { - enum rogue_opcode opcode; - - size_t num_operands; - struct rogue_operand *operands; - - uint64_t flags; /** A mask of #rogue_instr_flag values. */ - - struct list_head node; /** Linked list node. */ -}; - -struct rogue_instr *rogue_instr_create(void *mem_ctx, enum rogue_opcode opcode); - -bool rogue_instr_set_flag(struct rogue_instr *instr, - enum rogue_instr_flag flag); - -bool rogue_instr_set_operand_imm(struct rogue_instr *instr, - size_t index, - uint64_t value); -bool rogue_instr_set_operand_drc(struct rogue_instr *instr, - size_t index, - size_t number); -bool rogue_instr_set_operand_reg(struct rogue_instr *instr, - size_t index, - enum rogue_operand_type type, - size_t number); -bool rogue_instr_set_operand_vreg(struct rogue_instr *instr, - size_t index, - size_t number); -bool rogue_instr_set_operand_vreg_vec(struct rogue_instr *instr, - size_t index, - size_t component, - size_t number); -#endif /* ROGUE_INSTR_H */ diff --git a/src/imagination/rogue/rogue_isa.h b/src/imagination/rogue/rogue_isa.h new file mode 100644 index 00000000000..c32bc163c8b --- /dev/null +++ b/src/imagination/rogue/rogue_isa.h @@ -0,0 +1,963 @@ +/* + * Copyright © 2022 Imagination Technologies Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef ROGUE_ISA_H +#define ROGUE_ISA_H + +/** + * \file rogue_isa.h + * + * \brief Contains hardware ISA definitions and encodings. + */ + +/* Source/destination encodings. */ + +/* Internal source selector. */ +typedef struct rogue_iss_encoding { + unsigned is1 : 1; + unsigned is2 : 1; + unsigned is3 : 2; + unsigned is4 : 2; + unsigned is5 : 2; +} PACKED rogue_iss_encoding; +static_assert(sizeof(rogue_iss_encoding) == 1, + "sizeof(rogue_iss_encoding) != 1"); + +typedef struct rogue_dbN { + union { + struct { + unsigned _0 : 1; + unsigned _2_1 : 2; + unsigned : 5; + } PACKED; + + uint8_t _; + } PACKED; +} PACKED rogue_dbN; +static_assert(sizeof(rogue_dbN) == 1, "sizeof(rogue_dbN) != 1"); + +typedef struct rogue_dN { + union { + struct { + unsigned _5_0 : 6; + unsigned _7_6 : 2; + unsigned _10_8 : 3; + unsigned : 5; + } PACKED; + + uint8_t _; + } PACKED; +} PACKED rogue_dN; +static_assert(sizeof(rogue_dN) == 2, "sizeof(rogue_dN) != 2"); + +typedef struct rogue_db0 { + union { + struct { + unsigned _0 : 1; + unsigned _2_1 : 2; + unsigned : 5; + } PACKED; + + uint8_t _; + } PACKED; +} PACKED rogue_db0; +static_assert(sizeof(rogue_db0) == 1, "sizeof(rogue_db0) != 1"); + +typedef struct rogue_db1 { + union { + struct { + unsigned _0 : 1; + unsigned _2_1 : 2; + unsigned : 5; + } PACKED; + + uint8_t _; + } PACKED; +} PACKED rogue_db1; +static_assert(sizeof(rogue_db0) == 1, "sizeof(rogue_db0) != 1"); + +typedef struct rogue_d0 { + union { + struct { + unsigned _6_0 : 7; + unsigned _7 : 1; + unsigned _10_8 : 3; + unsigned : 5; + } PACKED; + + uint16_t _; + } PACKED; +} PACKED rogue_d0; +static_assert(sizeof(rogue_d0) == 2, "sizeof(rogue_d0) != 2"); + +typedef struct rogue_d1 { + union { + struct { + unsigned _5_0 : 6; + unsigned _7_6 : 2; + unsigned _10_8 : 3; + unsigned : 5; + } PACKED; + + uint16_t _; + } PACKED; +} PACKED rogue_d1; +static_assert(sizeof(rogue_d1) == 2, "sizeof(rogue_d1) != 2"); + +typedef struct rogue_dest_map_encoding { + /* Byte 0 */ + union { + struct { + unsigned dN_5_0 : 6; + unsigned dbN_0 : 1; + unsigned ext0 : 1; + } PACKED; + + struct { + unsigned d0_6_0 : 7; + unsigned db0_0 : 1; + } PACKED; + }; + + /* Byte 1 */ + union { + struct { + unsigned dN_7_6 : 2; + unsigned dbN_2_1 : 2; + unsigned dN_10_8 : 3; + unsigned rsvd1 : 1; + } PACKED; + + struct { + unsigned d1_5_0 : 6; + unsigned db1_0 : 1; + unsigned ext1 : 1; + } PACKED; + }; + + /* Byte 2 */ + struct { + unsigned d0_7 : 1; + unsigned db0_2_1 : 2; + unsigned d1_7_6 : 2; + unsigned db1_2_1 : 2; + unsigned ext2 : 1; + } PACKED; + + /* Byte 3 */ + struct { + unsigned d0_10_8 : 3; + unsigned d1_10_8 : 3; + unsigned rsvd3 : 2; + } PACKED; +} PACKED rogue_dest_map_encoding; +static_assert(sizeof(rogue_dest_map_encoding) == 4, + "sizeof(rogue_dest_map_encoding) != 4"); + +/****************/ + +typedef struct rogue_source_map_encoding { + /* Byte 0 */ + struct { + unsigned sA_5_0 : 6; + unsigned sbA_0 : 1; + unsigned ext0 : 1; + } PACKED; + + /* Byte 1 */ + union { + struct { + unsigned sA_7_6 : 2; + unsigned sbA_2_1 : 2; + unsigned mux_1_0 : 2; + unsigned : 2; + } PACKED sA_1; + + struct { + unsigned sB_4_0 : 5; + unsigned sbB_0 : 1; + unsigned ext1 : 1; + unsigned sel : 1; + } PACKED; + } PACKED; + + /* Byte 2 */ + union { + /* Common def. */ + struct sA_sC_10_8 { + unsigned sA_10_8 : 3; + unsigned sC_10_8 : 3; + unsigned rsvd5 : 2; + } PACKED sA_2; + + struct { + unsigned sB_6_5 : 2; + unsigned sA_6 : 1; + unsigned sbB_1 : 1; + unsigned sbA_1 : 1; + unsigned mux_1_0 : 2; + unsigned ext2 : 1; + } PACKED; + + struct { + unsigned : 3; + unsigned rsvd2 : 5; + } PACKED; + } PACKED; + + /* Byte 3 */ + union { + /* Common def. */ + struct sB_sC_mux2 { + union { + struct { + unsigned sB_7 : 1; + unsigned sA_7 : 1; + unsigned rsvd3 : 1; + unsigned sbA_2 : 1; + unsigned mux_2 : 1; + unsigned sA_10_8 : 3; + } PACKED; + + struct { + unsigned : 2; + unsigned ext4 : 1; + unsigned : 2; + unsigned sC_7_6 : 2; + unsigned sbC_2 : 1; + } PACKED; + } PACKED; + } PACKED sB_3; + + struct { + unsigned sC_5_0 : 6; + unsigned sbC_1_0 : 2; + } PACKED; + } PACKED; + + /* Byte 4 */ + struct sB_sC_mux2 sC_4; + + /* Byte 5 */ + struct sA_sC_10_8 sC_5; +} PACKED rogue_source_map_encoding; +static_assert(sizeof(rogue_source_map_encoding) == 6, + "sizeof(rogue_source_map_encoding) == 6"); + +typedef struct rogue_sbA { + union { + struct { + unsigned _0 : 1; + unsigned _2_1 : 2; + unsigned : 5; + } PACKED; + + struct { + unsigned : 1; + unsigned _1 : 1; + unsigned _2 : 1; + unsigned : 5; + } PACKED; + + uint8_t _; + } PACKED; +} PACKED rogue_sbA; +static_assert(sizeof(rogue_sbA) == 1, "sizeof(rogue_sbA) != 1"); + +typedef struct rogue_sA { + union { + struct { + unsigned _5_0 : 6; + unsigned _7_6 : 2; + unsigned _10_8 : 3; + unsigned : 5; + } PACKED; + + struct { + unsigned : 6; + unsigned _6 : 1; + unsigned _7 : 1; + unsigned : 8; + } PACKED; + + uint16_t _; + } PACKED; +} PACKED rogue_sA; +static_assert(sizeof(rogue_sA) == 2, "sizeof(rogue_sA) != 2"); + +typedef struct rogue_sbB { + union { + struct { + unsigned _0 : 1; + unsigned _1 : 1; + unsigned : 6; + } PACKED; + + uint8_t _; + } PACKED; +} PACKED rogue_sbB; +static_assert(sizeof(rogue_sbB) == 1, "sizeof(rogue_sbB) != 1"); + +typedef struct rogue_sB { + union { + struct { + unsigned _4_0 : 5; + unsigned _6_5 : 2; + unsigned _7 : 1; + unsigned : 8; + } PACKED; + + uint16_t _; + } PACKED; +} PACKED rogue_sB; +static_assert(sizeof(rogue_sB) == 2, "sizeof(rogue_sB) != 2"); + +typedef struct rogue_sbC { + union { + struct { + unsigned _1_0 : 2; + unsigned _2 : 1; + unsigned : 5; + } PACKED; + + uint8_t _; + } PACKED; +} PACKED rogue_sbC; +static_assert(sizeof(rogue_sbC) == 1, "sizeof(rogue_sbC) != 1"); + +typedef struct rogue_sC { + union { + struct { + unsigned _5_0 : 6; + unsigned _7_6 : 2; + unsigned _10_8 : 3; + unsigned : 5; + } PACKED; + + uint16_t _; + } PACKED; +} PACKED rogue_sC; +static_assert(sizeof(rogue_sC) == 2, "sizeof(rogue_sC) != 2"); + +typedef struct rogue_mux { + union { + struct { + unsigned _1_0 : 2; + unsigned _2 : 1; + unsigned : 5; + } PACKED; + + uint8_t _; + } PACKED; +} PACKED rogue_mux; +static_assert(sizeof(rogue_mux) == 1, "sizeof(rogue_mux) != 1"); + +enum reg_bank { + BANK_SPECIAL = 0b000, + BANK_TEMP = 0b001, + BANK_VTXIN = 0b010, + BANK_COEFF = 0b011, + BANK_SHARED = 0b100, + BANK_COEFF_ALT = 0b101, + BANK_IDX0 = 0b110, + BANK_IDX1 = 0b111, +}; + +enum is0 { + IS0_S0 = 0b000, + IS0_S3 = 0b001, + IS0_S4 = 0b010, + IS0_S5 = 0b011, + IS0_S1 = 0b100, + IS0_S2 = 0b101, +}; + +enum is1 { + IS1_FT0 = 0b0, + IS1_FTE = 0b1, +}; + +enum is2 { + IS2_FT1 = 0b0, + IS2_FTE = 0b1, +}; + +enum is3 { + IS3_FT0 = 0b00, + IS3_FT1 = 0b01, + IS3_S2 = 0b10, + IS3_FTE = 0b11, +}; + +enum is4 { + IS4_FT0 = 0b00, + IS4_FT1 = 0b01, + IS4_FT2 = 0b10, + IS4_FTE = 0b11, +}; + +enum is5 { + IS5_FT0 = 0b00, + IS5_FT1 = 0b01, + IS5_FT2 = 0b10, + IS5_FTE = 0b11, +}; + +/* Single source instructions. */ + +typedef struct rogue_single_pck_encoding { + /* Byte 1 */ + union { + struct { + union { + struct { + unsigned format : 5; + unsigned scale : 1; + unsigned elem : 2; + } PACKED; + + struct { + unsigned : 5; + unsigned rtz : 1; + unsigned : 2; + } PACKED; + } PACKED; + } PACKED upck; + + struct { + unsigned format : 5; + unsigned scale : 1; + unsigned rtz : 1; + unsigned prog : 1; + } PACKED pck; + } PACKED; +} PACKED rogue_single_pck_encoding; +static_assert(sizeof(rogue_single_pck_encoding) == 1, + "sizeof(rogue_single_pck_encoding) != 1"); + +enum pck_fmt { + PCK_FMT_U8888 = 0b00000, + PCK_FMT_S8888 = 0b00001, + PCK_FMT_O8888 = 0b00010, + PCK_FMT_U1616 = 0b00011, + PCK_FMT_S1616 = 0b00100, + PCK_FMT_O1616 = 0b00101, + PCK_FMT_U32 = 0b00110, + PCK_FMT_S32 = 0b00111, + PCK_FMT_U1010102 = 0b01000, + PCK_FMT_S1010102 = 0b01001, + PCK_FMT_U111110 = 0b01010, + PCK_FMT_S111110 = 0b01011, + PCK_FMT_F111110 = 0b01100, + PCK_FMT_F16F16 = 0b01110, + PCK_FMT_F32 = 0b01111, + PCK_FMT_COV = 0b10000, + PCK_FMT_U565U565 = 0b10001, + PCK_FMT_D24S8 = 0b10010, + PCK_FMT_S8D24 = 0b10011, + PCK_FMT_F32_MASK = 0b10100, + PCK_FMT_2F10F10F10 = 0b10101, + PCK_FMT_S8888OGL = 0b10110, + PCK_FMT_S1616OGL = 0b10111, + PCK_FMT_ZERO = 0b11110, + PCK_FMT_ONE = 0b11111, +}; + +typedef struct rogue_single_mbyp_encoding { + /* Byte 1 */ + struct { + unsigned s0abs : 1; + unsigned s0neg : 1; + unsigned : 6; + } PACKED; +} PACKED rogue_single_mbyp_encoding; +static_assert(sizeof(rogue_single_mbyp_encoding) == 1, + "sizeof(rogue_single_mbyp_encoding) != 1"); + +enum snglop { + SNGLOP_RCP = 0b0000, + SNGLOP_RSQ = 0b0001, + SNGLOP_LOG = 0b0010, + SNGLOP_EXP = 0b0011, + SNGLOP_F16SOP = 0b0100, + SNGLOP_LOGCN = 0b0101, + SNGLOP_GAMMA = 0b0110, + SNGLOP_BYP = 0b0111, + SNGLOP_DSX = 0b1000, + SNGLOP_DSY = 0b1001, + SNGLOP_DSXF = 0b1010, + SNGLOP_DSYF = 0b1011, + SNGLOP_PCK = 0b1100, + SNGLOP_RED = 0b1101, + SNGLOP_SINC = 0b1110, + SNGLOP_ARCTANC = 0b1111, +}; + +typedef struct rogue_alu_single_encoding { + /* Byte 0 */ + struct { + unsigned snglop : 4; + unsigned ext0 : 1; + unsigned : 3; + } PACKED; + + /* Byte 1+ */ + union { + rogue_single_mbyp_encoding mbyp; + rogue_single_pck_encoding pck; + } PACKED; +} PACKED rogue_alu_single_encoding; +static_assert(sizeof(rogue_alu_single_encoding) == 2, + "sizeof(rogue_alu_single_encoding) != 2"); + +typedef struct rogue_alu_fmad_encoding { + /* Byte 0 */ + struct { + unsigned sat : 1; + unsigned s2neg : 1; + unsigned s0abs : 1; + unsigned s0neg : 1; + unsigned ext : 1; + unsigned : 3; + } PACKED; + + /* Byte 1 */ + struct { + unsigned s2abs : 1; + unsigned s2flr : 1; + unsigned s1neg : 1; + unsigned s1abs : 1; + unsigned lp : 1; + unsigned : 3; + } PACKED; +} PACKED rogue_alu_fmad_encoding; +static_assert(sizeof(rogue_alu_fmad_encoding) == 2, + "sizeof(rogue_alu_fmad_encoding) != 2"); + +typedef struct rogue_alu_fdual_encoding { + /* Byte 0 */ + struct { + unsigned s0flr : 1; + unsigned s1abs : 1; + unsigned s0abs : 1; + unsigned s0neg : 1; + unsigned sat : 1; + unsigned lp : 1; + unsigned : 2; + } PACKED; +} PACKED rogue_alu_fdual_encoding; +static_assert(sizeof(rogue_alu_fdual_encoding) == 1, + "sizeof(rogue_alu_fdual_encoding) != 1"); + +typedef struct rogue_alu_tst_encoding { + /* Byte 0 */ + struct { + unsigned pwen : 1; + unsigned tstop_2_0 : 3; + unsigned ext : 1; + unsigned : 3; + } PACKED; + + /* Byte 1 */ + struct { + unsigned tstop_3 : 1; + unsigned : 1; + unsigned elem : 2; + unsigned p2end : 1; + unsigned type : 3; + } PACKED; +} PACKED rogue_alu_tst_encoding; +static_assert(sizeof(rogue_alu_tst_encoding) == 2, + "sizeof(rogue_alu_tst_encoding) != 2"); + +typedef struct rogue_tstop { + union { + struct { + unsigned _2_0 : 3; + unsigned _3 : 1; + unsigned : 4; + } PACKED; + + uint8_t _; + } PACKED; +} PACKED rogue_tstop; +static_assert(sizeof(rogue_tstop) == 1, "sizeof(rogue_tstop) != 1"); + +enum tstop { + TSTOP_Z = 0b0000, + TSTOP_GZ = 0b0001, + TSTOP_GEZ = 0b0010, + TSTOP_IC = 0b0011, + TSTOP_EQ = 0b0100, + TSTOP_GT = 0b0101, + TSTOP_GE = 0b0110, + TSTOP_NE = 0b0111, + TSTOP_LT = 0b1000, + TSTOP_LE = 0b1001, +}; + +typedef struct rogue_alu_mov_encoding { + /* Byte 0 */ + struct { + unsigned movw0 : 2; + unsigned movw1 : 2; + unsigned ext : 1; + unsigned : 3; + } PACKED; + + /* Byte 1 */ + struct { + unsigned p2end : 1; + unsigned aw : 1; + unsigned maskw0 : 4; + unsigned : 2; + } PACKED; +} PACKED rogue_alu_mov_encoding; +static_assert(sizeof(rogue_alu_mov_encoding) == 2, + "sizeof(rogue_alu_mov_encoding) != 2"); + +typedef struct rogue_alu_instr_encoding { + union { + /* Byte 0 */ + struct { + unsigned : 5; + unsigned op : 3; + } PACKED; + + /* Bytes 1+ */ + rogue_alu_single_encoding sngl; + rogue_alu_fdual_encoding fadd; + rogue_alu_fdual_encoding fmul; + rogue_alu_fmad_encoding fmad; + rogue_alu_tst_encoding tst; + rogue_alu_mov_encoding mov; + } PACKED; +} PACKED rogue_alu_instr_encoding; +static_assert(sizeof(rogue_alu_instr_encoding) == 2, + "sizeof(rogue_alu_instr_encoding) != 2"); + +enum aluop { + ALUOP_FADD = 0b000, /** Phase 0, 1. */ + ALUOP_FMUL = 0b010, /** Phase 0, 1. */ + ALUOP_SNGL = 0b100, /** Phase 0, 1, 2. */ + ALUOP_INT8_16 = 0b101, /** Phase 0. */ + ALUOP_FMAD = 0b110, /** Phase 0, 1. */ + ALUOP_MOV = 0b110, /** Phase 2. */ + ALUOP_INT32_64 = 0b111, /** Phase 0. */ + ALUOP_TST = 0b111, /** Phase 2. */ +}; + +/* Backend instructions. */ + +typedef struct rogue_backend_emitpix_encoding { + /* Byte 0 */ + struct { + unsigned : 1; + unsigned freep : 1; + unsigned : 6; + } PACKED; +} PACKED rogue_backend_emitpix_encoding; +static_assert(sizeof(rogue_backend_emitpix_encoding) == 1, + "sizeof(rogue_backend_emitpix_encoding) != 1"); + +typedef struct rogue_backend_fitr_encoding { + /* Byte 0 */ + struct { + unsigned mode : 2; + unsigned : 1; + unsigned drc : 1; + unsigned p : 1; + unsigned : 3; + } PACKED; + + /* Byte 1 */ + struct { + unsigned count : 4; + unsigned sat : 1; + unsigned : 3; + } PACKED; +} PACKED rogue_backend_fitr_encoding; +static_assert(sizeof(rogue_backend_fitr_encoding) == 2, + "sizeof(rogue_backend_fitr_encoding) != 2"); + +enum fitr_mode { + FITR_MODE_PIXEL = 0b00, + FITR_MODE_SAMPLE = 0b01, + FITR_MODE_CENTROID = 0b10, +}; + +typedef struct rogue_backend_uvsw_encoding { + /* Byte 0 */ + struct { + unsigned writeop : 3; + unsigned imm : 1; + unsigned dsel : 1; + unsigned : 3; + } PACKED; + + /* Byte 1 */ + union { + struct { + unsigned srcsel : 3; + unsigned : 5; + } PACKED src; + + struct { + unsigned imm_addr : 8; + } PACKED imm_src; + + struct { + unsigned streamid : 2; + unsigned : 6; + } PACKED stream_src; + } PACKED; +} PACKED rogue_backend_uvsw_encoding; +static_assert(sizeof(rogue_backend_uvsw_encoding) == 2, + "sizeof(rogue_backend_uvsw_encoding) != 2"); + +enum uvsw_writeop { + UVSW_WRITEOP_WRITE = 0b000, + UVSW_WRITEOP_EMIT = 0b001, + UVSW_WRITEOP_CUT = 0b010, + UVSW_WRITEOP_EMIT_CUT = 0b011, + UVSW_WRITEOP_END = 0b100, + UVSW_WRITEOP_EMIT_END = 0b101, + UVSW_WRITEOP_WRITE_EMIT_END = 0b110, +}; + +typedef struct rogue_backend_instr_encoding { + union { + /* Byte 0 */ + struct { + unsigned : 5; + unsigned op : 3; + } PACKED; + + rogue_backend_uvsw_encoding uvsw; + rogue_backend_fitr_encoding fitr; + rogue_backend_emitpix_encoding emitpix; + } PACKED; +} PACKED rogue_backend_instr_encoding; +static_assert(sizeof(rogue_backend_instr_encoding) == 2, + "sizeof(rogue_backend_instr_encoding) != 2"); + +enum backendop { + BACKENDOP_UVSW = 0b000, + BACKENDOP_MSK = 0b001, + BACKENDOP_PHAS = 0b010, + BACKENDOP_SETL = 0b011, + BACKENDOP_VISTEST = 0b100, + BACKENDOP_FITR = 0b101, + BACKENDOP_EMIT = 0b110, + BACKENDOP_DMA = 0b111, +}; + +/* Branch */ +typedef struct rogue_ctrl_ba_encoding { + /* Byte 0 */ + struct { + unsigned : 1; + unsigned abs : 1; + unsigned allp : 1; + unsigned anyp : 1; + unsigned link : 1; + unsigned : 3; + } PACKED; + + /* Byte 1 */ + struct { + unsigned : 1; + unsigned offset_7_1 : 7; + } PACKED; + + /* Byte 2 */ + struct { + unsigned offset_15_8 : 8; + } PACKED; + + /* Byte 3 */ + struct { + unsigned offset_23_16 : 8; + } PACKED; + + /* Byte 4 */ + struct { + unsigned offset_31_24 : 8; + } PACKED; +} PACKED rogue_ctrl_ba_encoding; +static_assert(sizeof(rogue_ctrl_ba_encoding) == 5, + "sizeof(rogue_ctrl_ba_encoding) != 5"); + +typedef struct rogue_offset32 { + union { + struct { + struct { + unsigned : 1; + unsigned _7_1 : 7; + } PACKED; + + struct { + unsigned _15_8 : 8; + } PACKED; + + struct { + unsigned _23_16 : 8; + } PACKED; + + struct { + unsigned _31_24 : 8; + } PACKED; + } PACKED; + + uint32_t _; + } PACKED; +} PACKED rogue_offset32; +static_assert(sizeof(rogue_offset32) == 4, "sizeof(rogue_offset32) != 4"); + +/* NOP */ +typedef struct rogue_ctrl_nop_encoding { + /* Byte 0 */ + struct { + unsigned : 8; + } PACKED; +} PACKED rogue_ctrl_nop_encoding; +static_assert(sizeof(rogue_ctrl_nop_encoding) == 1, + "sizeof(rogue_ctrl_nop_encoding) != 1"); + +/* Common for all control instructions. */ +typedef struct rogue_ctrl_instr_encoding { + union { + /* Bytes 0+ */ + rogue_ctrl_ba_encoding ba; + rogue_ctrl_nop_encoding nop; + } PACKED; +} PACKED rogue_ctrl_instr_encoding; +static_assert(sizeof(rogue_ctrl_instr_encoding) == 5, + "sizeof(rogue_ctrl_instr_encoding) != 5"); + +enum ctrlop { + CTRLOP_BA = 0b0000, + CTRLOP_LAPC = 0b0001, + CTRLOP_SAVL = 0b0010, + CTRLOP_CND = 0b0011, + CTRLOP_WOP = 0b0100, + CTRLOP_WDF = 0b0101, + CTRLOP_MUTEX = 0b0110, + CTRLOP_NOP = 0b0111, + CTRLOP_ITRSMP = 0b1000, + CTRLOP_UNIQ = 0b1001, + CTRLOP_FETCH = 0b1010, + CTRLOP_SBO = 0b1011, +}; + +typedef struct rogue_instr_group_header_encoding { + /* Byte 0 */ + struct { + unsigned length : 4; + unsigned da : 4; + } PACKED; + + /* Byte 1 */ + union { + struct { + unsigned cc : 1; + unsigned w0p : 1; + unsigned w1p : 1; + unsigned olchk : 1; + unsigned oporg : 3; + unsigned ext : 1; + } PACKED; + + struct { + unsigned : 4; + unsigned opcnt : 3; + unsigned : 1; + } PACKED; + } PACKED; + + /* Byte 2 */ + union { + struct { + unsigned ccext : 1; + unsigned rpt : 2; + unsigned atom : 1; + unsigned crel : 1; + unsigned alutype : 2; + unsigned end : 1; + } PACKED; + + struct { + unsigned : 1; + unsigned ctrlop : 4; + unsigned : 2; + unsigned miscctl : 1; + } PACKED; + } PACKED; +} PACKED rogue_instr_group_header_encoding; +static_assert(sizeof(rogue_instr_group_header_encoding) == 3, + "sizeof(rogue_instr_group_header_encoding) != 3"); + +enum oporg { + OPORG_P0 = 0b000, + OPORG_P2 = 0b001, + OPORG_BE = 0b010, + OPORG_P0_P1 = 0b011, + OPORG_P0_P2 = 0b100, + OPORG_P0_P1_P2 = 0b101, + OPORG_P0_P2_BE = 0b110, + OPORG_P0_P1_P2_BE = 0b111, +}; + +enum opcnt { + OPCNT_P0 = 0b001, + OPCNT_P1 = 0b010, + OPCNT_P2 = 0b100, +}; + +enum alutype { + ALUTYPE_MAIN = 0b00, + ALUTYPE_BITWISE = 0b10, + ALUTYPE_CONTROL = 0b11, +}; + +enum cc { + CC_PE_TRUE = 0b00, + CC_P0_TRUE = 0b01, + CC_PE_ANY = 0b10, + CC_P0_FALSE = 0b11, +}; + +typedef struct rogue_cc { + union { + struct { + unsigned cc : 1; + unsigned ccext : 1; + unsigned : 6; + } PACKED; + + uint8_t _; + } PACKED; +} PACKED rogue_cc; +static_assert(sizeof(rogue_cc) == 1, "sizeof(rogue_cc) != 1"); + +#endif /* ROGUE_ISA_H */ diff --git a/src/imagination/rogue/rogue_nir.c b/src/imagination/rogue/rogue_nir.c index 9c6be45cc54..ac82644400e 100644 --- a/src/imagination/rogue/rogue_nir.c +++ b/src/imagination/rogue/rogue_nir.c @@ -23,14 +23,15 @@ #include "compiler/spirv/nir_spirv.h" #include "nir/nir.h" -#include "nir/nir_schedule.h" -#include "rogue_nir.h" -#include "rogue_operand.h" +#include "rogue.h" +#include "util/macros.h" + +#include /** * \file rogue_nir.c * - * \brief Contains NIR-specific functions. + * \brief Contains SPIR-V and NIR-specific functions. */ /** @@ -44,22 +45,9 @@ static const struct spirv_to_nir_options spirv_options = { }; static const nir_shader_compiler_options nir_options = { - .lower_fsat = true, .fuse_ffma32 = true, }; -const struct spirv_to_nir_options * -rogue_get_spirv_options(const struct rogue_compiler *compiler) -{ - return &spirv_options; -} - -const nir_shader_compiler_options * -rogue_get_compiler_options(const struct rogue_compiler *compiler) -{ - return &nir_options; -} - static int rogue_glsl_type_size(const struct glsl_type *type, bool bindless) { return glsl_count_attribute_slots(type, false); @@ -72,23 +60,24 @@ static int rogue_glsl_type_size(const struct glsl_type *type, bool bindless) * \param[in] ctx Shared multi-stage build context. * \param[in] shader Rogue shader. * \param[in] stage Shader stage. - * \return true if successful, otherwise false. */ -bool rogue_nir_passes(struct rogue_build_ctx *ctx, - nir_shader *nir, - gl_shader_stage stage) +static void rogue_nir_passes(struct rogue_build_ctx *ctx, + nir_shader *nir, + gl_shader_stage stage) { bool progress; +#if !defined(NDEBUG) + bool nir_debug_print_shader_prev = nir_debug_print_shader[nir->info.stage]; + nir_debug_print_shader[nir->info.stage] = ROGUE_DEBUG(NIR_PASSES); +#endif /* !defined(NDEBUG) */ + nir_validate_shader(nir, "after spirv_to_nir"); /* Splitting. */ NIR_PASS_V(nir, nir_split_var_copies); NIR_PASS_V(nir, nir_split_per_member_structs); - /* Ensure fs outputs are in the [0.0f...1.0f] range. */ - NIR_PASS_V(nir, nir_lower_clamp_color_outputs); - /* Replace references to I/O variables with intrinsics. */ NIR_PASS_V(nir, nir_lower_io, @@ -97,6 +86,7 @@ bool rogue_nir_passes(struct rogue_build_ctx *ctx, (nir_lower_io_options)0); /* Load inputs to scalars (single registers later). */ + /* TODO: Fitrp can process multiple frag inputs at once, scalarise I/O. */ NIR_PASS_V(nir, nir_lower_io_to_scalar, nir_var_shader_in); /* Optimize GL access qualifiers. */ @@ -145,13 +135,14 @@ bool rogue_nir_passes(struct rogue_build_ctx *ctx, NIR_PASS_V(nir, nir_opt_cse); } while (progress); - /* Replace SSA constant references with a register that loads the value. */ - NIR_PASS_V(nir, rogue_nir_constreg); /* Remove unused constant registers. */ NIR_PASS_V(nir, nir_opt_dce); /* Move loads to just before they're needed. */ - NIR_PASS_V(nir, nir_opt_move, nir_move_load_ubo | nir_move_load_input); + /* Disabled for now since we want to try and keep them vectorised and group + * them. */ + /* TODO: Investigate this further. */ + /* NIR_PASS_V(nir, nir_opt_move, nir_move_load_ubo | nir_move_load_input); */ /* Convert vecNs to movs so we can sequentially allocate them later. */ NIR_PASS_V(nir, nir_lower_vec_to_movs, NULL, NULL); @@ -185,6 +176,57 @@ bool rogue_nir_passes(struct rogue_build_ctx *ctx, nir_sweep(nir); nir_validate_shader(nir, "after passes"); + if (ROGUE_DEBUG(NIR)) { + fputs("after passes\n", stdout); + nir_print_shader(nir, stdout); + } - return true; +#if !defined(NDEBUG) + nir_debug_print_shader[nir->info.stage] = nir_debug_print_shader_prev; +#endif /* !defined(NDEBUG) */ +} + +/** + * \brief Converts a SPIR-V shader to NIR. + * + * \param[in] ctx Shared multi-stage build context. + * \param[in] entry Shader entry-point function name. + * \param[in] stage Shader stage. + * \param[in] spirv_size SPIR-V data length in DWORDs. + * \param[in] spirv_data SPIR-V data. + * \param[in] num_spec Number of SPIR-V specializations. + * \param[in] spec SPIR-V specializations. + * \return A nir_shader* if successful, or NULL if unsuccessful. + */ +PUBLIC +nir_shader *rogue_spirv_to_nir(rogue_build_ctx *ctx, + gl_shader_stage stage, + const char *entry, + unsigned spirv_size, + const uint32_t *spirv_data, + unsigned num_spec, + struct nir_spirv_specialization *spec) +{ + nir_shader *nir; + + nir = spirv_to_nir(spirv_data, + spirv_size, + spec, + num_spec, + stage, + entry, + &spirv_options, + &nir_options); + if (!nir) + return NULL; + + ralloc_steal(ctx, nir); + + /* Apply passes. */ + rogue_nir_passes(ctx, nir, stage); + + /* Collect I/O data to pass back to the driver. */ + rogue_collect_io_data(ctx, nir); + + return nir; } diff --git a/src/imagination/rogue/rogue_nir.h b/src/imagination/rogue/rogue_nir.h deleted file mode 100644 index 580e5bf3d80..00000000000 --- a/src/imagination/rogue/rogue_nir.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright © 2022 Imagination Technologies Ltd. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef ROGUE_NIR_H -#define ROGUE_NIR_H - -#include "compiler/shader_enums.h" -#include "nir/nir.h" -#include "util/macros.h" - -struct rogue_build_ctx; -struct rogue_compiler; -struct spirv_to_nir_options; - -PUBLIC -const struct spirv_to_nir_options * -rogue_get_spirv_options(const struct rogue_compiler *compiler); - -PUBLIC -const nir_shader_compiler_options * -rogue_get_compiler_options(const struct rogue_compiler *compiler); - -bool rogue_nir_passes(struct rogue_build_ctx *ctx, - nir_shader *nir, - gl_shader_stage stage); - -/* Custom passes. */ -void rogue_nir_pfo(nir_shader *shader); -void rogue_nir_constreg(nir_shader *shader); -bool rogue_nir_lower_io(nir_shader *shader, void *layout); - -#endif /* ROGUE_NIR_H */ diff --git a/src/imagination/rogue/rogue_nir_helpers.h b/src/imagination/rogue/rogue_nir_helpers.h deleted file mode 100644 index 85fc6db6f52..00000000000 --- a/src/imagination/rogue/rogue_nir_helpers.h +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright © 2022 Imagination Technologies Ltd. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef ROGUE_NIR_HELPERS_H -#define ROGUE_NIR_HELPERS_H - -#include -#include -#include - -#include "nir/nir.h" -#include "util/bitscan.h" - -/** - * \file rogue_nir.c - * - * \brief Contains various NIR helper functions. - */ - -static inline unsigned nir_alu_dest_regindex(const nir_alu_instr *alu) -{ - assert(!alu->dest.dest.is_ssa); - - return alu->dest.dest.reg.reg->index; -} - -static inline unsigned nir_alu_dest_comp(const nir_alu_instr *alu) -{ - assert(!alu->dest.dest.is_ssa); - assert(util_is_power_of_two_nonzero(alu->dest.write_mask)); - - return ffs(alu->dest.write_mask) - 1; -} - -static inline unsigned nir_alu_src_regindex(const nir_alu_instr *alu, - size_t src) -{ - assert(src < nir_op_infos[alu->op].num_inputs); - assert(!alu->src[src].src.is_ssa); - - return alu->src[src].src.reg.reg->index; -} - -static inline uint32_t nir_alu_src_const(const nir_alu_instr *alu, size_t src) -{ - assert(src < nir_op_infos[alu->op].num_inputs); - assert(alu->src[src].src.is_ssa); - - nir_const_value *const_value = nir_src_as_const_value(alu->src[src].src); - - return nir_const_value_as_uint(*const_value, 32); -} - -static inline bool nir_alu_src_is_const(const nir_alu_instr *alu, size_t src) -{ - assert(src < nir_op_infos[alu->op].num_inputs); - - if (!alu->src[src].src.is_ssa) - return false; - - assert(alu->src[src].src.ssa->parent_instr); - - return (alu->src[src].src.ssa->parent_instr->type == - nir_instr_type_load_const); -} - -static inline unsigned nir_intr_dest_regindex(const nir_intrinsic_instr *intr) -{ - assert(!intr->dest.is_ssa); - - return intr->dest.reg.reg->index; -} - -static inline unsigned nir_intr_src_regindex(const nir_intrinsic_instr *intr, - size_t src) -{ - assert(src < nir_intrinsic_infos[intr->intrinsic].num_srcs); - assert(!intr->src[src].is_ssa); - - return intr->src[src].reg.reg->index; -} - -static inline uint32_t nir_intr_src_const(const nir_intrinsic_instr *intr, - size_t src) -{ - assert(src < nir_intrinsic_infos[intr->intrinsic].num_srcs); - assert(intr->src[src].is_ssa); - - nir_const_value *const_value = nir_src_as_const_value(intr->src[src]); - - return nir_const_value_as_uint(*const_value, 32); -} - -static inline uint32_t nir_intr_src_comp_const(const nir_intrinsic_instr *intr, - size_t src, - size_t comp) -{ - assert(src < nir_intrinsic_infos[intr->intrinsic].num_srcs); - assert(intr->src[src].is_ssa); - assert(comp < nir_src_num_components(intr->src[src])); - - return nir_src_comp_as_uint(intr->src[src], comp); -} - -static inline bool nir_intr_src_is_const(const nir_intrinsic_instr *intr, - size_t src) -{ - assert(src < nir_intrinsic_infos[intr->intrinsic].num_srcs); - - if (!intr->src[src].is_ssa) - return false; - - assert(intr->src[src].ssa->parent_instr); - - return (intr->src[src].ssa->parent_instr->type == nir_instr_type_load_const); -} - -static inline size_t nir_count_variables_with_modes(const nir_shader *nir, - nir_variable_mode mode) -{ - size_t count = 0; - - nir_foreach_variable_with_modes (var, nir, mode) - ++count; - - return count; -} - -#endif /* ROGUE_NIR_HELPERS_H */ diff --git a/src/imagination/rogue/rogue_operand.h b/src/imagination/rogue/rogue_operand.h deleted file mode 100644 index 753f09053d7..00000000000 --- a/src/imagination/rogue/rogue_operand.h +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright © 2022 Imagination Technologies Ltd. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef ROGUE_OPERAND_H -#define ROGUE_OPERAND_H - -#include -#include - -#include "rogue_util.h" -#include "util/macros.h" - -/* Register-related defines. */ - -/* Total max number of registers per class - * (instances > ROGUE_MAX_REG_INDEX addressable via indexing only). - */ -#define ROGUE_MAX_REG_TEMP 248 -#define ROGUE_MAX_REG_COEFF 4096 -#define ROGUE_MAX_REG_CONST 240 -#define ROGUE_MAX_REG_SHARED 4096 -#define ROGUE_MAX_REG_PIXEL_OUT 8 -#define ROGUE_MAX_REG_VERTEX_IN 248 -#define ROGUE_MAX_REG_INTERNAL 8 - -/* Maximum register index via offset encoding. */ -#define ROGUE_MAX_REG_INDEX 256 - -/* Pixel-out register offset. */ -#define ROGUE_PIXEL_OUT_REG_OFFSET 32 - -/* Internal register offset. */ -#define ROGUE_INTERNAL_REG_OFFSET 36 - -/* Coefficient registers are typically used in groups of 4. */ -#define ROGUE_COEFF_ALIGN 4 - -/* Defines for other operand types. */ - -/* Available dependent read counters. */ -#define ROGUE_NUM_DRCS 2 - -/* Maximum number of vertex outputs. */ -#define ROGUE_MAX_VERTEX_OUTPUTS 256 - -/* All components of an emulated vec4 register group. */ -#define ROGUE_COMPONENT_ALL (~0) - -/** - * \brief Operand types. - */ -enum rogue_operand_type { - /* Register operands. */ - ROGUE_OPERAND_TYPE_REG_TEMP = 0, /** Temporary register. */ - ROGUE_OPERAND_TYPE_REG_COEFF, /** Coefficient register. */ - ROGUE_OPERAND_TYPE_REG_CONST, /** Constant register. */ - ROGUE_OPERAND_TYPE_REG_SHARED, /** Shared register. */ - ROGUE_OPERAND_TYPE_REG_PIXEL_OUT, /** Pixel output register. */ - ROGUE_OPERAND_TYPE_REG_VERTEX_IN, /** Vertex input register. */ - ROGUE_OPERAND_TYPE_REG_INTERNAL, /** Internal register. */ - - ROGUE_OPERAND_TYPE_REG_MAX = ROGUE_OPERAND_TYPE_REG_INTERNAL, - - ROGUE_OPERAND_TYPE_IMMEDIATE, /** Immediate value. */ - - ROGUE_OPERAND_TYPE_DRC, /** Dependent read counter. */ - - ROGUE_OPERAND_TYPE_VREG, /** Virtual register (pre-regalloc). */ - - ROGUE_OPERAND_TYPE_COUNT, -}; - -/* clang-format off */ - -#define ROGUE_NUM_REG_TYPES (ROGUE_OPERAND_TYPE_REG_MAX + 1) - -/** - * \brief A bitmask for any register operand type. - */ -#define ROGUE_MASK_ANY_REG \ - ROH(ROGUE_OPERAND_TYPE_REG_TEMP) | \ - ROH(ROGUE_OPERAND_TYPE_REG_COEFF) | \ - ROH(ROGUE_OPERAND_TYPE_REG_CONST) | \ - ROH(ROGUE_OPERAND_TYPE_REG_PIXEL_OUT) | \ - ROH(ROGUE_OPERAND_TYPE_REG_VERTEX_IN) | \ - ROH(ROGUE_OPERAND_TYPE_REG_SHARED) | \ - ROH(ROGUE_OPERAND_TYPE_REG_INTERNAL) - -/* clang-format on */ - -/** - * \brief Operand description. - */ -struct rogue_operand { - enum rogue_operand_type type; - - union { - struct { - uint64_t value; - } immediate; - - struct { - size_t number; - } drc; - - struct { - size_t number; - } reg; - - struct { - size_t number; - bool is_vector; - size_t component; - } vreg; - }; -}; - -/** - * \brief Register access flags. - */ -enum rogue_register_access { - ROGUE_REG_ACCESS_READ = BITFIELD_BIT(0U), /** Read-only. */ - ROGUE_REG_ACCESS_WRITE = BITFIELD_BIT(1U), /* Write-only. */ - ROGUE_REG_ACCESS_RW = ROGUE_REG_ACCESS_READ | - ROGUE_REG_ACCESS_WRITE, /** Read/write. */ -}; - -/** - * \brief Register modifier flags. - */ -enum rogue_register_modifier { - ROGUE_REG_MOD_NONE = 0U, - ROGUE_REG_MOD_IDX = BITFIELD_BIT(0U), /** Index modifier. */ - ROGUE_REG_MOD_DIM = BITFIELD_BIT(1U), /** Dimension modifier. */ - ROGUE_REG_MOD_ALL = ROGUE_REG_MOD_IDX | ROGUE_REG_MOD_DIM, -}; - -#endif /* ROGUE_OPERAND_H */ diff --git a/src/imagination/rogue/rogue_print.c b/src/imagination/rogue/rogue_print.c new file mode 100644 index 00000000000..b185869668b --- /dev/null +++ b/src/imagination/rogue/rogue_print.c @@ -0,0 +1,751 @@ +/* + * Copyright © 2022 Imagination Technologies Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "compiler/shader_enums.h" +#include "rogue.h" +#include "util/bitscan.h" +#include "util/macros.h" + +#include +#include + +/** + * \file rogue_print.c + * + * \brief Contains functions to print Rogue IR types and structures. + */ + +/* TODO NEXT: Go through and make types the same, i.e. decide on using ONLY + * unsigned, uint32/64_t, etc., and then use inttypes if so */ +/* TODO NEXT: Make fp the last argument. */ + +enum color_esc { + ESC_RESET = 0, + ESC_BLACK, + ESC_RED, + ESC_GREEN, + ESC_YELLOW, + ESC_BLUE, + ESC_PURPLE, + ESC_CYAN, + ESC_WHITE, + + ESC_COUNT, +}; + +static +const char *color_esc[2][ESC_COUNT] = { + [0] = { + [ESC_RESET] = "", + [ESC_BLACK] = "", + [ESC_RED] = "", + [ESC_GREEN] = "", + [ESC_YELLOW] = "", + [ESC_BLUE] = "", + [ESC_PURPLE] = "", + [ESC_CYAN] = "", + [ESC_WHITE] = "", + }, + [1] = { + [ESC_RESET] = "\033[0m", + [ESC_BLACK] = "\033[0;30m", + [ESC_RED] = "\033[0;31m", + [ESC_GREEN] = "\033[0;32m", + [ESC_YELLOW] = "\033[0;33m", + [ESC_BLUE] = "\033[0;34m", + [ESC_PURPLE] = "\033[0;35m", + [ESC_CYAN] = "\033[0;36m", + [ESC_WHITE] = "\033[0;37m", + }, +}; + +static inline void RESET(FILE *fp) +{ + fputs(color_esc[rogue_color][ESC_RESET], fp); +} + +static inline void BLACK(FILE *fp) +{ + fputs(color_esc[rogue_color][ESC_BLACK], fp); +} + +static inline void RED(FILE *fp) +{ + fputs(color_esc[rogue_color][ESC_RED], fp); +} + +static inline void GREEN(FILE *fp) +{ + fputs(color_esc[rogue_color][ESC_GREEN], fp); +} + +static inline void YELLOW(FILE *fp) +{ + fputs(color_esc[rogue_color][ESC_YELLOW], fp); +} + +static inline void BLUE(FILE *fp) +{ + fputs(color_esc[rogue_color][ESC_BLUE], fp); +} + +static inline void PURPLE(FILE *fp) +{ + fputs(color_esc[rogue_color][ESC_PURPLE], fp); +} + +static inline void CYAN(FILE *fp) +{ + fputs(color_esc[rogue_color][ESC_CYAN], fp); +} + +static inline void WHITE(FILE *fp) +{ + fputs(color_esc[rogue_color][ESC_WHITE], fp); +} + +static inline void rogue_print_val(FILE *fp, unsigned val) +{ + PURPLE(fp); + fprintf(fp, "%u", val); + RESET(fp); +} + +static inline void rogue_print_reg(FILE *fp, const rogue_reg *reg) +{ + const rogue_reg_info *info = &rogue_reg_infos[reg->class]; + YELLOW(fp); + fprintf(fp, "%s%" PRIu32, info->str, reg->index); + RESET(fp); +} + +static inline void rogue_print_regarray(FILE *fp, + const rogue_regarray *regarray) +{ + const rogue_reg *reg = regarray->regs[0]; + const rogue_reg_info *info = &rogue_reg_infos[reg->class]; + YELLOW(fp); + fprintf(fp, "%s%" PRIu32, info->str, reg->index); + RESET(fp); + fputs("-", fp); + YELLOW(fp); + fprintf(fp, "%" PRIu32, regarray->size + reg->index - 1); + RESET(fp); +} + +static inline void rogue_print_imm(FILE *fp, const rogue_imm *imm) +{ + PURPLE(fp); + fprintf(fp, "0x%" PRIx32, imm->imm.u32); + RESET(fp); +} + +static inline void rogue_print_io(FILE *fp, enum rogue_io io) +{ + const rogue_io_info *info = &rogue_io_infos[io]; + BLUE(fp); + fprintf(fp, "%s", info->str); + RESET(fp); +} + +static inline void rogue_print_drc(FILE *fp, const rogue_drc *drc) +{ + RED(fp); + fprintf(fp, "drc%u", drc->index); + RESET(fp); +} + +static inline void rogue_print_ref(FILE *fp, const rogue_ref *ref) +{ + switch (ref->type) { + case ROGUE_REF_TYPE_VAL: + rogue_print_val(fp, ref->val); + break; + + case ROGUE_REF_TYPE_REG: + rogue_print_reg(fp, ref->reg); + break; + + case ROGUE_REF_TYPE_REGARRAY: + rogue_print_regarray(fp, ref->regarray); + break; + + case ROGUE_REF_TYPE_IMM: + rogue_print_imm(fp, &ref->imm); + break; + + case ROGUE_REF_TYPE_IO: + rogue_print_io(fp, ref->io); + break; + + case ROGUE_REF_TYPE_DRC: + rogue_print_drc(fp, &ref->drc); + break; + + default: + unreachable("Invalid ref type."); + } +} + +static inline void rogue_print_alu_dst(FILE *fp, const rogue_alu_dst *dst) +{ + rogue_print_ref(fp, &dst->ref); + + uint64_t mod = dst->mod; + while (mod) { + enum rogue_alu_dst_mod dst_mod = u_bit_scan64(&mod); + assert(dst_mod < ROGUE_ALU_DST_MOD_COUNT); + fprintf(fp, ".%s", rogue_alu_dst_mod_infos[dst_mod].str); + } +} + +static inline void rogue_print_alu_src(FILE *fp, const rogue_alu_src *src) +{ + rogue_print_ref(fp, &src->ref); + + uint64_t mod = src->mod; + while (mod) { + enum rogue_alu_src_mod src_mod = u_bit_scan64(&mod); + assert(src_mod < ROGUE_ALU_SRC_MOD_COUNT); + fprintf(fp, ".%s", rogue_alu_src_mod_infos[src_mod].str); + } +} + +static inline void rogue_print_alu_mods(FILE *fp, const rogue_alu_instr *alu) +{ + uint64_t mod = alu->mod; + while (mod) { + enum rogue_alu_op_mod op_mod = u_bit_scan64(&mod); + assert(op_mod < ROGUE_ALU_OP_MOD_COUNT); + fprintf(fp, ".%s", rogue_alu_op_mod_infos[op_mod].str); + } +} + +static inline void rogue_print_alu_instr(FILE *fp, const rogue_alu_instr *alu) +{ + const rogue_alu_op_info *info = &rogue_alu_op_infos[alu->op]; + + /* TODO: Print conditional info once supported. */ + + fprintf(fp, "%s", info->str); + + if (alu->op == ROGUE_ALU_OP_TST) { + fprintf(fp, "%s", rogue_comp_test_str[alu->comp_test]); + fprintf(fp, ".%s", rogue_comp_type_str[alu->comp_type]); + } + + rogue_print_alu_mods(fp, alu); + + fputs(" ", fp); + + rogue_print_alu_dst(fp, &alu->dst); + + for (unsigned i = 0; i < info->num_srcs; ++i) { + fputs(", ", fp); + rogue_print_alu_src(fp, &alu->src[i]); + } +} + +static inline void rogue_print_block_label(FILE *fp, const rogue_block *block) +{ + /* For debug purposes. */ + if (block->label) + fprintf(fp, "%s", block->label); + else + fprintf(fp, "block%u", block->index); +} + +static inline void rogue_print_backend_dst(FILE *fp, + const rogue_backend_dst *dst) +{ + rogue_print_ref(fp, &dst->ref); +} + +static inline void rogue_print_backend_src(FILE *fp, + const rogue_backend_src *src) +{ + rogue_print_ref(fp, &src->ref); +} + +static inline void rogue_print_backend_mods(FILE *fp, + const rogue_backend_instr *backend) +{ + uint64_t mod = backend->mod; + while (mod) { + enum rogue_backend_op_mod op_mod = u_bit_scan64(&mod); + assert(op_mod < ROGUE_BACKEND_OP_MOD_COUNT); + fprintf(fp, ".%s", rogue_backend_op_mod_infos[op_mod].str); + } +} + +static inline void rogue_print_backend_instr(FILE *fp, + const rogue_backend_instr *backend) +{ + const rogue_backend_op_info *info = &rogue_backend_op_infos[backend->op]; + + fprintf(fp, "%s", info->str); + + rogue_print_backend_mods(fp, backend); + + for (unsigned i = 0; i < info->num_dsts; ++i) { + if (i > 0) + fputs(",", fp); + + fputs(" ", fp); + + rogue_print_backend_dst(fp, &backend->dst[i]); + } + + for (unsigned i = 0; i < info->num_srcs; ++i) { + if (i == 0 && !info->num_dsts) + fputs(" ", fp); + else + fputs(", ", fp); + + rogue_print_backend_src(fp, &backend->src[i]); + } +} + +static inline void rogue_print_ctrl_mods(FILE *fp, const rogue_ctrl_instr *ctrl) +{ + uint64_t mod = ctrl->mod; + while (mod) { + enum rogue_ctrl_op_mod op_mod = u_bit_scan64(&mod); + assert(op_mod < ROGUE_CTRL_OP_MOD_COUNT); + fprintf(fp, ".%s", rogue_ctrl_op_mod_infos[op_mod].str); + } +} + +static inline void rogue_print_ctrl_src(FILE *fp, const rogue_ctrl_src *src) +{ + rogue_print_ref(fp, &src->ref); +} + +static inline void rogue_print_ctrl_instr(FILE *fp, + const rogue_ctrl_instr *ctrl) +{ + const rogue_ctrl_op_info *info = &rogue_ctrl_op_infos[ctrl->op]; + + /* TODO: Print conditional info once supported. */ + + fprintf(fp, "%s", info->str); + + rogue_print_ctrl_mods(fp, ctrl); + + if (ctrl->target_block) { + fputs(" ", fp); + rogue_print_block_label(fp, ctrl->target_block); + } + + /* TODO NEXT: Dests. */ + /* TODO: Special case for the conditional ctrl instructions as they're + * printed as source 0, then dest, then rest of the sources. */ + + for (unsigned i = 0; i < info->num_srcs; ++i) { + if (i == 0 && !info->num_dsts) + fputs(" ", fp); + else + fputs(", ", fp); + + rogue_print_ctrl_src(fp, &ctrl->src[i]); + } +} + +PUBLIC +void rogue_print_instr(FILE *fp, const rogue_instr *instr) +{ + if (instr->repeat > 1) + fprintf(fp, "(rpt%u) ", instr->repeat); + + GREEN(fp); + switch (instr->type) { + case ROGUE_INSTR_TYPE_ALU: + rogue_print_alu_instr(fp, rogue_instr_as_alu(instr)); + break; + + case ROGUE_INSTR_TYPE_BACKEND: + rogue_print_backend_instr(fp, rogue_instr_as_backend(instr)); + break; + + case ROGUE_INSTR_TYPE_CTRL: + rogue_print_ctrl_instr(fp, rogue_instr_as_ctrl(instr)); + break; + + default: + unreachable("Invalid instruction type."); + } + RESET(fp); + + if (instr->end) + fputs(".end", fp); + + /* For debug purposes. */ + fputs(";", fp); + + if (instr->comment) + fprintf(fp, " /* %s */", instr->comment); +} + +/* TODO NEXT: Split this up into separate functions for printing lower srcs, + * upper srcs, etc. since we'd want to print them in-between instructions. */ +/* TODO NEXT: Commonise with printing the ref io stuff. */ +static inline void +rogue_print_instr_group_io_sel(FILE *fp, const rogue_instr_group_io_sel *io_sel) +{ + bool present = false; + + fputs(" ", fp); + + /* TODO NEXT: Commonise this code!! */ + /* Print upper and lower sources. */ + for (unsigned i = 0; i < ARRAY_SIZE(io_sel->srcs); ++i) { + if (rogue_ref_is_null(&io_sel->srcs[i])) + continue; + + if (present && i > 0) + fputs(", ", fp); + + present = true; + + rogue_print_io(fp, ROGUE_IO_S0 + i); + fputs("=", fp); + + if (rogue_ref_is_reg(&io_sel->srcs[i])) + rogue_print_reg(fp, io_sel->srcs[i].reg); + else if (rogue_ref_is_regarray(&io_sel->srcs[i])) + rogue_print_regarray(fp, io_sel->srcs[i].regarray); + else if (rogue_ref_is_io(&io_sel->srcs[i])) + rogue_print_io(fp, io_sel->srcs[i].io); + else + unreachable("Invalid src map."); + } + if (present) + fputs(" ", fp); + + /* Print internal sources. */ + present = false; + for (unsigned i = 0; i < ARRAY_SIZE(io_sel->iss); ++i) { + if (rogue_ref_is_null(&io_sel->iss[i])) + continue; + + if (present && i > 0) + fputs(", ", fp); + + present = true; + + rogue_print_io(fp, ROGUE_IO_IS0 + i); + fputs("=", fp); + + if (rogue_ref_is_reg(&io_sel->iss[i])) + rogue_print_reg(fp, io_sel->iss[i].reg); + else if (rogue_ref_is_regarray(&io_sel->iss[i])) + rogue_print_regarray(fp, io_sel->iss[i].regarray); + else if (rogue_ref_is_io(&io_sel->iss[i])) + rogue_print_io(fp, io_sel->iss[i].io); + else + unreachable("Invalid iss map."); + } + if (present) + fputs(" ", fp); + + /* Print destinations. */ + present = false; + for (unsigned i = 0; i < ARRAY_SIZE(io_sel->dsts); ++i) { + if (rogue_ref_is_null(&io_sel->dsts[i])) + continue; + + if (present && i > 0) + fputs(", ", fp); + + present = true; + + rogue_print_io(fp, ROGUE_IO_W0 + i); + fputs("=", fp); + + if (rogue_ref_is_reg(&io_sel->dsts[i])) + rogue_print_reg(fp, io_sel->dsts[i].reg); + else if (rogue_ref_is_io(&io_sel->dsts[i])) + rogue_print_io(fp, io_sel->dsts[i].io); + else + unreachable("Invalid dst map."); + } + if (present) + fputs(" ", fp); +} + +static inline void rogue_print_instr_phase(FILE *fp, + enum rogue_alu alu, + enum rogue_instr_phase phase) +{ + const char *phase_str = rogue_instr_phase_str[alu][phase]; + assert(phase_str); + fputs(phase_str, fp); +} + +static inline void +rogue_print_instr_group_header(FILE *fp, const rogue_instr_group *group) +{ + /* ALU specific */ + switch (group->header.alu) { + case ROGUE_ALU_MAIN: + break; + + case ROGUE_ALU_BITWISE: + break; + + case ROGUE_ALU_CONTROL: + break; + + default: + unreachable("Invalid instruction group ALU."); + } + + if (group->header.end) + fputs(".end", fp); +} + +static inline void rogue_print_instr_group(FILE *fp, + const rogue_instr_group *group) +{ + /* For debug purposes. */ + fprintf(fp, "%u", group->index); + fputs(": ", fp); + + if (group->header.repeat > 1) + fprintf(fp, "(rpt%u) ", group->header.repeat); + + fputs("{ ", fp); + + CYAN(fp); + fprintf(fp, "%s ", rogue_alu_str[group->header.alu]); + RESET(fp); + + /* Print each instruction. */ + rogue_foreach_phase_in_set (p, group->header.phases) { + const rogue_instr *instr = group->instrs[p]; + assert(instr); + + rogue_print_instr_phase(fp, group->header.alu, p); + fputs(": ", fp); + rogue_print_instr(fp, instr); + } + + /* Print source/dest mappings (if present). */ + rogue_print_instr_group_io_sel(fp, &group->io_sel); + + fputs("}", fp); + + /* Print group header info. */ + rogue_print_instr_group_header(fp, group); +} + +static inline void rogue_print_block(FILE *fp, const rogue_block *block) +{ + rogue_print_block_label(fp, block); + fputs(":\n", fp); + + if (!block->shader->is_grouped) { + rogue_foreach_instr_in_block (instr, block) { + fputs("\t", fp); + fprintf(fp, "%u", instr->index); + fputs(": ", fp); + fprintf(fp, "%s: ", rogue_instr_type_str[instr->type]); + rogue_print_instr(fp, instr); + fputs("\n", fp); + } + } else { + rogue_foreach_instr_group_in_block (group, block) { + fputs("\t", fp); + rogue_print_instr_group(fp, group); + fputs("\n", fp); + } + } +} + +PUBLIC +void rogue_print_shader(FILE *fp, const rogue_shader *shader) +{ + fputs("/*", fp); + + if (shader->name) + fprintf(fp, " \"%s\":", shader->name); + + fprintf(fp, " %s shader */\n", _mesa_shader_stage_to_string(shader->stage)); + + rogue_foreach_block (block, shader) + rogue_print_block(fp, block); +} + +static void rogue_print_instr_ref(FILE *fp, + const rogue_instr *instr, + bool dst, + unsigned index, + bool is_grouped) +{ + if (is_grouped) { + fprintf(fp, "%u", instr->group->index); + fputs(": { ", fp); + rogue_print_instr_phase(fp, instr->group->header.alu, instr->index); + } else { + fprintf(fp, "%u", instr->index); + if (index != ~0) + fputs(": ", fp); + } + + if (index != ~0) { + BLUE(fp); + fprintf(fp, "[%s%u]", dst ? "dst" : "src", index); + RESET(fp); + } + + if (is_grouped) + fputs(" }", fp); +} + +PUBLIC +void rogue_print_reg_writes(FILE *fp, const rogue_shader *shader) +{ + fputs("/* register writes */\n", fp); + for (enum rogue_reg_class class = 0; class < ROGUE_REG_CLASS_COUNT; + ++class) { + rogue_foreach_reg (reg, shader, class) { + rogue_print_reg(fp, reg); + fputs(":", fp); + + if (list_is_empty(®->writes)) { + fputs(" \n", fp); + continue; + } + + rogue_foreach_reg_write (write, reg) { + assert(write->instr); + + fputs(" ", fp); + rogue_print_instr_ref(fp, + write->instr, + true, + write->dst_index, + shader->is_grouped); + } + + fputs("\n", fp); + } + } +} + +PUBLIC +void rogue_print_reg_uses(FILE *fp, const rogue_shader *shader) +{ + fputs("/* register uses */\n", fp); + for (enum rogue_reg_class class = 0; class < ROGUE_REG_CLASS_COUNT; + ++class) { + rogue_foreach_reg (reg, shader, class) { + rogue_print_reg(fp, reg); + fputs(":", fp); + + if (list_is_empty(®->uses)) { + fputs(" \n", fp); + continue; + } + + rogue_foreach_reg_use (use, reg) { + assert(use->instr); + + fputs(" ", fp); + rogue_print_instr_ref(fp, + use->instr, + false, + use->src_index, + shader->is_grouped); + } + + fputs("\n", fp); + } + } +} + +PUBLIC +void rogue_print_block_uses(FILE *fp, const rogue_shader *shader) +{ + fputs("/* block uses */\n", fp); + rogue_foreach_block (block, shader) { + rogue_print_block_label(fp, block); + fputs(":", fp); + + if (list_is_empty(&block->uses)) { + if (list_first_entry(&shader->blocks, rogue_block, link) == block) + fputs(" \n", fp); + else + fputs(" \n", fp); + + continue; + } + + rogue_foreach_block_use (use, block) { + assert(use->instr); + + fputs(" ", fp); + rogue_print_instr_ref(fp, use->instr, false, ~0, shader->is_grouped); + } + + fputs("\n", fp); + } +} + +static void rogue_print_drc_trxn(FILE *fp, + const rogue_shader *shader, + const rogue_drc_trxn *drc_trxn, + unsigned index) +{ + fprintf(fp, "drc%u: ack: ", index); + + rogue_print_instr_ref(fp, drc_trxn->acquire, false, ~0, shader->is_grouped); + + fputs(", rel: ", fp); + + if (drc_trxn->release) { + rogue_print_instr_ref(fp, + drc_trxn->release, + false, + ~0, + shader->is_grouped); + } else { + fputs("", fp); + } + + fputs("\n", fp); +} + +PUBLIC +void rogue_print_drc_trxns(FILE *fp, const rogue_shader *shader) +{ + fputs("/* DRC transactions */\n", fp); + + rogue_foreach_drc_trxn (drc_trxn, shader, 0) { + rogue_print_drc_trxn(fp, shader, drc_trxn, 0); + } + + rogue_foreach_drc_trxn (drc_trxn, shader, 1) { + rogue_print_drc_trxn(fp, shader, drc_trxn, 1); + } +} diff --git a/src/imagination/rogue/rogue_regalloc.c b/src/imagination/rogue/rogue_regalloc.c deleted file mode 100644 index f18e44322f6..00000000000 --- a/src/imagination/rogue/rogue_regalloc.c +++ /dev/null @@ -1,313 +0,0 @@ -/* - * Copyright © 2022 Imagination Technologies Ltd. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include - -#include "rogue_operand.h" -#include "rogue_regalloc.h" -#include "rogue_shader.h" -#include "rogue_util.h" -#include "util/hash_table.h" -#include "util/list.h" -#include "util/ralloc.h" -#include "util/register_allocate.h" -#include "util/u_dynarray.h" - -/** - * \file rogue_regalloc.c - * - * \brief Contains register allocation helper functions. - */ - -/** - * \brief Sets up the register data with the classes to be used for allocation. - * - * \param[in] data The register data array. - */ -static void -rogue_reg_data_init(struct rogue_reg_data data[static ROGUE_REG_CLASS_COUNT]) -{ - data[ROGUE_REG_CLASS_TEMP].type = ROGUE_OPERAND_TYPE_REG_TEMP; - data[ROGUE_REG_CLASS_TEMP].count = ROGUE_MAX_REG_TEMP; - data[ROGUE_REG_CLASS_TEMP].stride = 1; - - data[ROGUE_REG_CLASS_VEC4].type = ROGUE_OPERAND_TYPE_REG_INTERNAL; - data[ROGUE_REG_CLASS_VEC4].count = ROGUE_MAX_REG_INTERNAL; - data[ROGUE_REG_CLASS_VEC4].stride = 4; -} - -/** - * \brief Initializes the Rogue register allocation context. - * - * \param[in] mem_ctx The memory context for the ra context. - * \return A rogue_ra * if successful, or NULL if unsuccessful. - */ -struct rogue_ra *rogue_ra_init(void *mem_ctx) -{ - struct rogue_ra *ra; - size_t total_regs = 0; - - ra = rzalloc_size(mem_ctx, sizeof(*ra)); - if (!ra) - return NULL; - - /* Initialize the register class data. */ - rogue_reg_data_init(ra->reg_data); - - /* Count up the registers classes and set up their offsets. - * - * The physical register numbers are sequential, even if the - * registers are from different banks, so keeping track of - * the offset means we can get the true physical register - * number back after allocation. - */ - for (size_t u = 0; u < ARRAY_SIZE(ra->reg_data); ++u) { - ra->reg_data[u].offset = total_regs; - total_regs += ra->reg_data[u].count; - } - - /* Create a register set for allocation. */ - ra->regs = ra_alloc_reg_set(ra, total_regs, true); - if (!ra->regs) { - ralloc_free(ra); - return NULL; - } - - /* Create the register class for the temps. */ - ra->reg_data[ROGUE_REG_CLASS_TEMP].class = - ra_alloc_contig_reg_class(ra->regs, 1); - - /* Create the register class for vec4 registers - * (using the internal register bank). - */ - ra->reg_data[ROGUE_REG_CLASS_VEC4].class = - ra_alloc_contig_reg_class(ra->regs, 4); - - /* Populate the register classes. */ - for (size_t u = 0; u < ARRAY_SIZE(ra->reg_data); ++u) { - struct rogue_reg_data *reg_data = &ra->reg_data[u]; - size_t offset = reg_data->offset; - size_t end = reg_data->offset + reg_data->count; - size_t stride = reg_data->stride; - - for (size_t r = offset; r < end; r += stride) - ra_class_add_reg(reg_data->class, r); - } - - /* Finalize the set (no early conflicts passed along for now). */ - ra_set_finalize(ra->regs, NULL); - - return ra; -} - -/** - * \brief The range for which a (virtual) register is live, and its references. - */ -struct live_range { - size_t start; - size_t end; - enum rogue_reg_class class; - struct util_dynarray operand_refs; -}; - -/** - * \brief Performs register allocation. - * - * \param[in] instr_list A linked list of instructions with virtual registers to - * be allocated. - * \param[in] ra The register allocation context. - */ -bool rogue_ra_alloc(struct list_head *instr_list, - struct rogue_ra *ra, - size_t *temps_used, - size_t *internals_used) -{ - /* Used for ra_alloc_interference_graph() as it doesn't - * like having gaps (e.g. with v0, v2 count = 3 rather - * than 2). - */ - size_t max_vreg = 0; - - struct hash_table *reg_ht = - _mesa_hash_table_create(ra, _mesa_hash_uint, _mesa_key_uint_equal); - if (!reg_ht) - return false; - - /* Calculate live ranges for virtual registers. */ - size_t ip = 0U; /* "Instruction pointer". */ - foreach_instr (instr, instr_list) { - for (size_t u = 0U; u < instr->num_operands; ++u) { - struct hash_entry *entry; - struct live_range *range; - - if (instr->operands[u].type != ROGUE_OPERAND_TYPE_VREG) - continue; - - entry = - _mesa_hash_table_search(reg_ht, &instr->operands[u].vreg.number); - if (!entry) { - /* First use of this virtual register: initialize live range. */ - /* TODO: Error handling. */ - range = rzalloc_size(reg_ht, sizeof(*range)); - - range->start = ip; - range->end = ip; - range->class = instr->operands[u].vreg.is_vector - ? ROGUE_REG_CLASS_VEC4 - : ROGUE_REG_CLASS_TEMP; - - entry = _mesa_hash_table_insert(reg_ht, - &instr->operands[u].vreg.number, - range); - - max_vreg = MAX2(max_vreg, instr->operands[u].vreg.number); - - util_dynarray_init(&range->operand_refs, range); - } else { - /* Subsequent uses: update live range end. */ - range = entry->data; - range->end = MAX2(range->end, ip); - assert(range->class == (instr->operands[u].vreg.is_vector - ? ROGUE_REG_CLASS_VEC4 - : ROGUE_REG_CLASS_TEMP)); - } - - /* Save a reference to the operand. */ - util_dynarray_append(&range->operand_refs, - struct rogue_operand *, - &instr->operands[u]); - } - ++ip; - } - - /* Initialize the interference graph. */ - struct ra_graph *g = ra_alloc_interference_graph(ra->regs, max_vreg + 1); - - /* Set each virtual register to the appropriate class. */ - hash_table_foreach (reg_ht, entry) { - const uint32_t *vreg = entry->key; - struct live_range *range = entry->data; - struct ra_class *class = ra->reg_data[range->class].class; - - ra_set_node_class(g, *vreg, class); - /* TODO: ra_set_node_spill_cost(g, *vreg, cost); */ - } - - /* Build interference graph from overlapping live ranges. */ - hash_table_foreach (reg_ht, entry_first) { - const uint32_t *vreg_first = entry_first->key; - struct live_range *range_first = entry_first->data; - - hash_table_foreach (reg_ht, entry_second) { - const uint32_t *vreg_second = entry_second->key; - struct live_range *range_second = entry_second->data; - - if (*vreg_first == *vreg_second) - continue; - - /* If the live ranges overlap, those register nodes interfere. */ - if (!(range_first->start >= range_second->end || - range_second->start >= range_first->end)) { - ra_add_node_interference(g, *vreg_first, *vreg_second); - } - } - } - - /* Add node interferences such that the same register can't be used for - * both an instruction's source and destination. - */ - foreach_instr (instr, instr_list) { - for (size_t u = 0U; u < instr->num_operands; ++u) { - if (instr->operands[u].type != ROGUE_OPERAND_TYPE_VREG) - continue; - - /* Operand 0 (if it exists and is virtual) is always - * the destination register. - */ - if (u > 0 && instr->operands[0].type == ROGUE_OPERAND_TYPE_VREG) - ra_add_node_interference(g, - instr->operands[0].vreg.number, - instr->operands[u].vreg.number); - } - } - - /* Perform register allocation. */ - /* TODO: Spilling support. */ - assert(ra_allocate(g)); - - /* Replace virtual registers with allocated physical registers. - * N.B. This is a destructive process as it overwrites the hash table key! - */ - hash_table_foreach (reg_ht, entry) { - uint32_t vreg = *(uint32_t *)entry->key; - unsigned phy_reg = ra_get_node_reg(g, vreg); - struct live_range *range = entry->data; - - struct rogue_reg_data *reg_data = &ra->reg_data[range->class]; - enum rogue_operand_type type = reg_data->type; - size_t reg_offset = reg_data->offset; - size_t *num_used = ®_data->num_used; - - util_dynarray_foreach (&range->operand_refs, - struct rogue_operand *, - operand_ptr) { - size_t num = phy_reg - reg_offset; - struct rogue_operand *operand = *operand_ptr; - - assert(operand->type == ROGUE_OPERAND_TYPE_VREG); - assert(operand->vreg.number == vreg); - - /* Index the component of emulated vec4 registers. */ - if (operand->vreg.is_vector && - operand->vreg.component != ROGUE_COMPONENT_ALL) - num += operand->vreg.component; - - operand->type = type; - operand->reg.number = num; - - *num_used = MAX2(*num_used, operand->reg.number); - } - - util_dynarray_fini(&range->operand_refs); - _mesa_hash_table_remove(reg_ht, entry); - } - - /* Registers used = max reg number + 1. */ - for (size_t u = 0; u < ARRAY_SIZE(ra->reg_data); ++u) - if (ra->reg_data[u].num_used) - ++ra->reg_data[u].num_used; - - /* Pass back the registers used. */ - if (temps_used) - *temps_used = ra->reg_data[ROGUE_REG_CLASS_TEMP].num_used; - - if (internals_used) - *internals_used = ra->reg_data[ROGUE_REG_CLASS_VEC4].num_used; - - ralloc_free(g); - - _mesa_hash_table_destroy(reg_ht, NULL); - - return true; -} diff --git a/src/imagination/rogue/rogue_shader.c b/src/imagination/rogue/rogue_shader.c deleted file mode 100644 index 174d96248ac..00000000000 --- a/src/imagination/rogue/rogue_shader.c +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright © 2022 Imagination Technologies Ltd. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include - -#include "rogue_shader.h" -#include "rogue_instr.h" -#include "rogue_regalloc.h" -#include "rogue_util.h" -#include "util/ralloc.h" - -/** - * \file rogue_shader.c - * - * \brief Contains functions to manipulate Rogue shaders. - */ - -/** - * \brief Counts how many times an instruction is used in a shader. - * - * \param[in] shader The shader containing instructions to count. - * \param[in] opcode The opcode of the instruction to be counted. - * \return The number of times "opcode" is present, or 0 on error. - */ -size_t rogue_shader_instr_count_type(const struct rogue_shader *shader, - enum rogue_opcode opcode) -{ - size_t count = 0U; - - ASSERT_OPCODE_RANGE(opcode); - - foreach_instr (instr, &shader->instr_list) - if (instr->opcode == opcode) - ++count; - - return count; -} - -/** - * \brief Allocates and sets up a Rogue shader. - * - * \param[in] stage The shader stage. - * \return A rogue_shader* if successful, or NULL if unsuccessful. - */ -struct rogue_shader *rogue_shader_create(struct rogue_build_ctx *ctx, - gl_shader_stage stage) -{ - struct rogue_shader *shader; - - if (!ctx) - return NULL; - - shader = rzalloc_size(ctx, sizeof(*shader)); - if (!shader) - return NULL; - - shader->stage = stage; - - list_inithead(&shader->instr_list); - - shader->ctx = ctx; - shader->ra = rogue_ra_init(shader); - if (!shader->ra) { - ralloc_free(shader); - return NULL; - } - - return shader; -} - -/** - * \brief Creates an instruction and appends it to a Rogue shader. - * - * \param[in] shader The shader. - * \param[in] opcode The instruction opcode. - * \return A rogue_instr* if successful, or NULL if unsuccessful. - */ -struct rogue_instr *rogue_shader_insert(struct rogue_shader *shader, - enum rogue_opcode opcode) -{ - struct rogue_instr *instr = rogue_instr_create(shader, opcode); - if (!instr) - return NULL; - - list_addtail(&instr->node, &shader->instr_list); - - return instr; -} - -size_t rogue_acquire_drc(struct rogue_shader *shader) -{ - size_t drc; - - /* If both DRCs are in use, we have a problem. */ - if (shader->drc_used[0] && shader->drc_used[1]) - return SIZE_MAX; - - drc = !shader->drc_used[0] ? 0 : 1; - shader->drc_used[drc] = true; - - return drc; -} - -void rogue_release_drc(struct rogue_shader *shader, size_t drc) -{ - assert(drc < ROGUE_NUM_DRCS); - assert(shader->drc_used[drc]); - - shader->drc_used[drc] = false; -} diff --git a/src/imagination/rogue/rogue_shader.h b/src/imagination/rogue/rogue_shader.h deleted file mode 100644 index 2109855be08..00000000000 --- a/src/imagination/rogue/rogue_shader.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright © 2022 Imagination Technologies Ltd. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef ROGUE_SHADER_H -#define ROGUE_SHADER_H - -#include -#include - -#include "compiler/shader_enums.h" -#include "rogue_instr.h" -#include "rogue_operand.h" -#include "rogue_util.h" -#include "util/list.h" -#include "util/macros.h" - -struct rogue_build_ctx; -struct rogue_ra; - -/** - * \brief Shader description. - */ -struct rogue_shader { - gl_shader_stage stage; /** Shader stage. */ - - struct list_head instr_list; /** Instructions linked list. */ - - struct rogue_build_ctx *ctx; - struct rogue_ra *ra; - - bool drc_used[ROGUE_NUM_DRCS]; -}; - -/* Shader instruction list iterators and helpers. */ -#define foreach_instr(__instr, __list) \ - list_for_each_entry (struct rogue_instr, __instr, __list, node) -#define foreach_instr_rev(__instr, __list) \ - list_for_each_entry_rev (struct rogue_instr, __instr, __list, node) -#define foreach_instr_safe(__instr, __list) \ - list_for_each_entry_safe (struct rogue_instr, __instr, __list, node) - -#define instr_first_entry(__list) \ - list_first_entry(__list, struct rogue_instr, node) -#define instr_last_entry(__list) \ - list_last_entry(__list, struct rogue_instr, node) - -size_t rogue_shader_instr_count_type(const struct rogue_shader *shader, - enum rogue_opcode opcode); - -PUBLIC -struct rogue_shader *rogue_shader_create(struct rogue_build_ctx *ctx, - gl_shader_stage stage); - -PUBLIC -struct rogue_instr *rogue_shader_insert(struct rogue_shader *shader, - enum rogue_opcode opcode); - -size_t rogue_acquire_drc(struct rogue_shader *shader); -void rogue_release_drc(struct rogue_shader *shader, size_t drc); - -#endif /* ROGUE_SHADER_H */ diff --git a/src/imagination/rogue/rogue_util.c b/src/imagination/rogue/rogue_util.c deleted file mode 100644 index f20c13ab122..00000000000 --- a/src/imagination/rogue/rogue_util.c +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright © 2022 Imagination Technologies Ltd. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include - -#include "rogue_util.h" -#include "util/macros.h" - -/** - * \file rogue_util.c - * - * \brief Contains compiler utility and helper functions. - */ - -/** - * \brief Splits and distributes value "source" across "dest_bytes" according to - * the ranges specified (from MSB to LSB). - * - * \param[in] source The source value to be distributed. - * \param[in] rangelist The rangelist describing how to distribute "source". - * \param[in] dest_size The size of the destination in bytes. - * \param[in] dest_bytes The destination byte array. - * \return false if invalid inputs were provided, else true. - */ -bool rogue_distribute_value(uint64_t source, - const struct rogue_rangelist *rangelist, - size_t dest_size, - uint8_t dest_bytes[dest_size]) -{ - size_t total_bits_left = 0U; - - /* Check that "value" is actually representable in "total_bits" bits. */ - total_bits_left = rogue_rangelist_bits(rangelist); - assert(util_last_bit64(source) <= total_bits_left && - "Value cannot be represented."); - - /* Iterate over each range. */ - for (size_t u = 0U; u < rangelist->num_ranges; ++u) { - struct rogue_bitrange *range = &rangelist->ranges[u]; - - size_t dest_bit = range->start; - size_t bits_left = range->num; - size_t bytes_covered = rogue_bytes_spilled(range) + 1; - size_t base_byte = rogue_byte_index(range, dest_size); - - /* Iterate over each byte covered by the current range. */ - for (size_t b = 0U; b < bytes_covered; ++b) { - size_t max_bits = rogue_max_bits(dest_bit); - size_t bits_to_place = MIN2(bits_left, max_bits); - size_t dest_byte_bit = dest_bit % 8; - size_t source_bit = total_bits_left - 1; - - /* Mask and shuffle the source value so that it'll fit into the - * correct place in the destination byte: - */ - - /* Extract bits. */ - uint64_t value_masked = - (source & BITMASK64_N(source_bit, bits_to_place)); - /* Shift all the way right. */ - value_masked >>= (1 + source_bit - bits_to_place); - /* Shift left to the correct position. */ - value_masked <<= (1 + dest_byte_bit - bits_to_place); - /* Place value into byte. */ - dest_bytes[base_byte + b] |= (value_masked & 0xff); - - dest_bit -= max_bits; - bits_left -= bits_to_place; - total_bits_left -= bits_to_place; - } - } - - return true; -} diff --git a/src/imagination/rogue/rogue_util.h b/src/imagination/rogue/rogue_util.h deleted file mode 100644 index fa789c48220..00000000000 --- a/src/imagination/rogue/rogue_util.h +++ /dev/null @@ -1,320 +0,0 @@ -/* - * Copyright © 2022 Imagination Technologies Ltd. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef ROGUE_UTIL_H -#define ROGUE_UTIL_H - -#include -#include -#include -#include - -#include "util/bitscan.h" -#include "util/log.h" -#include "util/macros.h" - -/* Input validation helpers. */ - -/** - * \brief Returns false if "expr" is not asserted. - * - * \param[in] expr The expression to check. - */ -#define CHECK(expr) \ - do { \ - if (!(expr)) \ - return false; \ - } while (0) - -/** - * \brief Returns false if "expr" is not asserted, - * and logs the provided error message. - * - * \param[in] expr The expression to check. - * \param[in] fmt The error message to print. - * \param[in] ... The printf-style varable arguments. - */ -#define CHECKF(expr, fmt, ...) \ - do { \ - if (!(expr)) { \ - mesa_log(MESA_LOG_ERROR, "ROGUE", fmt, ##__VA_ARGS__); \ - return false; \ - } \ - } while (0) - -/** - * \brief Asserts if "opcode" is invalid. - * - * \param[in] opcode The opcode to check. - */ -#define ASSERT_OPCODE_RANGE(opcode) assert((opcode) < ROGUE_OP_COUNT) - -/** - * \brief Asserts if "operand" is invalid. - * - * \param[in] operand The operand to check. - */ -#define ASSERT_OPERAND_RANGE(operand) \ - assert((operand) < ROGUE_OPERAND_TYPE_COUNT) - -/** - * \brief Asserts if "operand" is not a register. - * - * \param[in] operand The operand to check. - */ -#define ASSERT_OPERAND_REG(operand) \ - assert((operand) <= ROGUE_OPERAND_TYPE_REG_MAX) - -/** - * \brief Asserts if "flag" is invalid. - * - * \param[in] flag The flag to check. - */ -#define ASSERT_INSTR_FLAG_RANGE(flag) assert((flag) < ROGUE_INSTR_FLAG_COUNT) - -/** - * \brief Asserts if operand index "index" is out of range. - * - * \param[in] instr The target instruction. - * \param[in] index The operand index to check. - */ -#define ASSERT_INSTR_OPERAND_INDEX(instr, index) \ - assert((index) < (instr)->num_operands) - -/** - * \brief Asserts if "stage" is invalid. - * - * \param[in] stage The stage to check. - */ -#define ASSERT_SHADER_STAGE_RANGE(stage) assert((stage) < MESA_SHADER_STAGES) - -/** - * \brief Creates a "n"-bit mask starting from bit "b". - * - * \param[in] b The starting bit. - * \param[in] n The number of bits in the mask. - */ -#define BITMASK64_N(b, n) (((~0ULL) << (64 - (n))) >> (63 - (b))) - -/** - * \brief Compile-time rogue_onehot. - * - * \sa #rogue_onehot() - */ -#define ROH(OFFSET) BITFIELD64_BIT(OFFSET) - -/* TODO: Consider integrating the following into src/util/{macros,bitscan}.h */ - -/** - * \brief Converts a one-hot encoding to an offset encoding. - * - * E.g. 0b10000 -> 4 - * - * \param[in] onehot The one-hot encoding. - * \return The offset encoding. - */ -static inline uint64_t rogue_offset(uint64_t onehot) -{ - assert(util_bitcount64(onehot) == 1); - return ffsll(onehot) - 1; -} - -/** - * \brief Converts an offset encoding to a one-hot encoding. - * - * E.g. 0 -> 0b1 - * - * \param[in] offset The offset encoding. - * \return The one-hot encoding. - */ -static inline uint64_t rogue_onehot(uint64_t offset) -{ - assert(offset < 64ULL); - return (1ULL << offset); -} - -/** - * \brief Checks whether an input bitfield contains only a valid bitset. - * - * E.g. rogue_check_bitset(0b00001100, 0b00001111) -> true - * rogue_check_bitset(0b00001100, 0b00000111) -> false - * - * \param[in] input The input bitfield. - * \param[in] valid_bits The valid bitset. - * \return true if "input" contains only "valid_bits", false otherwise. - */ -static inline bool rogue_check_bitset(uint64_t input, uint64_t valid_bits) -{ - input &= ~valid_bits; - return !input; -} - -/** - * \brief Describes a downward range of bits within an arbitrarily-sized - * sequence. - * - * E.g. for start = 7 and num = 3: - * - * 76543210 - * abcdefgh - * - * the bit range would be: abc. - */ -struct rogue_bitrange { - size_t start; - size_t num; -}; - -/** - * \brief Describes a collection of bit-ranges within an arbitrarily-sized - * sequence that are meaningful together. - * - * E.g. an 8-bit value that is encoded within a larger value: - * 8-bit value: abcdefgh - * Parent value: 010ab0cdef0010gh - * - */ -struct rogue_rangelist { - size_t num_ranges; - struct rogue_bitrange *ranges; -}; - -/** - * \brief Counts the total number of bits described in a rangelist. - * - * \param[in] rangelist The input rangelist. - * \return The total number of bits. - */ -static inline size_t -rogue_rangelist_bits(const struct rogue_rangelist *rangelist) -{ - size_t total_bits = 0U; - - for (size_t u = 0U; u < rangelist->num_ranges; ++u) - total_bits += rangelist->ranges[u].num; - - return total_bits; -} - -/** - * \brief Returns the byte offset of the bitrange moving left from the LSB. - * - * \param[in] bitrange The input bit-range. - * \return The byte offset. - */ -static inline size_t rogue_byte_num(const struct rogue_bitrange *bitrange) -{ - /* Make sure there are enough bits. */ - assert(bitrange->num <= (bitrange->start + 1)); - - return bitrange->start / 8; -} - -/** - * \brief Returns the array-indexable byte offset of a bit-range if the sequence - * it represents were to be stored in an byte-array containing "num_bytes" - * bytes. - * - * E.g. uint8_t array[2] is a sequence of 16 bits: - * bit(0) is located in array[1]. - * bit(15) is located in array[0]. - * - * For uint8_t array[4]: - * bit(0) is located in array[3]. - * bit(15) is located in array[2]. - * - * \param[in] bitrange The input bit-range. - * \param[in] num_bytes The number of bytes that are used to contain the - * bit-range. \return The byte offset. - */ -static inline size_t rogue_byte_index(const struct rogue_bitrange *bitrange, - size_t num_bytes) -{ - /* Make sure there are enough bits. */ - assert(bitrange->num <= (bitrange->start + 1)); - - return num_bytes - rogue_byte_num(bitrange) - 1; -} - -/** - * \brief Returns the bit offset of a bit-range if the sequence it represents is - * being accessed in a byte-wise manner. - * - * E.g. bit 17 has a bit offset of 1. - * - * \param[in] bitrange The input bit-range. - * \return The bit offset. - */ -static inline size_t rogue_bit_offset(const struct rogue_bitrange *bitrange) -{ - /* Make sure there are enough bits. */ - assert(bitrange->num <= (bitrange->start + 1)); - - return bitrange->start % 8; -} - -/** - * \brief Returns the number of additional bytes that the bit-range spills into - * (excluding its "starting" byte). - * - * \param[in] bitrange The input bit-range. - * \return The number of bytes spilled. - */ -static inline size_t rogue_bytes_spilled(const struct rogue_bitrange *bitrange) -{ - /* Make sure there are enough bits. */ - assert(bitrange->num <= (bitrange->start + 1)); - - return ((bitrange->num - 1) / 8) + - ((bitrange->num % 8) > (rogue_bit_offset(bitrange) + 1)); -} - -/** - * \brief For a given bit offset, returns the maximum number of bits (including - * itself) that are accessible before spilling into the following byte. - * - * E.g. When trying to insert an 8-bit value offset of 13, a maximum of 6 bits - * can be placed; the last 2 bits will need to go into the next byte. - * - * 8-bit value: abcdefgh - * - * array[0] array[1] - * 15 8 7 0 - * iiiiiiii jjjjjjjj - * ^ - * abcdef gh - * - * \param[in] The bit offset. - * \return The maximum number of accessible bits. - */ -static inline size_t rogue_max_bits(size_t offset) -{ - return (offset % 8) + 1; -} - -bool rogue_distribute_value(uint64_t source, - const struct rogue_rangelist *rangelist, - size_t dest_size, - uint8_t dest_bytes[dest_size]); - -#endif /* ROGUE_UTIL_H */ diff --git a/src/imagination/rogue/rogue_validate.c b/src/imagination/rogue/rogue_validate.c index 8d9d10e3642..76ce80640ec 100644 --- a/src/imagination/rogue/rogue_validate.c +++ b/src/imagination/rogue/rogue_validate.c @@ -21,268 +21,491 @@ * SOFTWARE. */ -/** - * \file rogue_validate.c - * - * \brief Contains rules and functions for validating Rogue data structures. - */ +#include "rogue.h" +#include "util/bitscan.h" +#include "util/macros.h" +#include "util/u_dynarray.h" #include -#include "rogue_operand.h" -#include "rogue_shader.h" -#include "rogue_util.h" -#include "rogue_validate.h" -#include "util/list.h" -#include "util/macros.h" - /** - * \brief Register operand rules. - */ -#define REG_RULE(OPERAND, ACCESS, MAX, MODIFIERS) \ - [ROGUE_OPERAND_TYPE_REG_##OPERAND] = { \ - .access = ROGUE_REG_ACCESS_##ACCESS, \ - .max = MAX, \ - .modifiers = ROGUE_REG_MOD_##MODIFIERS, \ - } - -/* TODO: Support register indexing > ROGUE_MAX_REG_TEMP. */ -static const struct rogue_register_rule reg_rules[ROGUE_NUM_REG_TYPES] = { - REG_RULE(TEMP, RW, MIN2(ROGUE_MAX_REG_INDEX, ROGUE_MAX_REG_TEMP), ALL), - REG_RULE(COEFF, RW, MIN2(ROGUE_MAX_REG_INDEX, ROGUE_MAX_REG_COEFF), ALL), - REG_RULE(CONST, RW, MIN2(ROGUE_MAX_REG_INDEX, ROGUE_MAX_REG_CONST), NONE), - REG_RULE(SHARED, RW, MIN2(ROGUE_MAX_REG_INDEX, ROGUE_MAX_REG_SHARED), ALL), - REG_RULE(PIXEL_OUT, - RW, - MIN2(ROGUE_MAX_REG_INDEX, ROGUE_MAX_REG_PIXEL_OUT), - NONE), - REG_RULE(VERTEX_IN, - RW, - MIN2(ROGUE_MAX_REG_INDEX, ROGUE_MAX_REG_VERTEX_IN), - ALL), - REG_RULE(INTERNAL, - RW, - MIN2(ROGUE_MAX_REG_INDEX, ROGUE_MAX_REG_INTERNAL), - NONE), -}; -#undef REG_RULE - -/** - * \brief Instruction rules. - */ -/* TODO: Common up register classes to prevent long lines. */ -static const struct rogue_instr_rule instr_rules[ROGUE_OP_COUNT] = { - [ROGUE_OP_NOP] = { .flags = 0, .num_operands = 0, .operand_rules = NULL, }, - [ROGUE_OP_END_FRAG] = { .flags = 0, .num_operands = 0, .operand_rules = NULL, }, - [ROGUE_OP_END_VERT] = { .flags = 0, .num_operands = 0, .operand_rules = NULL, }, - [ROGUE_OP_WDF] = { .flags = 0, - .num_operands = 1, .operand_rules = (struct rogue_instr_operand_rule[]){ - [0] = { .mask = ROH(ROGUE_OPERAND_TYPE_DRC), .min = -1, .max = -1, .align = -1, }, - }, - }, - [ROGUE_OP_PIX_ITER_W] = { .flags = ROH(ROGUE_INSTR_FLAG_SAT), - .num_operands = 5, .operand_rules = (struct rogue_instr_operand_rule[]){ - [0] = { .mask = ROH(ROGUE_OPERAND_TYPE_REG_TEMP), .min = -1, .max = -1, .align = -1, }, - [1] = { .mask = ROH(ROGUE_OPERAND_TYPE_DRC), .min = -1, .max = -1, .align = -1, }, - [2] = { .mask = ROH(ROGUE_OPERAND_TYPE_REG_COEFF), .min = -1, .max = -1, .align = ROGUE_COEFF_ALIGN, }, - [3] = { .mask = ROH(ROGUE_OPERAND_TYPE_REG_COEFF), .min = -1, .max = -1, .align = ROGUE_COEFF_ALIGN, }, - [4] = { .mask = ROH(ROGUE_OPERAND_TYPE_IMMEDIATE), .min = 1, .max = 16, .align = -1, }, - }, - }, - [ROGUE_OP_MAX] = { .flags = 0, - .num_operands = 3, .operand_rules = (struct rogue_instr_operand_rule[]){ - [0] = { .mask = ROH(ROGUE_OPERAND_TYPE_REG_TEMP), .min = -1, .max = -1, .align = -1, }, - [1] = { .mask = ROH(ROGUE_OPERAND_TYPE_REG_TEMP), .min = -1, .max = -1, .align = -1, }, - [2] = { .mask = ROH(ROGUE_OPERAND_TYPE_REG_CONST) | ROH(ROGUE_OPERAND_TYPE_REG_TEMP), .min = -1, .max = -1, .align = -1, }, - }, - }, - [ROGUE_OP_MIN] = { .flags = 0, - .num_operands = 3, .operand_rules = (struct rogue_instr_operand_rule[]){ - [0] = { .mask = ROH(ROGUE_OPERAND_TYPE_REG_TEMP) | ROH(ROGUE_OPERAND_TYPE_REG_INTERNAL), .min = -1, .max = -1, .align = -1, }, - [1] = { .mask = ROH(ROGUE_OPERAND_TYPE_REG_TEMP), .min = -1, .max = -1, .align = -1, }, - [2] = { .mask = ROH(ROGUE_OPERAND_TYPE_REG_CONST) | ROH(ROGUE_OPERAND_TYPE_REG_TEMP), .min = -1, .max = -1, .align = -1, }, - }, - }, - /* TODO: Add representation for 4 sequential registers. */ - [ROGUE_OP_PACK_U8888] = { .flags = 0, - .num_operands = 2, .operand_rules = (struct rogue_instr_operand_rule[]){ - [0] = { .mask = ROH(ROGUE_OPERAND_TYPE_REG_TEMP), .min = -1, .max = -1, .align = -1, }, - [1] = { .mask = ROH(ROGUE_OPERAND_TYPE_REG_INTERNAL), .min = -1, .max = -1, .align = -1, }, - }, - }, - [ROGUE_OP_MOV] = { .flags = ROH(ROGUE_INSTR_FLAG_OLCHK), - .num_operands = 2, .operand_rules = (struct rogue_instr_operand_rule[]){ - [0] = { .mask = ROH(ROGUE_OPERAND_TYPE_REG_TEMP) | ROH(ROGUE_OPERAND_TYPE_REG_INTERNAL) | ROH(ROGUE_OPERAND_TYPE_REG_PIXEL_OUT), .min = -1, .max = -1, .align = -1, }, - [1] = { .mask = ROH(ROGUE_OPERAND_TYPE_REG_CONST) | ROH(ROGUE_OPERAND_TYPE_REG_TEMP) | ROH(ROGUE_OPERAND_TYPE_REG_SHARED) | ROH(ROGUE_OPERAND_TYPE_REG_VERTEX_IN), .min = -1, .max = -1, .align = -1, }, - }, - }, - [ROGUE_OP_MOV_IMM] = { .flags = 0, - .num_operands = 2, .operand_rules = (struct rogue_instr_operand_rule[]){ - [0] = { .mask = ROH(ROGUE_OPERAND_TYPE_REG_TEMP), .min = -1, .max = -1, .align = -1, }, - [1] = { .mask = ROH(ROGUE_OPERAND_TYPE_IMMEDIATE), .min = 0, .max = UINT32_MAX, .align = -1, }, - }, - }, - [ROGUE_OP_FMA] = { .flags = ROH(ROGUE_INSTR_FLAG_SAT) | ROH(ROGUE_INSTR_FLAG_LP), - .num_operands = 4, .operand_rules = (struct rogue_instr_operand_rule[]){ - [0] = { .mask = ROH(ROGUE_OPERAND_TYPE_REG_TEMP), .min = -1, .max = -1, .align = -1, }, - [1] = { .mask = ROH(ROGUE_OPERAND_TYPE_REG_TEMP), .min = -1, .max = -1, .align = -1, }, - [2] = { .mask = ROH(ROGUE_OPERAND_TYPE_REG_TEMP), .min = -1, .max = -1, .align = -1, }, - [3] = { .mask = ROH(ROGUE_OPERAND_TYPE_REG_TEMP), .min = -1, .max = -1, .align = -1, }, - }, - }, - [ROGUE_OP_MUL] = { .flags = ROH(ROGUE_INSTR_FLAG_SAT) | ROH(ROGUE_INSTR_FLAG_LP), - .num_operands = 3, .operand_rules = (struct rogue_instr_operand_rule[]){ - [0] = { .mask = ROH(ROGUE_OPERAND_TYPE_REG_TEMP), .min = -1, .max = -1, .align = -1, }, - [1] = { .mask = ROH(ROGUE_OPERAND_TYPE_REG_TEMP), .min = -1, .max = -1, .align = -1, }, - [2] = { .mask = ROH(ROGUE_OPERAND_TYPE_REG_TEMP), .min = -1, .max = -1, .align = -1, }, - }, - }, - [ROGUE_OP_VTXOUT] = { .flags = 0, - .num_operands = 2, .operand_rules = (struct rogue_instr_operand_rule[]){ - [0] = { .mask = ROH(ROGUE_OPERAND_TYPE_IMMEDIATE), .min = 0, .max = ROGUE_MAX_VERTEX_OUTPUTS, .align = -1, }, - [1] = { .mask = ROH(ROGUE_OPERAND_TYPE_REG_TEMP), .min = -1, .max = -1, .align = -1, }, - }, - }, -}; - -/** - * \brief Validates an operand. + * \file rogue_validate.c * - * \param[in] operand The operand. - * \return true if valid, otherwise false. + * \brief Contains functions to validate Rogue IR. */ -bool rogue_validate_operand(const struct rogue_operand *operand) + +/* TODO: Rogue_validate should make sure that immediate (sources) don't have any + * modifiers set... */ + +/* TODO NEXT: Make sure that Register Usage Restrictions are followed (and go + * through ISR and add any other restrictions). */ +/* TODO: Remember that some instructions have the DESTINATION as a source + * (register pointers), e.g. fitrp using S3 */ +/* TODO NEXT: Add field to instr_info that specifies which source/destination + * should be affected by instruction repeating. */ +/* TODO NEXT: Validate backend and control sources/dests. */ +/* TODO: Go through and make sure that validation state is being properly + * updated as so to allow for validation_log to print enough info. */ + +/* TODO NEXT: Check for emit/end/etc. as last instruction in vertex shader, and + * nop.end, or end flag set (or just pseudo-end) otherwise. */ + +typedef struct rogue_validation_state { + const rogue_shader *shader; /** The shader being validated. */ + const char *when; /** Description of the validation being done. */ + bool nonfatal; /** Don't stop at the first error.*/ + const rogue_instr *instr; /** Current instruction being validated. */ + const rogue_instr_group *group; /** Current instruction group being + validated. */ + const rogue_ref *ref; /** Current reference being validated. */ + struct util_dynarray *error_msgs; /** Error message list. */ +} rogue_validation_state; + +/* Returns true if errors are present. */ +static bool validate_print_errors(rogue_validation_state *state) { - ASSERT_OPERAND_RANGE(operand->type); - - switch (operand->type) { - case ROGUE_OPERAND_TYPE_IMMEDIATE: - return true; - - case ROGUE_OPERAND_TYPE_DRC: - CHECKF(operand->drc.number < ROGUE_NUM_DRCS, - "Invalid DRC number '%zu'.", - operand->drc.number); - return true; - - case ROGUE_OPERAND_TYPE_REG_TEMP: - case ROGUE_OPERAND_TYPE_REG_COEFF: - case ROGUE_OPERAND_TYPE_REG_CONST: - case ROGUE_OPERAND_TYPE_REG_SHARED: - case ROGUE_OPERAND_TYPE_REG_PIXEL_OUT: - case ROGUE_OPERAND_TYPE_REG_VERTEX_IN: - case ROGUE_OPERAND_TYPE_REG_INTERNAL: - CHECKF(operand->reg.number < reg_rules[operand->type].max, - "Register number '%zu' out of range.", - operand->reg.number); - return true; - - default: - break; - } - - return false; -} - -/** - * \brief Validates an instruction. - * - * \param[in] instr The instruction. - * \return true if valid, otherwise false. - */ -bool rogue_validate_instr(const struct rogue_instr *instr) -{ - const struct rogue_instr_rule *rule; - - ASSERT_OPCODE_RANGE(instr->opcode); - - rule = &instr_rules[instr->opcode]; - - /* Validate flags. */ - CHECKF(rogue_check_bitset(instr->flags, rule->flags), - "Invalid instruction flags specified."); - - /* Validate number of operands. */ - CHECKF(instr->num_operands == rule->num_operands, - "Invalid number of operands specified."); - - CHECK(!rule->num_operands || instr->operands); - for (size_t u = 0U; u < instr->num_operands; ++u) { - /* Validate operand types. */ - CHECKF(rogue_check_bitset(rogue_onehot(instr->operands[u].type), - rule->operand_rules[u].mask), - "Invalid type for operand %zu.", - u); - - /* Validate immediate ranges. */ - if (rogue_check_bitset(rogue_onehot(instr->operands[u].type), - ROH(ROGUE_OPERAND_TYPE_IMMEDIATE)) && - rule->operand_rules[u].min != -1 && - rule->operand_rules[u].max != -1) { - CHECKF( - instr->operands[u].immediate.value >= rule->operand_rules[u].min && - instr->operands[u].immediate.value <= rule->operand_rules[u].max, - "Immediate value out of range for operand %zu.", - u); - } - - /* Validate register alignment. */ - if (rogue_check_bitset(rogue_onehot(instr->operands[u].type), - ROGUE_MASK_ANY_REG) && - rule->operand_rules[u].align != -1) { - CHECKF(!(instr->operands[u].reg.number % rule->operand_rules[u].align), - "Invalid register alignment in operand %zu.", - u); - } - - /* Validate each operand. */ - CHECKF(rogue_validate_operand(&instr->operands[u]), - "Failed to validate operand."); - } - - return true; -} - -/** - * \brief Validates a shader. - * - * \param[in] shader The shader. - * \return true if valid, otherwise false. - */ -bool rogue_validate_shader(const struct rogue_shader *shader) -{ - CHECK(!list_is_empty(&shader->instr_list)); - ASSERT_SHADER_STAGE_RANGE(shader->stage); - - /* Shader stage-specific validation. */ - switch (shader->stage) { - case MESA_SHADER_VERTEX: - /* Make sure there is (only) one end vertex shader instruction. */ - CHECKF(rogue_shader_instr_count_type(shader, ROGUE_OP_END_VERT) == 1, - "Shader must contain a single end.vert instruction."); - - /* Make sure the end vertex shader instruction is the last one. */ - CHECKF(instr_last_entry(&shader->instr_list)->opcode == ROGUE_OP_END_VERT, - "end.vert not last instruction."); - break; - - case MESA_SHADER_FRAGMENT: - /* Make sure there is (only) one end fragment shader instruction. */ - CHECKF(rogue_shader_instr_count_type(shader, ROGUE_OP_END_FRAG) == 1, - "Shader must contain a single end.frag instruction."); - - /* Make sure the end fragment shader instruction is the last one. */ - CHECKF(instr_last_entry(&shader->instr_list)->opcode == ROGUE_OP_END_FRAG, - "end.frag not last instruction."); - break; - - default: + if (!util_dynarray_num_elements(state->error_msgs, const char *)) return false; + + util_dynarray_foreach (state->error_msgs, const char *, msg) { + fprintf(stderr, "%s\n", *msg); } - /* Validate each instruction. */ - foreach_instr (instr, &shader->instr_list) - CHECKF(rogue_validate_instr(instr), "Failed to validate instruction."); + fputs("\n", stderr); + + /* TODO: Figure out if/when to print this. */ + rogue_print_shader(stderr, state->shader); + fputs("\n", stderr); return true; } + +static void PRINTFLIKE(2, 3) + validate_log(rogue_validation_state *state, const char *fmt, ...) +{ + char *msg = ralloc_asprintf(state->error_msgs, "Validation error"); + + /* Add info about the item that was being validated. */ + if (state->instr) { + ralloc_asprintf_append(&msg, " instr %u", state->instr->index); + } + + if (state->ref) { + /* TODO: Find a way to get an index. */ + } + + ralloc_asprintf_append(&msg, ": "); + + va_list args; + va_start(args, fmt); + ralloc_vasprintf_append(&msg, fmt, args); + util_dynarray_append(state->error_msgs, const char *, msg); + va_end(args); + + if (!state->nonfatal) { + validate_print_errors(state); + abort(); + } +} + +static rogue_validation_state * +create_validation_state(const rogue_shader *shader, const char *when) +{ + rogue_validation_state *state = rzalloc_size(shader, sizeof(*state)); + + state->shader = shader; + state->when = when; + state->nonfatal = ROGUE_DEBUG(VLD_NONFATAL); + + state->error_msgs = rzalloc_size(state, sizeof(*state->error_msgs)); + util_dynarray_init(state->error_msgs, state); + + return state; +} + +static void validate_regarray(rogue_validation_state *state, + rogue_regarray *regarray) +{ + if (!regarray->size) { + validate_log(state, "Register array is empty."); + return; + } + + enum rogue_reg_class class = regarray->regs[0]->class; + unsigned base_index = regarray->regs[0]->index; + + for (unsigned u = 0; u < regarray->size; ++u) { + if (regarray->regs[u]->class != class) + validate_log(state, "Register class mismatch in register array."); + + if (regarray->regs[u]->index != (base_index + u)) + validate_log(state, "Non-contiguous registers in register array."); + } +} + +static void validate_alu_dst(rogue_validation_state *state, + const rogue_alu_dst *dst, + uint64_t supported_dst_types) +{ + state->ref = &dst->ref; + + if (rogue_ref_is_null(&dst->ref)) + validate_log(state, "ALU destination has not been set."); + + if (!state->shader->is_grouped) + if (!rogue_ref_type_supported(dst->ref.type, supported_dst_types)) + validate_log(state, "Unsupported ALU destination type."); + + state->ref = NULL; +} + +static void validate_alu_src(rogue_validation_state *state, + const rogue_alu_src *src, + uint64_t supported_src_types) +{ + state->ref = &src->ref; + + if (rogue_ref_is_null(&src->ref)) + validate_log(state, "ALU source has not been set."); + + if (!state->shader->is_grouped) { + if (!rogue_ref_type_supported(src->ref.type, supported_src_types)) + validate_log(state, "Unsupported ALU source type."); + } + + state->ref = NULL; +} + +static void validate_alu_instr(rogue_validation_state *state, + const rogue_alu_instr *alu) +{ + if (alu->op == ROGUE_ALU_OP_INVALID || alu->op >= ROGUE_ALU_OP_COUNT) + validate_log(state, "Unknown ALU op 0x%x encountered.", alu->op); + + const rogue_alu_op_info *info = &rogue_alu_op_infos[alu->op]; + + if (!rogue_alu_comp_is_none(alu) && alu->op != ROGUE_ALU_OP_TST) + validate_log(state, "ALU comparison set for non-test op."); + + if (rogue_alu_comp_is_none(alu) && alu->op == ROGUE_ALU_OP_TST) + validate_log(state, "ALU comparison not set for test op."); + + /* Initial check if instruction modifiers are valid. */ + if (!rogue_mods_supported(alu->mod, info->supported_op_mods)) + validate_log(state, "Unsupported ALU op modifiers."); + + /* Validate destination and sources. */ + validate_alu_dst(state, &alu->dst, info->supported_dst_types); + + for (unsigned i = 0; i < info->num_srcs; ++i) + validate_alu_src(state, &alu->src[i], info->supported_src_types[i]); + + /* TODO: Check that the src_use and dst_write fields are correct? */ +} + +static void validate_backend_instr(rogue_validation_state *state, + const rogue_backend_instr *backend) +{ + if (backend->op == ROGUE_BACKEND_OP_INVALID || + backend->op >= ROGUE_BACKEND_OP_COUNT) + validate_log(state, "Unknown backend op 0x%x encountered.", backend->op); + + const rogue_backend_op_info *info = &rogue_backend_op_infos[backend->op]; + + /* Initial check if instruction modifiers are valid. */ + if (!rogue_mods_supported(backend->mod, info->supported_op_mods)) + validate_log(state, "Unsupported backend op modifiers."); + + /* TODO: Validate dests and srcs? */ + /* TODO: Check that the src_use and dst_write fields are correct? */ +} + +/* Returns true if instruction can end block. */ +static bool validate_ctrl_instr(rogue_validation_state *state, + const rogue_ctrl_instr *ctrl) +{ + if (ctrl->op == ROGUE_CTRL_OP_INVALID || ctrl->op >= ROGUE_CTRL_OP_COUNT) + validate_log(state, "Unknown ctrl op 0x%x encountered.", ctrl->op); + + /* TODO: Validate rest, check blocks, etc. */ + const rogue_ctrl_op_info *info = &rogue_ctrl_op_infos[ctrl->op]; + + if (info->has_target && !ctrl->target_block) + validate_log(state, "Ctrl op expected target block, but none provided."); + else if (!info->has_target && ctrl->target_block) + validate_log(state, + "Ctrl op did not expect target block, but one provided."); + + /* Initial check if instruction modifiers are valid. */ + if (!rogue_mods_supported(ctrl->mod, info->supported_op_mods)) + validate_log(state, "Unsupported CTRL op modifiers."); + + /* TODO: Validate dests and srcs? */ + /* TODO: Check that the src_use and dst_write fields are correct? */ + + /* nop.end counts as a end-of-block instruction. */ + if (rogue_instr_is_nop_end(&ctrl->instr)) + return true; + + /* Control instructions have no end flag to set. */ + if (ctrl->instr.end) + validate_log(state, "CTRL ops have no end flag."); + + return info->ends_block; +} + +/* Returns true if instruction can end block. */ +static bool validate_instr(rogue_validation_state *state, + const rogue_instr *instr) +{ + state->instr = instr; + + bool ends_block = false; + + switch (instr->type) { + case ROGUE_INSTR_TYPE_ALU: + validate_alu_instr(state, rogue_instr_as_alu(instr)); + break; + + case ROGUE_INSTR_TYPE_BACKEND: + validate_backend_instr(state, rogue_instr_as_backend(instr)); + break; + + case ROGUE_INSTR_TYPE_CTRL: + ends_block = validate_ctrl_instr(state, rogue_instr_as_ctrl(instr)); + break; + + default: + validate_log(state, + "Unknown instruction type 0x%x encountered.", + instr->type); + } + + /* If the last instruction isn't control flow but has the end flag set, it + * can end a block. */ + if (!ends_block) + ends_block = instr->end; + + state->instr = NULL; + + return ends_block; +} + +/* Returns true if instruction can end block. */ +static bool validate_instr_group(rogue_validation_state *state, + const rogue_instr_group *group) +{ + state->group = group; + /* TODO: Validate group properties. */ + /* TODO: Check for pseudo-instructions. */ + + bool ends_block = false; + + /* Validate instructions in group. */ + /* TODO: Check util_last_bit group_phases < bla bla */ + rogue_foreach_phase_in_set (p, group->header.phases) { + const rogue_instr *instr = group->instrs[p]; + + if (!instr) + validate_log(state, "Missing instruction where phase was set."); + + /* TODO NEXT: Groups that have control instructions should only have a + * single instruction. */ + ends_block = validate_instr(state, instr); + } + + state->group = NULL; + + if (group->header.alu != ROGUE_ALU_CONTROL) + return group->header.end; + + return ends_block; +} + +static void validate_block(rogue_validation_state *state, + const rogue_block *block) +{ + /* TODO: Set/reset state->block */ + /* TODO: Validate block properties. */ + + if (list_is_empty(&block->instrs)) { + validate_log(state, "Block is empty."); + return; + } + + unsigned block_ends = 0; + struct list_head *block_end = NULL; + struct list_head *last = block->instrs.prev; + + /* Validate instructions/groups in block. */ + if (!block->shader->is_grouped) { + rogue_foreach_instr_in_block (instr, block) { + bool ends_block = validate_instr(state, instr); + block_ends += ends_block; + block_end = ends_block ? &instr->link : block_end; + } + } else { + rogue_foreach_instr_group_in_block (group, block) { + bool ends_block = validate_instr_group(state, group); + block_ends += ends_block; + block_end = ends_block ? &group->link : block_end; + } + } + + if (!block_ends || block_ends > 1) + validate_log(state, + "Block must end with a single control flow instruction."); + else if (block_end != last) + validate_log( + state, + "Control flow instruction is present prior to the end of the block."); +} + +static void validate_reg_use(rogue_validation_state *state, + const rogue_reg_use *use, + uint64_t supported_io_srcs) +{ + /* No restrictions. */ + if (!supported_io_srcs) + return; + + const rogue_instr *instr = use->instr; + + rogue_foreach_phase_in_set (p, rogue_instr_supported_phases(instr)) { + enum rogue_io io_src = rogue_instr_src_io_src(instr, p, use->src_index); + if (io_src == ROGUE_IO_INVALID) + validate_log(state, "Register used where no source is present."); + + if (!rogue_io_supported(io_src, supported_io_srcs)) + validate_log(state, + "Register class unsupported in S%u.", + io_src - ROGUE_IO_S0); /* TODO: Either add info here to + get register class and print as + string, or add info to + rogue_validation_state. */ + } +} + +static void validate_reg_state(rogue_validation_state *state, + rogue_shader *shader) +{ + BITSET_WORD *regs_used = NULL; + + for (enum rogue_reg_class class = 0; class < ROGUE_REG_CLASS_COUNT; + ++class) { + const rogue_reg_info *info = &rogue_reg_infos[class]; + if (info->num) + regs_used = + rzalloc_size(state, sizeof(*regs_used) * BITSET_WORDS(info->num)); + + rogue_foreach_reg (reg, shader, class) { + /* Ensure that the range restrictions are satisfied. */ + if (info->num && reg->index >= info->num) + validate_log(state, "%s register index out of range.", info->name); + + /* Ensure that only registers of this class are in the regs list. */ + if (reg->class != class) + validate_log(state, + "%s register found in %s register list.", + rogue_reg_infos[reg->class].name, + info->name); + + /* Track the registers used in the class. */ + if (info->num) + BITSET_SET(regs_used, reg->index); + + /* Check register cache entry. */ + rogue_reg **reg_cached = + util_sparse_array_get(&shader->reg_cache[class], reg->index); + if (!reg_cached || !*reg_cached) + validate_log(state, + "Missing %s register %u cache entry.", + info->name, + reg->index); + else if (*reg_cached != reg || (*reg_cached)->index != reg->index || + (*reg_cached)->class != reg->class) + validate_log(state, + "Mismatching %s register %u cache entry.", + info->name, + reg->index); + else if (reg_cached != reg->cached) + validate_log(state, + "Mismatching %s register %u cache entry pointer.", + info->name, + reg->index); + + /* Validate register uses. */ + const rogue_reg_info *reg_info = &rogue_reg_infos[class]; + rogue_foreach_reg_use (use, reg) + validate_reg_use(state, use, reg_info->supported_io_srcs); + } + + /* Check that the registers used matches the usage list. */ + if (info->num && memcmp(shader->regs_used[class], + regs_used, + sizeof(*regs_used) * BITSET_WORDS(info->num))) + validate_log(state, "Incorrect %s register usage list.", info->name); + + ralloc_free(regs_used); + } + + /* Check that SSA registers aren't being written to more than once. */ + rogue_foreach_reg (reg, shader, ROGUE_REG_CLASS_SSA) + if (list_length(®->writes) > 1) + validate_log(state, + "SSA register %u is written to more than once.", + reg->index); + + rogue_foreach_regarray (regarray, shader) { + /* Validate regarray contents. */ + validate_regarray(state, regarray); + + /* Check regarray cache entry. */ + uint64_t key = rogue_regarray_cache_key(regarray->size, + regarray->regs[0]->class, + regarray->regs[0]->index, + false, + 0); + rogue_regarray **regarray_cached = + util_sparse_array_get(&shader->regarray_cache, key); + if (!regarray_cached || !*regarray_cached) + validate_log(state, "Missing regarray cache entry."); + else if (*regarray_cached != regarray || + (*regarray_cached)->size != regarray->size || + (*regarray_cached)->parent != regarray->parent || + (*regarray_cached)->regs != regarray->regs) + validate_log(state, "Mismatching regarray cache entry."); + else if (regarray_cached != regarray->cached) + validate_log(state, "Mismatching regarray cache entry pointer."); + + if (regarray->parent && (regarray->parent->size <= regarray->size || + regarray->parent->parent)) + validate_log(state, "Invalid sub-regarray."); + } +} + +/* TODO: To properly test this and see what needs validating, try and write some + * failing tests and then filling them from there. */ +PUBLIC +bool rogue_validate_shader(rogue_shader *shader, const char *when) +{ + if (ROGUE_DEBUG(VLD_SKIP)) + return true; + + bool errors_present; + + rogue_validation_state *state = create_validation_state(shader, when); + + validate_reg_state(state, shader); + + /* TODO: Ensure there is at least one block (with at least an end + * instruction!) */ + rogue_foreach_block (block, shader) + validate_block(state, block); + + errors_present = validate_print_errors(state); + + ralloc_free(state); + + return !errors_present; +} diff --git a/src/imagination/rogue/tools/meson.build b/src/imagination/rogue/tools/meson.build new file mode 100644 index 00000000000..c7108b2a361 --- /dev/null +++ b/src/imagination/rogue/tools/meson.build @@ -0,0 +1,39 @@ +# Copyright © 2022 Imagination Technologies Ltd. + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice (including the next +# paragraph) shall be included in all copies or substantial portions of the +# Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +rogue_compiler = executable( + 'rogue_vk_compiler', + 'vk_compiler.c', + link_with : [libpowervr_rogue], + dependencies : [idep_mesautil, idep_nir], + include_directories : [ + inc_mesa, + inc_include, + inc_src, + inc_mapi, + inc_gallium, + inc_gallium_aux, + inc_compiler, + inc_rogue, + ], + build_by_default : with_imagination_tools, + install : false, +) diff --git a/src/imagination/rogue/tools/offline_compiler.c b/src/imagination/rogue/tools/vk_compiler.c similarity index 71% rename from src/imagination/rogue/tools/offline_compiler.c rename to src/imagination/rogue/tools/vk_compiler.c index bbf597f8d61..5bd5f936942 100644 --- a/src/imagination/rogue/tools/offline_compiler.c +++ b/src/imagination/rogue/tools/vk_compiler.c @@ -24,26 +24,23 @@ #include "compiler/shader_enums.h" #include "nir/nir.h" #include "rogue.h" -#include "rogue_build_data.h" -#include "rogue_compiler.h" -#include "rogue_dump.h" +#include "util/macros.h" #include "util/os_file.h" #include "util/ralloc.h" #include -#include #include #include #include #include -/* Number of hex columns to dump before starting a new line. */ -#define ARRAY_DUMP_COLS 16 +/* Number of hex columns to print before starting a new line. */ +#define ARRAY_PRINT_COLS 16 /** - * \file compiler.c + * \file vk_compiler.c * - * \brief Rogue offline compiler. + * \brief Rogue offline Vulkan shader compiler. */ static const struct option cmdline_opts[] = { @@ -56,28 +53,21 @@ static const struct option cmdline_opts[] = { { "help", no_argument, NULL, 'h' }, { "out", required_argument, NULL, 'o' }, - { "dump-c-array", no_argument, NULL, 'c' }, - { "dump-rogue", no_argument, NULL, 'r' }, - { "dump-nir", no_argument, NULL, 'n' }, - { NULL, 0, NULL, 0 }, }; -struct compiler_opts { +typedef struct compiler_opts { gl_shader_stage stage; char *file; char *entry; char *out_file; - bool dump_c_array; - bool dump_rogue; - bool dump_nir; -}; +} compiler_opts; static void usage(const char *argv0) { /* clang-format off */ - printf("Rogue offline compiler.\n"); - printf("Usage: %s -s -f [-e ] [-o ] [-c] [-r] [-n] [-h]\n", argv0); + printf("Rogue offline Vulkan shader compiler.\n"); + printf("Usage: %s -s -f [-e ] [-o ] [-h]\n", argv0); printf("\n"); printf("Required arguments:\n"); @@ -90,11 +80,6 @@ static void usage(const char *argv0) printf("\t-e, --entry Overrides the shader entry-point name (default: 'main').\n"); printf("\t-o, --out Overrides the output filename (default: 'out.bin').\n"); printf("\n"); - - printf("\t-c, --dump-c-array Print the shader binary as a C byte array.\n"); - printf("\t-r, --dump-rogue Prints the shader Rogue assembly.\n"); - printf("\t-n, --dump-nir Prints the shader NIR.\n"); - printf("\n"); /* clang-format on */ } @@ -104,14 +89,9 @@ static bool parse_cmdline(int argc, char *argv[], struct compiler_opts *opts) int longindex; while ( - (opt = - getopt_long(argc, argv, "crnhs:f:e:o:", cmdline_opts, &longindex)) != + (opt = getopt_long(argc, argv, "hs:f:e:o:", cmdline_opts, &longindex)) != -1) { switch (opt) { - case 'c': - opts->dump_c_array = true; - break; - case 'e': if (opts->entry) continue; @@ -126,10 +106,6 @@ static bool parse_cmdline(int argc, char *argv[], struct compiler_opts *opts) opts->file = optarg; break; - case 'n': - opts->dump_nir = true; - break; - case 'o': if (opts->out_file) continue; @@ -137,17 +113,13 @@ static bool parse_cmdline(int argc, char *argv[], struct compiler_opts *opts) opts->out_file = optarg; break; - case 'r': - opts->dump_rogue = true; - break; - case 's': if (opts->stage != MESA_SHADER_NONE) continue; - if (!strcmp(optarg, "frag")) + if (!strcmp(optarg, "frag") || !strcmp(optarg, "f")) opts->stage = MESA_SHADER_FRAGMENT; - else if (!strcmp(optarg, "vert")) + else if (!strcmp(optarg, "vert") || !strcmp(optarg, "v")) opts->stage = MESA_SHADER_VERTEX; else { fprintf(stderr, "Invalid stage \"%s\".\n", optarg); @@ -185,7 +157,7 @@ int main(int argc, char *argv[]) { /* Command-line options. */ /* N.B. MESA_SHADER_NONE != 0 */ - struct compiler_opts opts = { .stage = MESA_SHADER_NONE, 0 }; + compiler_opts opts = { .stage = MESA_SHADER_NONE, 0 }; /* Input file data. */ char *input_data; @@ -219,7 +191,8 @@ int main(int argc, char *argv[]) goto err_free_input; } - ctx = rogue_create_build_context(compiler); + /* Create build context. */ + ctx = rogue_build_context_create(compiler); if (!ctx) { fprintf(stderr, "Failed to set up build context.\n"); goto err_destroy_compiler; @@ -238,10 +211,6 @@ int main(int argc, char *argv[]) goto err_free_build_context; } - /* Dump NIR shader. */ - if (opts.dump_nir) - nir_print_shader(ctx->nir[opts.stage], stdout); - /* NIR -> Rogue. */ ctx->rogue[opts.stage] = rogue_nir_to_rogue(ctx, ctx->nir[opts.stage]); if (!ctx->rogue[opts.stage]) { @@ -249,28 +218,7 @@ int main(int argc, char *argv[]) goto err_free_build_context; } - /* Dump Rogue shader. */ - if (opts.dump_rogue) - rogue_dump_shader(ctx->rogue[opts.stage], stdout); - - /* Rogue -> Binary. */ - ctx->binary[opts.stage] = rogue_to_binary(ctx, ctx->rogue[opts.stage]); - if (!ctx->binary[opts.stage]) { - fprintf(stderr, "Failed to translate Rogue to binary.\n"); - goto err_free_build_context; - } - - /* Dump binary as a C array. */ - if (opts.dump_c_array) { - printf("uint8_t shader_bytes[%zu] = {", ctx->binary[opts.stage]->size); - for (size_t u = 0U; u < ctx->binary[opts.stage]->size; ++u) { - if (!(u % ARRAY_DUMP_COLS)) - printf("\n\t"); - - printf("0x%02x, ", ctx->binary[opts.stage]->data[u]); - } - printf("\n};\n"); - } + rogue_encode_shader(ctx, ctx->rogue[opts.stage], &ctx->binary[opts.stage]); /* Write shader binary to disk. */ fp = fopen(opts.out_file, "wb"); @@ -279,24 +227,22 @@ int main(int argc, char *argv[]) goto err_free_build_context; } - bytes_written = fwrite(ctx->binary[opts.stage]->data, - 1, - ctx->binary[opts.stage]->size, - fp); - if (bytes_written != ctx->binary[opts.stage]->size) { + bytes_written = + fwrite(ctx->binary[opts.stage].data, 1, ctx->binary[opts.stage].size, fp); + if (bytes_written != ctx->binary[opts.stage].size) { fprintf( stderr, - "Failed to write to output file \"%s\" (%zu bytes of %zu written).\n", + "Failed to write to output file \"%s\" (%zu bytes of %u written).\n", opts.out_file, bytes_written, - ctx->binary[opts.stage]->size); + ctx->binary[opts.stage].size); goto err_close_outfile; } /* Clean up. */ fclose(fp); ralloc_free(ctx); - rogue_compiler_destroy(compiler); + ralloc_free(compiler); free(input_data); return 0; @@ -306,7 +252,7 @@ err_close_outfile: err_free_build_context: ralloc_free(ctx); err_destroy_compiler: - rogue_compiler_destroy(compiler); + ralloc_free(compiler); err_free_input: free(input_data); diff --git a/src/imagination/vulkan/pvr_clear.c b/src/imagination/vulkan/pvr_clear.c index a5b782e2abd..b2cf694327f 100644 --- a/src/imagination/vulkan/pvr_clear.c +++ b/src/imagination/vulkan/pvr_clear.c @@ -487,7 +487,7 @@ VkResult pvr_device_init_graphics_static_clear_state(struct pvr_device *device) struct pvr_device_static_clear_state *state = &device->static_clear_state; const uint32_t cache_line_size = rogue_get_slc_cache_line_size(dev_info); - const struct rogue_shader_binary *passthrough_vert_shader; + struct util_dynarray passthrough_vert_shader; struct pvr_pds_vertex_shader_program pds_program; VkResult result; @@ -514,14 +514,16 @@ VkResult pvr_device_init_graphics_static_clear_state(struct pvr_device *device) state->usc_multi_layer_vertex_shader_bo = NULL; } + util_dynarray_init(&passthrough_vert_shader, NULL); pvr_hard_code_get_passthrough_vertex_shader(dev_info, &passthrough_vert_shader); result = pvr_gpu_upload_usc(device, - passthrough_vert_shader->data, - passthrough_vert_shader->size, + passthrough_vert_shader.data, + passthrough_vert_shader.size, cache_line_size, &state->usc_vertex_shader_bo); + util_dynarray_fini(&passthrough_vert_shader); if (result != VK_SUCCESS) goto err_free_usc_multi_layer_shader; diff --git a/src/imagination/vulkan/pvr_device.c b/src/imagination/vulkan/pvr_device.c index 18c9449fc5c..98f221bf660 100644 --- a/src/imagination/vulkan/pvr_device.c +++ b/src/imagination/vulkan/pvr_device.c @@ -55,12 +55,13 @@ #include "pvr_tex_state.h" #include "pvr_types.h" #include "pvr_winsys.h" -#include "rogue/rogue_compiler.h" +#include "rogue/rogue.h" #include "util/build_id.h" #include "util/log.h" #include "util/macros.h" #include "util/mesa-sha1.h" #include "util/os_misc.h" +#include "util/u_dynarray.h" #include "util/u_math.h" #include "vk_alloc.h" #include "vk_log.h" @@ -218,7 +219,7 @@ static void pvr_physical_device_finish(struct pvr_physical_device *pdevice) */ if (pdevice->compiler) - rogue_compiler_destroy(pdevice->compiler); + ralloc_free(pdevice->compiler); pvr_wsi_finish(pdevice); @@ -1349,13 +1350,14 @@ static VkResult pvr_device_init_compute_idfwdf_state(struct pvr_device *device) { uint64_t sampler_state[ROGUE_NUM_TEXSTATE_SAMPLER_WORDS]; uint64_t image_state[ROGUE_NUM_TEXSTATE_IMAGE_WORDS]; - const struct rogue_shader_binary *usc_program; + struct util_dynarray usc_program; struct pvr_texture_state_info tex_info; uint32_t *dword_ptr; uint32_t usc_shareds; uint32_t usc_temps; VkResult result; + util_dynarray_init(&usc_program, NULL); pvr_hard_code_get_idfwdf_program(&device->pdevice->dev_info, &usc_program, &usc_shareds, @@ -1365,10 +1367,12 @@ static VkResult pvr_device_init_compute_idfwdf_state(struct pvr_device *device) /* FIXME: Figure out the define for alignment of 16. */ result = pvr_gpu_upload_usc(device, - usc_program->data, - usc_program->size, + usc_program.data, + usc_program.size, 16, &device->idfwdf_state.usc); + util_dynarray_fini(&usc_program); + if (result != VK_SUCCESS) return result; diff --git a/src/imagination/vulkan/pvr_hardcode.c b/src/imagination/vulkan/pvr_hardcode.c index 27f6e169b34..8e2e1e62c80 100644 --- a/src/imagination/vulkan/pvr_hardcode.c +++ b/src/imagination/vulkan/pvr_hardcode.c @@ -56,6 +56,9 @@ enum pvr_hard_code_shader_type { PVR_HARD_CODE_SHADER_TYPE_GRAPHICS, }; +#define util_dynarray_append_mem(buf, size, mem) \ + memcpy(util_dynarray_grow_bytes((buf), 1, size), mem, size) + /* Table indicating which demo and for which device the compiler is capable of * generating valid shaders. */ @@ -65,7 +68,7 @@ static struct { } compatiblity_table[] = { { .name = "triangle", - .bvncs = { PVR_GX6250_BVNC, }, + .bvncs = { PVR_GX6250_BVNC, PVR_AXE_1_16M_BVNC, }, }, }; @@ -89,8 +92,10 @@ static const struct pvr_hard_coding_data { /* Mask of MESA_SHADER_* (gl_shader_stage). */ uint32_t flags; - struct rogue_shader_binary *const *const vert_shaders; - struct rogue_shader_binary *const *const frag_shaders; + uint8_t *const *const vert_shaders; + unsigned *vert_shader_sizes; + uint8_t *const *const frag_shaders; + unsigned *frag_shader_sizes; const struct pvr_vertex_shader_state *const *const vert_shader_states; const struct pvr_fragment_shader_state *const *const frag_shader_states; @@ -225,7 +230,7 @@ pvr_hard_code_graphics_get_flags(const struct pvr_device_info *const dev_info) void pvr_hard_code_graphics_shader(const struct pvr_device_info *const dev_info, uint32_t pipeline_n, gl_shader_stage stage, - struct rogue_shader_binary **const shader_out) + struct util_dynarray *shader_out) { const struct pvr_hard_coding_data *const data = pvr_get_hard_coding_data(dev_info); @@ -240,11 +245,15 @@ void pvr_hard_code_graphics_shader(const struct pvr_device_info *const dev_info, switch (stage) { case MESA_SHADER_VERTEX: - *shader_out = data->graphics.vert_shaders[pipeline_n]; + util_dynarray_append_mem(shader_out, + data->graphics.vert_shader_sizes[pipeline_n], + data->graphics.vert_shaders[pipeline_n]); break; case MESA_SHADER_FRAGMENT: - *shader_out = data->graphics.frag_shaders[pipeline_n]; + util_dynarray_append_mem(shader_out, + data->graphics.frag_shader_sizes[pipeline_n], + data->graphics.frag_shaders[pipeline_n]); break; default: @@ -339,33 +348,30 @@ void pvr_hard_code_graphics_get_build_info( void pvr_hard_code_get_idfwdf_program( const struct pvr_device_info *const dev_info, - const struct rogue_shader_binary **const program_out, + struct util_dynarray *program_out, uint32_t *usc_shareds_out, uint32_t *usc_temps_out) { - static const struct rogue_shader_binary shader = { - .size = 8U, - .data = { 0, 0, 0, 0, 0, 0, 0, 0 } - }; + static const uint8_t shader[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; mesa_loge("No hard coded idfwdf program. Returning empty program."); - *program_out = &shader; + + util_dynarray_append_mem(program_out, ARRAY_SIZE(shader), &shader[0]); + *usc_shareds_out = 12U; *usc_temps_out = 4U; } void pvr_hard_code_get_passthrough_vertex_shader( const struct pvr_device_info *const dev_info, - const struct rogue_shader_binary **const program_out) + struct util_dynarray *program_out) { - static const struct rogue_shader_binary shader = { - .size = 8U, - .data = { 0, 0, 0, 0, 0, 0, 0, 0 } - }; + static const uint8_t shader[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; mesa_loge( "No hard coded passthrough vertex shader. Returning empty shader."); - *program_out = &shader; + + util_dynarray_append_mem(program_out, ARRAY_SIZE(shader), &shader[0]); }; /* Render target array (RTA). */ diff --git a/src/imagination/vulkan/pvr_hardcode.h b/src/imagination/vulkan/pvr_hardcode.h index 069201452f1..3fcdd1e361e 100644 --- a/src/imagination/vulkan/pvr_hardcode.h +++ b/src/imagination/vulkan/pvr_hardcode.h @@ -29,7 +29,7 @@ #include #include "compiler/shader_enums.h" -#include "rogue/rogue_build_data.h" +#include "rogue/rogue.h" #include "util/u_dynarray.h" /** @@ -53,8 +53,8 @@ struct pvr_explicit_constant_usage { }; struct pvr_hard_code_compute_build_info { - struct rogue_ubo_data ubo_data; - struct rogue_compile_time_consts_data compile_time_consts_data; + rogue_ubo_data ubo_data; + rogue_compile_time_consts_data compile_time_consts_data; uint32_t local_invocation_regs[2]; uint32_t work_group_regs[3]; @@ -65,10 +65,10 @@ struct pvr_hard_code_compute_build_info { }; struct pvr_hard_code_graphics_build_info { - struct rogue_build_data stage_data; + rogue_build_data stage_data; - struct rogue_common_build_data vert_common_data; - struct rogue_common_build_data frag_common_data; + rogue_common_build_data vert_common_data; + rogue_common_build_data frag_common_data; struct pvr_explicit_constant_usage vert_explicit_conts_usage; struct pvr_explicit_constant_usage frag_explicit_conts_usage; @@ -96,11 +96,10 @@ pvr_hard_code_graphics_get_flags(const struct pvr_device_info *const dev_info); * This pipeline number to request data for the first pipeline to be created * is 0 and should be incremented for each subsequent pipeline. */ -void pvr_hard_code_graphics_shader( - const struct pvr_device_info *const dev_info, - uint32_t pipeline_n, - gl_shader_stage stage, - struct rogue_shader_binary **const shader_out); +void pvr_hard_code_graphics_shader(const struct pvr_device_info *const dev_info, + uint32_t pipeline_n, + gl_shader_stage stage, + struct util_dynarray *shader_out); void pvr_hard_code_graphics_vertex_state( const struct pvr_device_info *const dev_info, @@ -116,19 +115,19 @@ void pvr_hard_code_graphics_get_build_info( const struct pvr_device_info *const dev_info, uint32_t pipeline_n, gl_shader_stage stage, - struct rogue_common_build_data *const common_build_data, - struct rogue_build_data *const build_data, + rogue_common_build_data *const common_build_data, + rogue_build_data *const build_data, struct pvr_explicit_constant_usage *const explicit_const_usage); void pvr_hard_code_get_idfwdf_program( const struct pvr_device_info *const dev_info, - const struct rogue_shader_binary **const program_out, + struct util_dynarray *program_out, uint32_t *usc_shareds_out, uint32_t *usc_temps_out); void pvr_hard_code_get_passthrough_vertex_shader( const struct pvr_device_info *const dev_info, - const struct rogue_shader_binary **const program_out); + struct util_dynarray *program_out); void pvr_hard_code_get_passthrough_rta_vertex_shader( const struct pvr_device_info *const dev_info, struct util_dynarray *program_out); diff --git a/src/imagination/vulkan/pvr_pipeline.c b/src/imagination/vulkan/pvr_pipeline.c index 2cb6fd90654..e97459eca78 100644 --- a/src/imagination/vulkan/pvr_pipeline.c +++ b/src/imagination/vulkan/pvr_pipeline.c @@ -42,7 +42,6 @@ #include "pvr_shader.h" #include "pvr_types.h" #include "rogue/rogue.h" -#include "rogue/rogue_build_data.h" #include "util/log.h" #include "util/macros.h" #include "util/ralloc.h" @@ -1423,7 +1422,7 @@ pvr_graphics_pipeline_compile(struct pvr_device *const device, VkResult result; /* Setup shared build context. */ - ctx = rogue_create_build_context(compiler); + ctx = rogue_build_context_create(compiler); if (!ctx) return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY); @@ -1507,8 +1506,8 @@ pvr_graphics_pipeline_compile(struct pvr_device *const device, return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY); } - ctx->binary[stage] = pvr_rogue_to_binary(ctx, ctx->rogue[stage]); - if (!ctx->binary[stage]) { + pvr_rogue_to_binary(ctx, ctx->rogue[stage], &ctx->binary[stage]); + if (!ctx->binary[stage].size) { ralloc_free(ctx); return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY); } @@ -1527,8 +1526,8 @@ pvr_graphics_pipeline_compile(struct pvr_device *const device, } result = pvr_gpu_upload_usc(device, - ctx->binary[MESA_SHADER_VERTEX]->data, - ctx->binary[MESA_SHADER_VERTEX]->size, + ctx->binary[MESA_SHADER_VERTEX].data, + ctx->binary[MESA_SHADER_VERTEX].size, cache_line_size, &gfx_pipeline->shader_state.vertex.bo); if (result != VK_SUCCESS) @@ -1547,8 +1546,8 @@ pvr_graphics_pipeline_compile(struct pvr_device *const device, } result = pvr_gpu_upload_usc(device, - ctx->binary[MESA_SHADER_FRAGMENT]->data, - ctx->binary[MESA_SHADER_FRAGMENT]->size, + ctx->binary[MESA_SHADER_FRAGMENT].data, + ctx->binary[MESA_SHADER_FRAGMENT].size, cache_line_size, &gfx_pipeline->shader_state.fragment.bo); if (result != VK_SUCCESS) diff --git a/src/imagination/vulkan/pvr_shader.c b/src/imagination/vulkan/pvr_shader.c index a0063d8993e..6539f5c8e79 100644 --- a/src/imagination/vulkan/pvr_shader.c +++ b/src/imagination/vulkan/pvr_shader.c @@ -31,7 +31,6 @@ #include "pvr_private.h" #include "pvr_shader.h" #include "rogue/rogue.h" -#include "rogue/rogue_shader.h" #include "spirv/nir_spirv.h" #include "vk_format.h" #include "vk_shader_module.h" @@ -52,7 +51,7 @@ * \param[in] create_info Shader creation info from Vulkan pipeline. * \return A nir_shader* if successful, or NULL if unsuccessful. */ -nir_shader *pvr_spirv_to_nir(struct rogue_build_ctx *ctx, +nir_shader *pvr_spirv_to_nir(rogue_build_ctx *ctx, gl_shader_stage stage, const VkPipelineShaderStageCreateInfo *create_info) { @@ -84,8 +83,7 @@ nir_shader *pvr_spirv_to_nir(struct rogue_build_ctx *ctx, * \param[in] nir NIR shader. * \return A rogue_shader* if successful, or NULL if unsuccessful. */ -struct rogue_shader *pvr_nir_to_rogue(struct rogue_build_ctx *ctx, - nir_shader *nir) +rogue_shader *pvr_nir_to_rogue(rogue_build_ctx *ctx, nir_shader *nir) { return rogue_nir_to_rogue(ctx, nir); } @@ -95,10 +93,11 @@ struct rogue_shader *pvr_nir_to_rogue(struct rogue_build_ctx *ctx, * * \param[in] ctx Shared multi-stage build context. * \param[in] shader Rogue shader. - * \return A rogue_shader_binary* if successful, or NULL if unsuccessful. + * \param[out] binary Array containing shader binary. */ -struct rogue_shader_binary *pvr_rogue_to_binary(struct rogue_build_ctx *ctx, - struct rogue_shader *shader) +void pvr_rogue_to_binary(rogue_build_ctx *ctx, + rogue_shader *shader, + struct util_dynarray *binary) { - return rogue_to_binary(ctx, shader); + rogue_encode_shader(ctx, shader, binary); } diff --git a/src/imagination/vulkan/pvr_shader.h b/src/imagination/vulkan/pvr_shader.h index c52265485d1..5d82a5e06fa 100644 --- a/src/imagination/vulkan/pvr_shader.h +++ b/src/imagination/vulkan/pvr_shader.h @@ -28,21 +28,19 @@ #include "compiler/shader_enums.h" #include "nir/nir.h" +#include "rogue/rogue.h" +#include "util/u_dynarray.h" #include "vulkan/vulkan.h" -struct rogue_build_ctx; -struct rogue_compiler; -struct rogue_shader; - nir_shader * -pvr_spirv_to_nir(struct rogue_build_ctx *ctx, +pvr_spirv_to_nir(rogue_build_ctx *ctx, gl_shader_stage stage, const VkPipelineShaderStageCreateInfo *create_info); -struct rogue_shader *pvr_nir_to_rogue(struct rogue_build_ctx *ctx, - nir_shader *nir); +rogue_shader *pvr_nir_to_rogue(rogue_build_ctx *ctx, nir_shader *nir); -struct rogue_shader_binary *pvr_rogue_to_binary(struct rogue_build_ctx *ctx, - struct rogue_shader *rogue); +void pvr_rogue_to_binary(rogue_build_ctx *ctx, + rogue_shader *shader, + struct util_dynarray *binary); #endif /* PVR_SHADER_H */