pvr: Add new Rogue compiler framework

Signed-off-by: Simon Perretta <simon.perretta@imgtec.com>
Acked-by: Frank Binns <frank.binns@imgtec.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/20430>
This commit is contained in:
Simon Perretta 2022-12-23 23:22:10 +00:00 committed by Marge Bot
parent 5038a049f1
commit d187418f63
58 changed files with 10002 additions and 4595 deletions

View file

@ -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
---------------------------------

View file

@ -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',

View file

@ -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')

View file

@ -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);
}
}
}
}

View file

@ -21,14 +21,21 @@
* SOFTWARE.
*/
#include <assert.h>
#include <stdint.h>
#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 <assert.h>
#include <stdbool.h>
#include <stdint.h>
/**
* \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;

View file

@ -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);

View file

@ -21,24 +21,41 @@
* SOFTWARE.
*/
#ifndef ROGUE_DUMP_H
#define ROGUE_DUMP_H
#include <stdbool.h>
#include <stdio.h>
#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 <stdbool.h>
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;
}

View file

@ -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 <stdbool.h>
/**
* \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;
}

View file

@ -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 <stdbool.h>
/**
* \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;
}

View file

@ -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 <stdbool.h>
/**
* \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;
}

View file

@ -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 <stdbool.h>
/**
* \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(&reg->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(&reg->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;
}

View file

@ -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 <stdbool.h>
/**
* \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;
}

View file

@ -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 <stdbool.h>
/**
* \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;
}

View file

@ -21,54 +21,53 @@
* SOFTWARE.
*/
#ifndef ROGUE_VALIDATE_H
#define ROGUE_VALIDATE_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>
#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 <stdbool.h>
/**
* \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;
}

View file

@ -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 <stdbool.h>
/**
* \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;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -21,31 +21,47 @@
* SOFTWARE.
*/
#ifndef ROGUE_CONSTREGS_H
#define ROGUE_CONSTREGS_H
#include <stddef.h>
#include <stdint.h>
#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

View file

@ -21,33 +21,39 @@
* SOFTWARE.
*/
#ifndef ROGUE_ENCODERS_H
#define ROGUE_ENCODERS_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#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

View file

@ -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

View file

@ -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;

View file

@ -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 <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#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 */

View file

@ -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"

View file

@ -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 */

View file

@ -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);
}

View file

@ -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 <stddef.h>
#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);
}

View file

@ -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 */

View file

@ -25,7 +25,7 @@
#include <stdint.h>
#include <stdlib.h>
#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;

View file

@ -21,50 +21,45 @@
* SOFTWARE.
*/
#ifndef ROGUE_REGALLOC_H
#define ROGUE_REGALLOC_H
#include <stdbool.h>
#include <stddef.h>
#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

View file

@ -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 <stdbool.h>
#include <stdio.h>
#include <unistd.h>
/**
* \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);
}

View file

@ -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 <inttypes.h>
#include <stdbool.h>
#include <stdint.h>
#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;
}

File diff suppressed because it is too large Load diff

View file

@ -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 <stdbool.h>
#include <stdio.h>
#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 */

View file

@ -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 <assert.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#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

View file

@ -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",
},
};

View file

@ -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 <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#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;
}

View file

@ -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 <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#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 */

View file

@ -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 */

View file

@ -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 <stdbool.h>
/**
* \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;
}

View file

@ -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 */

View file

@ -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 <assert.h>
#include <stddef.h>
#include <stdint.h>
#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 */

View file

@ -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 <stddef.h>
#include <stdint.h>
#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 */

View file

@ -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 <inttypes.h>
#include <stdbool.h>
/**
* \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(&reg->writes)) {
fputs(" <none>\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(&reg->uses)) {
fputs(" <none>\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(" <entry>\n", fp);
else
fputs(" <none>\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("<none>", 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);
}
}

View file

@ -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 <stddef.h>
#include <stdint.h>
#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 = &reg_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;
}

View file

@ -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 <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#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;
}

View file

@ -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 <stdbool.h>
#include <stddef.h>
#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 */

View file

@ -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 <assert.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#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;
}

View file

@ -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 <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#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 */

View file

@ -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 <stdbool.h>
#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(&reg->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;
}

View file

@ -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,
)

View file

@ -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 <getopt.h>
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* 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 <stage> -f <file> [-e <entry>] [-o <file>] [-c] [-r] [-n] [-h]\n", argv0);
printf("Rogue offline Vulkan shader compiler.\n");
printf("Usage: %s -s <stage> -f <file> [-e <entry>] [-o <file>] [-h]\n", argv0);
printf("\n");
printf("Required arguments:\n");
@ -90,11 +80,6 @@ static void usage(const char *argv0)
printf("\t-e, --entry <entry> Overrides the shader entry-point name (default: 'main').\n");
printf("\t-o, --out <file> 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);

View file

@ -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;

View file

@ -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;

View file

@ -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). */

View file

@ -29,7 +29,7 @@
#include <vulkan/vulkan_core.h>
#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);

View file

@ -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)

View file

@ -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);
}

View file

@ -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 */