289 lines
11 KiB
C
289 lines
11 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.
|
|
*/
|
|
|
|
/**
|
|
* \file rogue_validate.c
|
|
*
|
|
* \brief Contains rules and functions for validating Rogue data structures.
|
|
*/
|
|
|
|
#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.
|
|
*
|
|
* \param[in] operand The operand.
|
|
* \return true if valid, otherwise false.
|
|
*/
|
|
bool rogue_validate_operand(const struct rogue_operand *operand)
|
|
{
|
|
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:
|
|
return false;
|
|
}
|
|
|
|
/* Validate each instruction. */
|
|
foreach_instr (instr, &shader->instr_list)
|
|
CHECKF(rogue_validate_instr(instr), "Failed to validate instruction.");
|
|
|
|
return true;
|
|
}
|