| /* |
| * 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; |
| } |