mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2026-05-20 11:18:11 +02:00
Signed-off-by: Simon Perretta <simon.perretta@imgtec.com> Acked-by: Karmjit Mahil <Karmjit.Mahil@imgtec.com> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/21550>
754 lines
25 KiB
C
754 lines
25 KiB
C
/*
|
|
* 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/bitscan.h"
|
|
#include "util/macros.h"
|
|
#include "util/u_dynarray.h"
|
|
|
|
#include <stdbool.h>
|
|
|
|
/**
|
|
* \file rogue_validate.c
|
|
*
|
|
* \brief Contains functions to validate Rogue IR.
|
|
*/
|
|
|
|
/* TODO: Rogue_validate should make sure that immediate (sources) don't have any
|
|
* modifiers set... */
|
|
|
|
/* 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.*/
|
|
struct {
|
|
const rogue_block *block; /** Current basic block being validated. */
|
|
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. */
|
|
bool src; /** Current reference type (src/dst). */
|
|
unsigned param; /** Current reference src/dst index. */
|
|
} ctx;
|
|
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)
|
|
{
|
|
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);
|
|
}
|
|
|
|
fputs("\n", stderr);
|
|
|
|
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->ctx.block) {
|
|
if (state->ctx.block->label)
|
|
ralloc_asprintf_append(&msg, " block \"%s\"", state->ctx.block->label);
|
|
else
|
|
ralloc_asprintf_append(&msg, " block%u", state->ctx.block->index);
|
|
}
|
|
|
|
if (state->ctx.instr) {
|
|
ralloc_asprintf_append(&msg, " instr %u", state->ctx.instr->index);
|
|
}
|
|
|
|
if (state->ctx.ref) {
|
|
ralloc_asprintf_append(&msg,
|
|
" %s %u",
|
|
state->ctx.src ? "src" : "dst",
|
|
state->ctx.param);
|
|
}
|
|
|
|
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_dst(rogue_validation_state *state,
|
|
const rogue_instr_dst *dst,
|
|
uint64_t supported_dst_types,
|
|
unsigned i,
|
|
unsigned stride,
|
|
unsigned repeat,
|
|
uint64_t repeat_mask)
|
|
{
|
|
state->ctx.ref = &dst->ref;
|
|
state->ctx.src = false;
|
|
state->ctx.param = i;
|
|
|
|
if (rogue_ref_is_null(&dst->ref))
|
|
validate_log(state, "Destination has not been set.");
|
|
|
|
if (!rogue_ref_type_supported(dst->ref.type, supported_dst_types))
|
|
validate_log(state, "Unsupported destination type.");
|
|
|
|
if (rogue_ref_is_reg_or_regarray(&dst->ref) && stride != ~0U) {
|
|
unsigned dst_size = stride + 1;
|
|
if (repeat_mask & (1 << i))
|
|
dst_size *= repeat;
|
|
|
|
if (rogue_ref_is_regarray(&dst->ref)) {
|
|
if (rogue_ref_get_regarray_size(&dst->ref) != dst_size) {
|
|
validate_log(state,
|
|
"Expected regarray size %u, got %u.",
|
|
dst_size,
|
|
rogue_ref_get_regarray_size(&dst->ref));
|
|
}
|
|
} else if (dst_size > 1) {
|
|
validate_log(state, "Expected regarray type for destination.");
|
|
}
|
|
}
|
|
|
|
state->ctx.ref = NULL;
|
|
}
|
|
|
|
static void validate_src(rogue_validation_state *state,
|
|
const rogue_instr_src *src,
|
|
uint64_t supported_src_types,
|
|
unsigned i,
|
|
unsigned stride,
|
|
unsigned repeat,
|
|
uint64_t repeat_mask)
|
|
{
|
|
state->ctx.ref = &src->ref;
|
|
state->ctx.src = true;
|
|
state->ctx.param = i;
|
|
|
|
if (rogue_ref_is_null(&src->ref))
|
|
validate_log(state, "Source has not been set.");
|
|
|
|
if (!rogue_ref_type_supported(src->ref.type, supported_src_types))
|
|
validate_log(state, "Unsupported source type.");
|
|
|
|
if (rogue_ref_is_reg_or_regarray(&src->ref) && stride != ~0U) {
|
|
unsigned src_size = stride + 1;
|
|
if (repeat_mask & (1 << i))
|
|
src_size *= repeat;
|
|
|
|
if (rogue_ref_is_regarray(&src->ref)) {
|
|
if (rogue_ref_get_regarray_size(&src->ref) != src_size) {
|
|
validate_log(state,
|
|
"Expected regarray size %u, got %u.",
|
|
src_size,
|
|
rogue_ref_get_regarray_size(&src->ref));
|
|
}
|
|
} else if (src_size > 1) {
|
|
validate_log(state, "Expected regarray type for source.");
|
|
}
|
|
}
|
|
|
|
state->ctx.ref = NULL;
|
|
}
|
|
|
|
static bool validate_alu_op_mod_combo(uint64_t mods)
|
|
{
|
|
rogue_foreach_mod_in_set (mod, mods) {
|
|
const rogue_alu_op_mod_info *info = &rogue_alu_op_mod_infos[mod];
|
|
|
|
/* Check if any excluded op mods have been included. */
|
|
if (info->exclude & mods)
|
|
return false;
|
|
|
|
/* Check if any required op mods have been missed. */
|
|
if (info->require && !(info->require & mods))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
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];
|
|
|
|
/* Check if instruction modifiers are valid. */
|
|
if (!rogue_mods_supported(alu->mod, info->supported_op_mods))
|
|
validate_log(state, "Unsupported ALU op modifiers.");
|
|
|
|
if (!validate_alu_op_mod_combo(alu->mod))
|
|
validate_log(state, "Unsupported ALU op modifier combination.");
|
|
|
|
/* Instruction repeat checks. */
|
|
if (alu->instr.repeat > 1 && !info->dst_repeat_mask &&
|
|
!info->src_repeat_mask) {
|
|
validate_log(state, "Repeat set for ALU op without repeat support.");
|
|
}
|
|
|
|
/* Validate destinations and sources for ungrouped shaders. */
|
|
if (!state->shader->is_grouped) {
|
|
for (unsigned i = 0; i < info->num_dsts; ++i) {
|
|
validate_dst(state,
|
|
&alu->dst[i],
|
|
info->supported_dst_types[i],
|
|
i,
|
|
info->dst_stride[i],
|
|
alu->instr.repeat,
|
|
info->dst_repeat_mask);
|
|
}
|
|
|
|
for (unsigned i = 0; i < info->num_srcs; ++i) {
|
|
validate_src(state,
|
|
&alu->src[i],
|
|
info->supported_src_types[i],
|
|
i,
|
|
info->src_stride[i],
|
|
alu->instr.repeat,
|
|
info->src_repeat_mask);
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool validate_backend_op_mod_combo(uint64_t mods)
|
|
{
|
|
rogue_foreach_mod_in_set (mod, mods) {
|
|
const rogue_backend_op_mod_info *info = &rogue_backend_op_mod_infos[mod];
|
|
|
|
/* Check if any excluded op mods have been included. */
|
|
if (info->exclude & mods)
|
|
return false;
|
|
|
|
/* Check if any required op mods have been missed. */
|
|
if (info->require && !(info->require & mods))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
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];
|
|
|
|
/* Check if instruction modifiers are valid. */
|
|
if (!rogue_mods_supported(backend->mod, info->supported_op_mods))
|
|
validate_log(state, "Unsupported backend op modifiers.");
|
|
|
|
if (!validate_backend_op_mod_combo(backend->mod))
|
|
validate_log(state, "Unsupported backend op modifier combination.");
|
|
|
|
/* Instruction repeat checks. */
|
|
if (backend->instr.repeat > 1 && !info->dst_repeat_mask &&
|
|
!info->src_repeat_mask) {
|
|
validate_log(state, "Repeat set for backend op without repeat support.");
|
|
}
|
|
|
|
/* Validate destinations and sources for ungrouped shaders. */
|
|
if (!state->shader->is_grouped) {
|
|
for (unsigned i = 0; i < info->num_dsts; ++i) {
|
|
validate_dst(state,
|
|
&backend->dst[i],
|
|
info->supported_dst_types[i],
|
|
i,
|
|
info->dst_stride[i],
|
|
backend->instr.repeat,
|
|
info->dst_repeat_mask);
|
|
}
|
|
|
|
for (unsigned i = 0; i < info->num_srcs; ++i) {
|
|
validate_src(state,
|
|
&backend->src[i],
|
|
info->supported_src_types[i],
|
|
i,
|
|
info->src_stride[i],
|
|
backend->instr.repeat,
|
|
info->src_repeat_mask);
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool validate_ctrl_op_mod_combo(uint64_t mods)
|
|
{
|
|
rogue_foreach_mod_in_set (mod, mods) {
|
|
const rogue_ctrl_op_mod_info *info = &rogue_ctrl_op_mod_infos[mod];
|
|
|
|
/* Check if any excluded op mods have been included. */
|
|
if (info->exclude & mods)
|
|
return false;
|
|
|
|
/* Check if any required op mods have been missed. */
|
|
if (info->require && !(info->require & mods))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/* 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.");
|
|
|
|
/* Check if instruction modifiers are valid. */
|
|
if (!rogue_mods_supported(ctrl->mod, info->supported_op_mods))
|
|
validate_log(state, "Unsupported CTRL op modifiers.");
|
|
|
|
if (!validate_ctrl_op_mod_combo(ctrl->mod))
|
|
validate_log(state, "Unsupported CTRL op modifier combination.");
|
|
|
|
/* Instruction repeat checks. */
|
|
if (ctrl->instr.repeat > 1 && !info->dst_repeat_mask &&
|
|
!info->src_repeat_mask) {
|
|
validate_log(state, "Repeat set for CTRL op without repeat support.");
|
|
}
|
|
|
|
/* Validate destinations and sources for ungrouped shaders. */
|
|
if (!state->shader->is_grouped) {
|
|
for (unsigned i = 0; i < info->num_dsts; ++i) {
|
|
validate_dst(state,
|
|
&ctrl->dst[i],
|
|
info->supported_dst_types[i],
|
|
i,
|
|
info->dst_stride[i],
|
|
ctrl->instr.repeat,
|
|
info->dst_repeat_mask);
|
|
}
|
|
|
|
for (unsigned i = 0; i < info->num_srcs; ++i) {
|
|
validate_src(state,
|
|
&ctrl->src[i],
|
|
info->supported_src_types[i],
|
|
i,
|
|
info->src_stride[i],
|
|
ctrl->instr.repeat,
|
|
info->src_repeat_mask);
|
|
}
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
static bool validate_bitwise_op_mod_combo(uint64_t mods)
|
|
{
|
|
rogue_foreach_mod_in_set (mod, mods) {
|
|
const rogue_bitwise_op_mod_info *info = &rogue_bitwise_op_mod_infos[mod];
|
|
|
|
/* Check if any excluded op mods have been included. */
|
|
if (info->exclude & mods)
|
|
return false;
|
|
|
|
/* Check if any required op mods have been missed. */
|
|
if (info->require && !(info->require & mods))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void validate_bitwise_instr(rogue_validation_state *state,
|
|
const rogue_bitwise_instr *bitwise)
|
|
{
|
|
if (bitwise->op == ROGUE_BITWISE_OP_INVALID ||
|
|
bitwise->op >= ROGUE_BITWISE_OP_COUNT)
|
|
validate_log(state, "Unknown bitwise op 0x%x encountered.", bitwise->op);
|
|
|
|
const rogue_bitwise_op_info *info = &rogue_bitwise_op_infos[bitwise->op];
|
|
|
|
/* Check if instruction modifiers are valid. */
|
|
if (!rogue_mods_supported(bitwise->mod, info->supported_op_mods))
|
|
validate_log(state, "Unsupported bitwise op modifiers.");
|
|
|
|
if (!validate_bitwise_op_mod_combo(bitwise->mod))
|
|
validate_log(state, "Unsupported bitwise op modifier combination.");
|
|
|
|
/* Instruction repeat checks. */
|
|
if (bitwise->instr.repeat > 1 && !info->dst_repeat_mask &&
|
|
!info->src_repeat_mask) {
|
|
validate_log(state, "Repeat set for bitwise op without repeat support.");
|
|
}
|
|
|
|
/* Validate destinations and sources for ungrouped shaders. */
|
|
if (!state->shader->is_grouped) {
|
|
for (unsigned i = 0; i < info->num_dsts; ++i) {
|
|
validate_dst(state,
|
|
&bitwise->dst[i],
|
|
info->supported_dst_types[i],
|
|
i,
|
|
info->dst_stride[i],
|
|
bitwise->instr.repeat,
|
|
info->dst_repeat_mask);
|
|
}
|
|
|
|
for (unsigned i = 0; i < info->num_srcs; ++i) {
|
|
validate_src(state,
|
|
&bitwise->src[i],
|
|
info->supported_src_types[i],
|
|
i,
|
|
info->src_stride[i],
|
|
bitwise->instr.repeat,
|
|
info->src_repeat_mask);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Returns true if instruction can end block. */
|
|
static bool validate_instr(rogue_validation_state *state,
|
|
const rogue_instr *instr)
|
|
{
|
|
state->ctx.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;
|
|
|
|
case ROGUE_INSTR_TYPE_BITWISE:
|
|
validate_bitwise_instr(state, rogue_instr_as_bitwise(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->ctx.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->ctx.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->ctx.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: Validate block properties. */
|
|
state->ctx.block = block;
|
|
|
|
if (list_is_empty(&block->instrs)) {
|
|
validate_log(state, "Block is empty.");
|
|
state->ctx.block = NULL;
|
|
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.");
|
|
|
|
state->ctx.block = NULL;
|
|
}
|
|
|
|
static void validate_reg_use(rogue_validation_state *state,
|
|
const rogue_reg_use *use,
|
|
uint64_t supported_io_srcs)
|
|
{
|
|
/* No restrictions. */
|
|
if (!supported_io_srcs)
|
|
return;
|
|
|
|
const rogue_instr *instr = use->instr;
|
|
|
|
rogue_foreach_phase_in_set (p, rogue_instr_supported_phases(instr)) {
|
|
enum rogue_io io_src = rogue_instr_src_io_src(instr, p, use->src_index);
|
|
if (io_src == ROGUE_IO_INVALID)
|
|
validate_log(state, "Register used where no source is present.");
|
|
|
|
if (!rogue_io_supported(io_src, supported_io_srcs))
|
|
validate_log(state,
|
|
"Register class unsupported in S%u.",
|
|
io_src - ROGUE_IO_S0); /* TODO: Either add info here to
|
|
get register class and print as
|
|
string, or add info to
|
|
rogue_validation_state. */
|
|
}
|
|
}
|
|
|
|
static void validate_reg_state(rogue_validation_state *state,
|
|
rogue_shader *shader)
|
|
{
|
|
BITSET_WORD *regs_used = NULL;
|
|
|
|
for (enum rogue_reg_class class = 0; class < ROGUE_REG_CLASS_COUNT;
|
|
++class) {
|
|
const rogue_reg_info *info = &rogue_reg_infos[class];
|
|
if (info->num)
|
|
regs_used =
|
|
rzalloc_size(state, sizeof(*regs_used) * BITSET_WORDS(info->num));
|
|
|
|
rogue_foreach_reg (reg, shader, class) {
|
|
/* Ensure that the range restrictions are satisfied. */
|
|
if (info->num && reg->index >= info->num)
|
|
validate_log(state, "%s register index out of range.", info->name);
|
|
|
|
/* Ensure that only registers of this class are in the regs list. */
|
|
if (reg->class != class)
|
|
validate_log(state,
|
|
"%s register found in %s register list.",
|
|
rogue_reg_infos[reg->class].name,
|
|
info->name);
|
|
|
|
/* Track the registers used in the class. */
|
|
if (info->num)
|
|
BITSET_SET(regs_used, reg->index);
|
|
|
|
/* Check register cache entry. */
|
|
rogue_reg **reg_cached =
|
|
util_sparse_array_get(&shader->reg_cache[class], reg->index);
|
|
if (!reg_cached || !*reg_cached)
|
|
validate_log(state,
|
|
"Missing %s register %u cache entry.",
|
|
info->name,
|
|
reg->index);
|
|
else if (*reg_cached != reg || (*reg_cached)->index != reg->index ||
|
|
(*reg_cached)->class != reg->class)
|
|
validate_log(state,
|
|
"Mismatching %s register %u cache entry.",
|
|
info->name,
|
|
reg->index);
|
|
else if (reg_cached != reg->cached)
|
|
validate_log(state,
|
|
"Mismatching %s register %u cache entry pointer.",
|
|
info->name,
|
|
reg->index);
|
|
|
|
/* Validate register uses. */
|
|
const rogue_reg_info *reg_info = &rogue_reg_infos[class];
|
|
rogue_foreach_reg_use (use, reg)
|
|
validate_reg_use(state, use, reg_info->supported_io_srcs);
|
|
}
|
|
|
|
/* Check that the registers used matches the usage list. */
|
|
if (info->num && memcmp(shader->regs_used[class],
|
|
regs_used,
|
|
sizeof(*regs_used) * BITSET_WORDS(info->num)))
|
|
validate_log(state, "Incorrect %s register usage list.", info->name);
|
|
|
|
ralloc_free(regs_used);
|
|
}
|
|
|
|
/* Check that SSA registers aren't being written to more than once. */
|
|
rogue_foreach_reg (reg, shader, ROGUE_REG_CLASS_SSA)
|
|
if (list_length(®->writes) > 1)
|
|
validate_log(state,
|
|
"SSA register %u is written to more than once.",
|
|
reg->index);
|
|
|
|
rogue_foreach_regarray (regarray, shader) {
|
|
/* Validate regarray contents. */
|
|
validate_regarray(state, regarray);
|
|
|
|
/* Check regarray cache entry. */
|
|
uint64_t key = rogue_regarray_cache_key(regarray->size,
|
|
regarray->regs[0]->class,
|
|
regarray->regs[0]->index,
|
|
false,
|
|
0);
|
|
rogue_regarray **regarray_cached =
|
|
util_sparse_array_get(&shader->regarray_cache, key);
|
|
if (!regarray_cached || !*regarray_cached)
|
|
validate_log(state, "Missing regarray cache entry.");
|
|
else if (*regarray_cached != regarray ||
|
|
(*regarray_cached)->size != regarray->size ||
|
|
(*regarray_cached)->parent != regarray->parent ||
|
|
(*regarray_cached)->regs != regarray->regs)
|
|
validate_log(state, "Mismatching regarray cache entry.");
|
|
else if (regarray_cached != regarray->cached)
|
|
validate_log(state, "Mismatching regarray cache entry pointer.");
|
|
|
|
if (regarray->parent && (regarray->parent->size <= regarray->size ||
|
|
regarray->parent->parent))
|
|
validate_log(state, "Invalid sub-regarray.");
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|