Home | History | Annotate | Download | only in tgsi
      1 /**************************************************************************
      2  *
      3  * Copyright 2008 VMware, Inc.
      4  * All Rights Reserved.
      5  *
      6  * Permission is hereby granted, free of charge, to any person obtaining a
      7  * copy of this software and associated documentation files (the
      8  * "Software"), to deal in the Software without restriction, including
      9  * without limitation the rights to use, copy, modify, merge, publish,
     10  * distribute, sub license, and/or sell copies of the Software, and to
     11  * permit persons to whom the Software is furnished to do so, subject to
     12  * the following conditions:
     13  *
     14  * The above copyright notice and this permission notice (including the
     15  * next paragraph) shall be included in all copies or substantial portions
     16  * of the Software.
     17  *
     18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
     19  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
     21  * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
     22  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
     23  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
     24  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     25  *
     26  **************************************************************************/
     27 
     28 #include "util/u_debug.h"
     29 #include "util/u_memory.h"
     30 #include "util/u_prim.h"
     31 #include "cso_cache/cso_hash.h"
     32 #include "tgsi_sanity.h"
     33 #include "tgsi_info.h"
     34 #include "tgsi_iterate.h"
     35 
     36 
     37 DEBUG_GET_ONCE_BOOL_OPTION(print_sanity, "TGSI_PRINT_SANITY", FALSE)
     38 
     39 
     40 typedef struct {
     41    uint file : 28;
     42    /* max 2 dimensions */
     43    uint dimensions : 4;
     44    uint indices[2];
     45 } scan_register;
     46 
     47 struct sanity_check_ctx
     48 {
     49    struct tgsi_iterate_context iter;
     50    struct cso_hash *regs_decl;
     51    struct cso_hash *regs_used;
     52    struct cso_hash *regs_ind_used;
     53 
     54    uint num_imms;
     55    uint num_instructions;
     56    uint index_of_END;
     57 
     58    uint errors;
     59    uint warnings;
     60    uint implied_array_size;
     61    uint implied_out_array_size;
     62 
     63    boolean print;
     64 };
     65 
     66 static inline unsigned
     67 scan_register_key(const scan_register *reg)
     68 {
     69    unsigned key = reg->file;
     70    key |= (reg->indices[0] << 4);
     71    key |= (reg->indices[1] << 18);
     72 
     73    return key;
     74 }
     75 
     76 static void
     77 fill_scan_register1d(scan_register *reg,
     78                      uint file, uint index)
     79 {
     80    reg->file = file;
     81    reg->dimensions = 1;
     82    reg->indices[0] = index;
     83    reg->indices[1] = 0;
     84 }
     85 
     86 static void
     87 fill_scan_register2d(scan_register *reg,
     88                      uint file, uint index1, uint index2)
     89 {
     90    reg->file = file;
     91    reg->dimensions = 2;
     92    reg->indices[0] = index1;
     93    reg->indices[1] = index2;
     94 }
     95 
     96 static void
     97 scan_register_dst(scan_register *reg,
     98                   struct tgsi_full_dst_register *dst)
     99 {
    100    if (dst->Register.Dimension) {
    101       /*FIXME: right now we don't support indirect
    102        * multidimensional addressing */
    103       fill_scan_register2d(reg,
    104                            dst->Register.File,
    105                            dst->Register.Index,
    106                            dst->Dimension.Index);
    107    } else {
    108       fill_scan_register1d(reg,
    109                            dst->Register.File,
    110                            dst->Register.Index);
    111    }
    112 }
    113 
    114 static void
    115 scan_register_src(scan_register *reg,
    116                   struct tgsi_full_src_register *src)
    117 {
    118    if (src->Register.Dimension) {
    119       /*FIXME: right now we don't support indirect
    120        * multidimensional addressing */
    121       fill_scan_register2d(reg,
    122                            src->Register.File,
    123                            src->Register.Index,
    124                            src->Dimension.Index);
    125    } else {
    126       fill_scan_register1d(reg,
    127                            src->Register.File,
    128                            src->Register.Index);
    129    }
    130 }
    131 
    132 static scan_register *
    133 create_scan_register_src(struct tgsi_full_src_register *src)
    134 {
    135    scan_register *reg = MALLOC(sizeof(scan_register));
    136    scan_register_src(reg, src);
    137 
    138    return reg;
    139 }
    140 
    141 static scan_register *
    142 create_scan_register_dst(struct tgsi_full_dst_register *dst)
    143 {
    144    scan_register *reg = MALLOC(sizeof(scan_register));
    145    scan_register_dst(reg, dst);
    146 
    147    return reg;
    148 }
    149 
    150 static void
    151 report_error(
    152    struct sanity_check_ctx *ctx,
    153    const char *format,
    154    ... )
    155 {
    156    va_list args;
    157 
    158    if (!ctx->print)
    159       return;
    160 
    161    debug_printf( "Error  : " );
    162    va_start( args, format );
    163    _debug_vprintf( format, args );
    164    va_end( args );
    165    debug_printf( "\n" );
    166    ctx->errors++;
    167 }
    168 
    169 static void
    170 report_warning(
    171    struct sanity_check_ctx *ctx,
    172    const char *format,
    173    ... )
    174 {
    175    va_list args;
    176 
    177    if (!ctx->print)
    178       return;
    179 
    180    debug_printf( "Warning: " );
    181    va_start( args, format );
    182    _debug_vprintf( format, args );
    183    va_end( args );
    184    debug_printf( "\n" );
    185    ctx->warnings++;
    186 }
    187 
    188 static boolean
    189 check_file_name(
    190    struct sanity_check_ctx *ctx,
    191    uint file )
    192 {
    193    if (file <= TGSI_FILE_NULL || file >= TGSI_FILE_COUNT) {
    194       report_error( ctx, "(%u): Invalid register file name", file );
    195       return FALSE;
    196    }
    197    return TRUE;
    198 }
    199 
    200 static boolean
    201 is_register_declared(
    202    struct sanity_check_ctx *ctx,
    203    const scan_register *reg)
    204 {
    205    void *data = cso_hash_find_data_from_template(
    206       ctx->regs_decl, scan_register_key(reg),
    207       (void*)reg, sizeof(scan_register));
    208    return  data ? TRUE : FALSE;
    209 }
    210 
    211 static boolean
    212 is_any_register_declared(
    213    struct sanity_check_ctx *ctx,
    214    uint file )
    215 {
    216    struct cso_hash_iter iter =
    217       cso_hash_first_node(ctx->regs_decl);
    218 
    219    while (!cso_hash_iter_is_null(iter)) {
    220       scan_register *reg = (scan_register *)cso_hash_iter_data(iter);
    221       if (reg->file == file)
    222          return TRUE;
    223       iter = cso_hash_iter_next(iter);
    224    }
    225 
    226    return FALSE;
    227 }
    228 
    229 static boolean
    230 is_register_used(
    231    struct sanity_check_ctx *ctx,
    232    scan_register *reg)
    233 {
    234    void *data = cso_hash_find_data_from_template(
    235       ctx->regs_used, scan_register_key(reg),
    236       reg, sizeof(scan_register));
    237    return  data ? TRUE : FALSE;
    238 }
    239 
    240 
    241 static boolean
    242 is_ind_register_used(
    243    struct sanity_check_ctx *ctx,
    244    scan_register *reg)
    245 {
    246    return cso_hash_contains(ctx->regs_ind_used, reg->file);
    247 }
    248 
    249 static const char *file_names[TGSI_FILE_COUNT] =
    250 {
    251    "NULL",
    252    "CONST",
    253    "IN",
    254    "OUT",
    255    "TEMP",
    256    "SAMP",
    257    "ADDR",
    258    "IMM",
    259    "PRED",
    260    "SV",
    261    "RES"
    262 };
    263 
    264 static boolean
    265 check_register_usage(
    266    struct sanity_check_ctx *ctx,
    267    scan_register *reg,
    268    const char *name,
    269    boolean indirect_access )
    270 {
    271    if (!check_file_name( ctx, reg->file )) {
    272       FREE(reg);
    273       return FALSE;
    274    }
    275 
    276    if (indirect_access) {
    277       /* Note that 'index' is an offset relative to the value of the
    278        * address register.  No range checking done here.*/
    279       reg->indices[0] = 0;
    280       reg->indices[1] = 0;
    281       if (!is_any_register_declared( ctx, reg->file ))
    282          report_error( ctx, "%s: Undeclared %s register", file_names[reg->file], name );
    283       if (!is_ind_register_used(ctx, reg))
    284          cso_hash_insert(ctx->regs_ind_used, reg->file, reg);
    285       else
    286          FREE(reg);
    287    }
    288    else {
    289       if (!is_register_declared( ctx, reg )) {
    290          if (reg->dimensions == 2) {
    291             report_error( ctx, "%s[%d][%d]: Undeclared %s register", file_names[reg->file],
    292                           reg->indices[0], reg->indices[1], name );
    293          }
    294          else {
    295             report_error( ctx, "%s[%d]: Undeclared %s register", file_names[reg->file],
    296                           reg->indices[0], name );
    297          }
    298       }
    299       if (!is_register_used( ctx, reg ))
    300          cso_hash_insert(ctx->regs_used, scan_register_key(reg), reg);
    301       else
    302          FREE(reg);
    303    }
    304    return TRUE;
    305 }
    306 
    307 static boolean
    308 iter_instruction(
    309    struct tgsi_iterate_context *iter,
    310    struct tgsi_full_instruction *inst )
    311 {
    312    struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter;
    313    const struct tgsi_opcode_info *info;
    314    uint i;
    315 
    316    if (inst->Instruction.Opcode == TGSI_OPCODE_END) {
    317       if (ctx->index_of_END != ~0) {
    318          report_error( ctx, "Too many END instructions" );
    319       }
    320       ctx->index_of_END = ctx->num_instructions;
    321    }
    322 
    323    info = tgsi_get_opcode_info( inst->Instruction.Opcode );
    324    if (!info) {
    325       report_error( ctx, "(%u): Invalid instruction opcode", inst->Instruction.Opcode );
    326       return TRUE;
    327    }
    328 
    329    if (info->num_dst != inst->Instruction.NumDstRegs) {
    330       report_error( ctx, "%s: Invalid number of destination operands, should be %u", info->mnemonic, info->num_dst );
    331    }
    332    if (info->num_src != inst->Instruction.NumSrcRegs) {
    333       report_error( ctx, "%s: Invalid number of source operands, should be %u", info->mnemonic, info->num_src );
    334    }
    335 
    336    /* Check destination and source registers' validity.
    337     * Mark the registers as used.
    338     */
    339    for (i = 0; i < inst->Instruction.NumDstRegs; i++) {
    340       scan_register *reg = create_scan_register_dst(&inst->Dst[i]);
    341       check_register_usage(
    342          ctx,
    343          reg,
    344          "destination",
    345          FALSE );
    346       if (!inst->Dst[i].Register.WriteMask) {
    347          report_error(ctx, "Destination register has empty writemask");
    348       }
    349    }
    350    for (i = 0; i < inst->Instruction.NumSrcRegs; i++) {
    351       scan_register *reg = create_scan_register_src(&inst->Src[i]);
    352       check_register_usage(
    353          ctx,
    354          reg,
    355          "source",
    356          (boolean)inst->Src[i].Register.Indirect );
    357       if (inst->Src[i].Register.Indirect) {
    358          scan_register *ind_reg = MALLOC(sizeof(scan_register));
    359 
    360          fill_scan_register1d(ind_reg,
    361                               inst->Src[i].Indirect.File,
    362                               inst->Src[i].Indirect.Index);
    363          check_register_usage(
    364             ctx,
    365             ind_reg,
    366             "indirect",
    367             FALSE );
    368       }
    369    }
    370 
    371    ctx->num_instructions++;
    372 
    373    return TRUE;
    374 }
    375 
    376 static void
    377 check_and_declare(struct sanity_check_ctx *ctx,
    378                   scan_register *reg)
    379 {
    380    if (is_register_declared( ctx, reg))
    381       report_error( ctx, "%s[%u]: The same register declared more than once",
    382                     file_names[reg->file], reg->indices[0] );
    383    cso_hash_insert(ctx->regs_decl,
    384                    scan_register_key(reg),
    385                    reg);
    386 }
    387 
    388 
    389 static boolean
    390 iter_declaration(
    391    struct tgsi_iterate_context *iter,
    392    struct tgsi_full_declaration *decl )
    393 {
    394    struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter;
    395    uint file;
    396    uint i;
    397 
    398    /* No declarations allowed after the first instruction.
    399     */
    400    if (ctx->num_instructions > 0)
    401       report_error( ctx, "Instruction expected but declaration found" );
    402 
    403    /* Check registers' validity.
    404     * Mark the registers as declared.
    405     */
    406    file = decl->Declaration.File;
    407    if (!check_file_name( ctx, file ))
    408       return TRUE;
    409    for (i = decl->Range.First; i <= decl->Range.Last; i++) {
    410       /* declared TGSI_FILE_INPUT's for geometry and tessellation
    411        * have an implied second dimension */
    412       uint processor = ctx->iter.processor.Processor;
    413       uint patch = decl->Semantic.Name == TGSI_SEMANTIC_PATCH ||
    414          decl->Semantic.Name == TGSI_SEMANTIC_TESSOUTER ||
    415          decl->Semantic.Name == TGSI_SEMANTIC_TESSINNER;
    416       if (file == TGSI_FILE_INPUT && !patch && (
    417                 processor == PIPE_SHADER_GEOMETRY ||
    418                 processor == PIPE_SHADER_TESS_CTRL ||
    419                 processor == PIPE_SHADER_TESS_EVAL)) {
    420          uint vert;
    421          for (vert = 0; vert < ctx->implied_array_size; ++vert) {
    422             scan_register *reg = MALLOC(sizeof(scan_register));
    423             fill_scan_register2d(reg, file, i, vert);
    424             check_and_declare(ctx, reg);
    425          }
    426       } else if (file == TGSI_FILE_OUTPUT && !patch &&
    427                  processor == PIPE_SHADER_TESS_CTRL) {
    428          uint vert;
    429          for (vert = 0; vert < ctx->implied_out_array_size; ++vert) {
    430             scan_register *reg = MALLOC(sizeof(scan_register));
    431             fill_scan_register2d(reg, file, i, vert);
    432             check_and_declare(ctx, reg);
    433          }
    434       } else {
    435          scan_register *reg = MALLOC(sizeof(scan_register));
    436          if (decl->Declaration.Dimension) {
    437             fill_scan_register2d(reg, file, i, decl->Dim.Index2D);
    438          } else {
    439             fill_scan_register1d(reg, file, i);
    440          }
    441          check_and_declare(ctx, reg);
    442       }
    443    }
    444 
    445    return TRUE;
    446 }
    447 
    448 static boolean
    449 iter_immediate(
    450    struct tgsi_iterate_context *iter,
    451    struct tgsi_full_immediate *imm )
    452 {
    453    struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter;
    454    scan_register *reg;
    455 
    456    /* No immediates allowed after the first instruction.
    457     */
    458    if (ctx->num_instructions > 0)
    459       report_error( ctx, "Instruction expected but immediate found" );
    460 
    461    /* Mark the register as declared.
    462     */
    463    reg = MALLOC(sizeof(scan_register));
    464    fill_scan_register1d(reg, TGSI_FILE_IMMEDIATE, ctx->num_imms);
    465    cso_hash_insert(ctx->regs_decl, scan_register_key(reg), reg);
    466    ctx->num_imms++;
    467 
    468    /* Check data type validity.
    469     */
    470    if (imm->Immediate.DataType != TGSI_IMM_FLOAT32 &&
    471        imm->Immediate.DataType != TGSI_IMM_UINT32 &&
    472        imm->Immediate.DataType != TGSI_IMM_INT32) {
    473       report_error( ctx, "(%u): Invalid immediate data type", imm->Immediate.DataType );
    474       return TRUE;
    475    }
    476 
    477    return TRUE;
    478 }
    479 
    480 
    481 static boolean
    482 iter_property(
    483    struct tgsi_iterate_context *iter,
    484    struct tgsi_full_property *prop )
    485 {
    486    struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter;
    487 
    488    if (iter->processor.Processor == PIPE_SHADER_GEOMETRY &&
    489        prop->Property.PropertyName == TGSI_PROPERTY_GS_INPUT_PRIM) {
    490       ctx->implied_array_size = u_vertices_per_prim(prop->u[0].Data);
    491    }
    492    if (iter->processor.Processor == PIPE_SHADER_TESS_CTRL &&
    493        prop->Property.PropertyName == TGSI_PROPERTY_TCS_VERTICES_OUT)
    494       ctx->implied_out_array_size = prop->u[0].Data;
    495    return TRUE;
    496 }
    497 
    498 static boolean
    499 prolog(struct tgsi_iterate_context *iter)
    500 {
    501    struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter;
    502    if (iter->processor.Processor == PIPE_SHADER_TESS_CTRL ||
    503        iter->processor.Processor == PIPE_SHADER_TESS_EVAL)
    504       ctx->implied_array_size = 32;
    505    return TRUE;
    506 }
    507 
    508 static boolean
    509 epilog(
    510    struct tgsi_iterate_context *iter )
    511 {
    512    struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter;
    513 
    514    /* There must be an END instruction somewhere.
    515     */
    516    if (ctx->index_of_END == ~0) {
    517       report_error( ctx, "Missing END instruction" );
    518    }
    519 
    520    /* Check if all declared registers were used.
    521     */
    522    {
    523       struct cso_hash_iter iter =
    524          cso_hash_first_node(ctx->regs_decl);
    525 
    526       while (!cso_hash_iter_is_null(iter)) {
    527          scan_register *reg = (scan_register *)cso_hash_iter_data(iter);
    528          if (!is_register_used(ctx, reg) && !is_ind_register_used(ctx, reg)) {
    529             report_warning( ctx, "%s[%u]: Register never used",
    530                             file_names[reg->file], reg->indices[0] );
    531          }
    532          iter = cso_hash_iter_next(iter);
    533       }
    534    }
    535 
    536    /* Print totals, if any.
    537     */
    538    if (ctx->errors || ctx->warnings)
    539       debug_printf( "%u errors, %u warnings\n", ctx->errors, ctx->warnings );
    540 
    541    return TRUE;
    542 }
    543 
    544 static void
    545 regs_hash_destroy(struct cso_hash *hash)
    546 {
    547    struct cso_hash_iter iter = cso_hash_first_node(hash);
    548    while (!cso_hash_iter_is_null(iter)) {
    549       scan_register *reg = (scan_register *)cso_hash_iter_data(iter);
    550       iter = cso_hash_erase(hash, iter);
    551       assert(reg->file < TGSI_FILE_COUNT);
    552       FREE(reg);
    553    }
    554    cso_hash_delete(hash);
    555 }
    556 
    557 boolean
    558 tgsi_sanity_check(
    559    const struct tgsi_token *tokens )
    560 {
    561    struct sanity_check_ctx ctx;
    562    boolean retval;
    563 
    564    ctx.iter.prolog = prolog;
    565    ctx.iter.iterate_instruction = iter_instruction;
    566    ctx.iter.iterate_declaration = iter_declaration;
    567    ctx.iter.iterate_immediate = iter_immediate;
    568    ctx.iter.iterate_property = iter_property;
    569    ctx.iter.epilog = epilog;
    570 
    571    ctx.regs_decl = cso_hash_create();
    572    ctx.regs_used = cso_hash_create();
    573    ctx.regs_ind_used = cso_hash_create();
    574 
    575    ctx.num_imms = 0;
    576    ctx.num_instructions = 0;
    577    ctx.index_of_END = ~0;
    578 
    579    ctx.errors = 0;
    580    ctx.warnings = 0;
    581    ctx.implied_array_size = 0;
    582    ctx.print = debug_get_option_print_sanity();
    583 
    584    retval = tgsi_iterate_shader( tokens, &ctx.iter );
    585    regs_hash_destroy(ctx.regs_decl);
    586    regs_hash_destroy(ctx.regs_used);
    587    regs_hash_destroy(ctx.regs_ind_used);
    588    if (retval == FALSE)
    589       return FALSE;
    590 
    591    return ctx.errors == 0;
    592 }
    593