Path: blob/21.2-virgl/src/gallium/auxiliary/tgsi/tgsi_sanity.c
4565 views
/**************************************************************************1*2* Copyright 2008 VMware, Inc.3* All Rights Reserved.4*5* Permission is hereby granted, free of charge, to any person obtaining a6* copy of this software and associated documentation files (the7* "Software"), to deal in the Software without restriction, including8* without limitation the rights to use, copy, modify, merge, publish,9* distribute, sub license, and/or sell copies of the Software, and to10* permit persons to whom the Software is furnished to do so, subject to11* the following conditions:12*13* The above copyright notice and this permission notice (including the14* next paragraph) shall be included in all copies or substantial portions15* of the Software.16*17* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS18* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF19* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.20* IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR21* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,22* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE23* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.24*25**************************************************************************/2627#include "util/u_debug.h"28#include "util/u_memory.h"29#include "util/u_prim.h"30#include "cso_cache/cso_hash.h"31#include "tgsi_sanity.h"32#include "tgsi_info.h"33#include "tgsi_iterate.h"343536DEBUG_GET_ONCE_BOOL_OPTION(print_sanity, "TGSI_PRINT_SANITY", FALSE)373839typedef struct {40uint file : 28;41/* max 2 dimensions */42uint dimensions : 4;43uint indices[2];44} scan_register;4546struct sanity_check_ctx47{48struct tgsi_iterate_context iter;49struct cso_hash regs_decl;50struct cso_hash regs_used;51struct cso_hash regs_ind_used;5253uint num_imms;54uint num_instructions;55uint index_of_END;5657uint errors;58uint warnings;59uint implied_array_size;60uint implied_out_array_size;6162boolean print;63};6465static inline unsigned66scan_register_key(const scan_register *reg)67{68unsigned key = reg->file;69key |= (reg->indices[0] << 4);70key |= (reg->indices[1] << 18);7172return key;73}7475static void76fill_scan_register1d(scan_register *reg,77uint file, uint index)78{79reg->file = file;80reg->dimensions = 1;81reg->indices[0] = index;82reg->indices[1] = 0;83}8485static void86fill_scan_register2d(scan_register *reg,87uint file, uint index1, uint index2)88{89reg->file = file;90reg->dimensions = 2;91reg->indices[0] = index1;92reg->indices[1] = index2;93}9495static void96scan_register_dst(scan_register *reg,97struct tgsi_full_dst_register *dst)98{99if (dst->Register.Dimension) {100/*FIXME: right now we don't support indirect101* multidimensional addressing */102fill_scan_register2d(reg,103dst->Register.File,104dst->Register.Index,105dst->Dimension.Index);106} else {107fill_scan_register1d(reg,108dst->Register.File,109dst->Register.Index);110}111}112113static void114scan_register_src(scan_register *reg,115struct tgsi_full_src_register *src)116{117if (src->Register.Dimension) {118/*FIXME: right now we don't support indirect119* multidimensional addressing */120fill_scan_register2d(reg,121src->Register.File,122src->Register.Index,123src->Dimension.Index);124} else {125fill_scan_register1d(reg,126src->Register.File,127src->Register.Index);128}129}130131static scan_register *132create_scan_register_src(struct tgsi_full_src_register *src)133{134scan_register *reg = MALLOC(sizeof(scan_register));135scan_register_src(reg, src);136137return reg;138}139140static scan_register *141create_scan_register_dst(struct tgsi_full_dst_register *dst)142{143scan_register *reg = MALLOC(sizeof(scan_register));144scan_register_dst(reg, dst);145146return reg;147}148149static void150report_error(151struct sanity_check_ctx *ctx,152const char *format,153... )154{155va_list args;156157if (!ctx->print)158return;159160debug_printf( "Error : " );161va_start( args, format );162_debug_vprintf( format, args );163va_end( args );164debug_printf( "\n" );165ctx->errors++;166}167168static void169report_warning(170struct sanity_check_ctx *ctx,171const char *format,172... )173{174va_list args;175176if (!ctx->print)177return;178179debug_printf( "Warning: " );180va_start( args, format );181_debug_vprintf( format, args );182va_end( args );183debug_printf( "\n" );184ctx->warnings++;185}186187static boolean188check_file_name(189struct sanity_check_ctx *ctx,190uint file )191{192if (file <= TGSI_FILE_NULL || file >= TGSI_FILE_COUNT) {193report_error( ctx, "(%u): Invalid register file name", file );194return FALSE;195}196return TRUE;197}198199static boolean200is_register_declared(201struct sanity_check_ctx *ctx,202const scan_register *reg)203{204void *data = cso_hash_find_data_from_template(205&ctx->regs_decl, scan_register_key(reg),206(void*)reg, sizeof(scan_register));207return data ? TRUE : FALSE;208}209210static boolean211is_any_register_declared(212struct sanity_check_ctx *ctx,213uint file )214{215struct cso_hash_iter iter =216cso_hash_first_node(&ctx->regs_decl);217218while (!cso_hash_iter_is_null(iter)) {219scan_register *reg = (scan_register *)cso_hash_iter_data(iter);220if (reg->file == file)221return TRUE;222iter = cso_hash_iter_next(iter);223}224225return FALSE;226}227228static boolean229is_register_used(230struct sanity_check_ctx *ctx,231scan_register *reg)232{233void *data = cso_hash_find_data_from_template(234&ctx->regs_used, scan_register_key(reg),235reg, sizeof(scan_register));236return data ? TRUE : FALSE;237}238239240static boolean241is_ind_register_used(242struct sanity_check_ctx *ctx,243scan_register *reg)244{245return cso_hash_contains(&ctx->regs_ind_used, reg->file);246}247248static const char *file_names[TGSI_FILE_COUNT] =249{250"NULL",251"CONST",252"IN",253"OUT",254"TEMP",255"SAMP",256"ADDR",257"IMM",258"SV",259"RES"260};261262static boolean263check_register_usage(264struct sanity_check_ctx *ctx,265scan_register *reg,266const char *name,267boolean indirect_access )268{269if (!check_file_name( ctx, reg->file )) {270FREE(reg);271return FALSE;272}273274if (indirect_access) {275/* Note that 'index' is an offset relative to the value of the276* address register. No range checking done here.*/277reg->indices[0] = 0;278reg->indices[1] = 0;279if (!is_any_register_declared( ctx, reg->file ))280report_error( ctx, "%s: Undeclared %s register", file_names[reg->file], name );281if (!is_ind_register_used(ctx, reg))282cso_hash_insert(&ctx->regs_ind_used, reg->file, reg);283else284FREE(reg);285}286else {287if (!is_register_declared( ctx, reg )) {288if (reg->dimensions == 2) {289report_error( ctx, "%s[%d][%d]: Undeclared %s register", file_names[reg->file],290reg->indices[0], reg->indices[1], name );291}292else {293report_error( ctx, "%s[%d]: Undeclared %s register", file_names[reg->file],294reg->indices[0], name );295}296}297if (!is_register_used( ctx, reg ))298cso_hash_insert(&ctx->regs_used, scan_register_key(reg), reg);299else300FREE(reg);301}302return TRUE;303}304305static boolean306iter_instruction(307struct tgsi_iterate_context *iter,308struct tgsi_full_instruction *inst )309{310struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter;311const struct tgsi_opcode_info *info;312uint i;313314if (inst->Instruction.Opcode == TGSI_OPCODE_END) {315if (ctx->index_of_END != ~0u) {316report_error( ctx, "Too many END instructions" );317}318ctx->index_of_END = ctx->num_instructions;319}320321info = tgsi_get_opcode_info( inst->Instruction.Opcode );322if (!info) {323report_error( ctx, "(%u): Invalid instruction opcode", inst->Instruction.Opcode );324return TRUE;325}326327if (info->num_dst != inst->Instruction.NumDstRegs) {328report_error( ctx, "%s: Invalid number of destination operands, should be %u",329tgsi_get_opcode_name(inst->Instruction.Opcode), info->num_dst );330}331if (info->num_src != inst->Instruction.NumSrcRegs) {332report_error( ctx, "%s: Invalid number of source operands, should be %u",333tgsi_get_opcode_name(inst->Instruction.Opcode), info->num_src );334}335336/* Check destination and source registers' validity.337* Mark the registers as used.338*/339for (i = 0; i < inst->Instruction.NumDstRegs; i++) {340scan_register *reg = create_scan_register_dst(&inst->Dst[i]);341check_register_usage(342ctx,343reg,344"destination",345FALSE );346if (!inst->Dst[i].Register.WriteMask) {347report_error(ctx, "Destination register has empty writemask");348}349}350for (i = 0; i < inst->Instruction.NumSrcRegs; i++) {351scan_register *reg = create_scan_register_src(&inst->Src[i]);352check_register_usage(353ctx,354reg,355"source",356(boolean)inst->Src[i].Register.Indirect );357if (inst->Src[i].Register.Indirect) {358scan_register *ind_reg = MALLOC(sizeof(scan_register));359360fill_scan_register1d(ind_reg,361inst->Src[i].Indirect.File,362inst->Src[i].Indirect.Index);363check_register_usage(364ctx,365ind_reg,366"indirect",367FALSE );368}369}370371ctx->num_instructions++;372373return TRUE;374}375376static void377check_and_declare(struct sanity_check_ctx *ctx,378scan_register *reg)379{380if (is_register_declared( ctx, reg))381report_error( ctx, "%s[%u]: The same register declared more than once",382file_names[reg->file], reg->indices[0] );383cso_hash_insert(&ctx->regs_decl,384scan_register_key(reg),385reg);386}387388389static boolean390iter_declaration(391struct tgsi_iterate_context *iter,392struct tgsi_full_declaration *decl )393{394struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter;395uint file;396uint i;397398/* No declarations allowed after the first instruction.399*/400if (ctx->num_instructions > 0)401report_error( ctx, "Instruction expected but declaration found" );402403/* Check registers' validity.404* Mark the registers as declared.405*/406file = decl->Declaration.File;407if (!check_file_name( ctx, file ))408return TRUE;409for (i = decl->Range.First; i <= decl->Range.Last; i++) {410/* declared TGSI_FILE_INPUT's for geometry and tessellation411* have an implied second dimension */412uint processor = ctx->iter.processor.Processor;413uint patch = decl->Semantic.Name == TGSI_SEMANTIC_PATCH ||414decl->Semantic.Name == TGSI_SEMANTIC_TESSOUTER ||415decl->Semantic.Name == TGSI_SEMANTIC_TESSINNER;416if (file == TGSI_FILE_INPUT && !patch && (417processor == PIPE_SHADER_GEOMETRY ||418processor == PIPE_SHADER_TESS_CTRL ||419processor == PIPE_SHADER_TESS_EVAL)) {420uint vert;421for (vert = 0; vert < ctx->implied_array_size; ++vert) {422scan_register *reg = MALLOC(sizeof(scan_register));423fill_scan_register2d(reg, file, i, vert);424check_and_declare(ctx, reg);425}426} else if (file == TGSI_FILE_OUTPUT && !patch &&427processor == PIPE_SHADER_TESS_CTRL) {428uint vert;429for (vert = 0; vert < ctx->implied_out_array_size; ++vert) {430scan_register *reg = MALLOC(sizeof(scan_register));431fill_scan_register2d(reg, file, i, vert);432check_and_declare(ctx, reg);433}434} else {435scan_register *reg = MALLOC(sizeof(scan_register));436if (decl->Declaration.Dimension) {437fill_scan_register2d(reg, file, i, decl->Dim.Index2D);438} else {439fill_scan_register1d(reg, file, i);440}441check_and_declare(ctx, reg);442}443}444445return TRUE;446}447448static boolean449iter_immediate(450struct tgsi_iterate_context *iter,451struct tgsi_full_immediate *imm )452{453struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter;454scan_register *reg;455456/* No immediates allowed after the first instruction.457*/458if (ctx->num_instructions > 0)459report_error( ctx, "Instruction expected but immediate found" );460461/* Mark the register as declared.462*/463reg = MALLOC(sizeof(scan_register));464fill_scan_register1d(reg, TGSI_FILE_IMMEDIATE, ctx->num_imms);465cso_hash_insert(&ctx->regs_decl, scan_register_key(reg), reg);466ctx->num_imms++;467468/* Check data type validity.469*/470if (imm->Immediate.DataType != TGSI_IMM_FLOAT32 &&471imm->Immediate.DataType != TGSI_IMM_UINT32 &&472imm->Immediate.DataType != TGSI_IMM_INT32) {473report_error( ctx, "(%u): Invalid immediate data type", imm->Immediate.DataType );474return TRUE;475}476477return TRUE;478}479480481static boolean482iter_property(483struct tgsi_iterate_context *iter,484struct tgsi_full_property *prop )485{486struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter;487488if (iter->processor.Processor == PIPE_SHADER_GEOMETRY &&489prop->Property.PropertyName == TGSI_PROPERTY_GS_INPUT_PRIM) {490ctx->implied_array_size = u_vertices_per_prim(prop->u[0].Data);491}492if (iter->processor.Processor == PIPE_SHADER_TESS_CTRL &&493prop->Property.PropertyName == TGSI_PROPERTY_TCS_VERTICES_OUT)494ctx->implied_out_array_size = prop->u[0].Data;495return TRUE;496}497498static boolean499prolog(struct tgsi_iterate_context *iter)500{501struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter;502if (iter->processor.Processor == PIPE_SHADER_TESS_CTRL ||503iter->processor.Processor == PIPE_SHADER_TESS_EVAL)504ctx->implied_array_size = 32;505return TRUE;506}507508static boolean509epilog(510struct tgsi_iterate_context *iter )511{512struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter;513514/* There must be an END instruction somewhere.515*/516if (ctx->index_of_END == ~0u) {517report_error( ctx, "Missing END instruction" );518}519520/* Check if all declared registers were used.521*/522{523struct cso_hash_iter iter =524cso_hash_first_node(&ctx->regs_decl);525526while (!cso_hash_iter_is_null(iter)) {527scan_register *reg = (scan_register *)cso_hash_iter_data(iter);528if (!is_register_used(ctx, reg) && !is_ind_register_used(ctx, reg)) {529report_warning( ctx, "%s[%u]: Register never used",530file_names[reg->file], reg->indices[0] );531}532iter = cso_hash_iter_next(iter);533}534}535536/* Print totals, if any.537*/538if (ctx->errors || ctx->warnings)539debug_printf( "%u errors, %u warnings\n", ctx->errors, ctx->warnings );540541return TRUE;542}543544static void545regs_hash_destroy(struct cso_hash *hash)546{547struct cso_hash_iter iter = cso_hash_first_node(hash);548while (!cso_hash_iter_is_null(iter)) {549scan_register *reg = (scan_register *)cso_hash_iter_data(iter);550iter = cso_hash_erase(hash, iter);551assert(reg->file < TGSI_FILE_COUNT);552FREE(reg);553}554cso_hash_deinit(hash);555}556557boolean558tgsi_sanity_check(559const struct tgsi_token *tokens )560{561struct sanity_check_ctx ctx;562boolean retval;563564ctx.iter.prolog = prolog;565ctx.iter.iterate_instruction = iter_instruction;566ctx.iter.iterate_declaration = iter_declaration;567ctx.iter.iterate_immediate = iter_immediate;568ctx.iter.iterate_property = iter_property;569ctx.iter.epilog = epilog;570571cso_hash_init(&ctx.regs_decl);572cso_hash_init(&ctx.regs_used);573cso_hash_init(&ctx.regs_ind_used);574575ctx.num_imms = 0;576ctx.num_instructions = 0;577ctx.index_of_END = ~0;578579ctx.errors = 0;580ctx.warnings = 0;581ctx.implied_array_size = 0;582ctx.print = debug_get_option_print_sanity();583584retval = tgsi_iterate_shader( tokens, &ctx.iter );585regs_hash_destroy(&ctx.regs_decl);586regs_hash_destroy(&ctx.regs_used);587regs_hash_destroy(&ctx.regs_ind_used);588if (retval == FALSE)589return FALSE;590591return ctx.errors == 0;592}593594595