Home | History | Annotate | Download | only in tgsi
      1 /**************************************************************************
      2  *
      3  * Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas.
      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 TUNGSTEN GRAPHICS 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 
     62    boolean print;
     63 };
     64 
     65 static INLINE unsigned
     66 scan_register_key(const scan_register *reg)
     67 {
     68    unsigned key = reg->file;
     69    key |= (reg->indices[0] << 4);
     70    key |= (reg->indices[1] << 18);
     71 
     72    return key;
     73 }
     74 
     75 static void
     76 fill_scan_register1d(scan_register *reg,
     77                      uint file, uint index)
     78 {
     79    reg->file = file;
     80    reg->dimensions = 1;
     81    reg->indices[0] = index;
     82    reg->indices[1] = 0;
     83 }
     84 
     85 static void
     86 fill_scan_register2d(scan_register *reg,
     87                      uint file, uint index1, uint index2)
     88 {
     89    reg->file = file;
     90    reg->dimensions = 2;
     91    reg->indices[0] = index1;
     92    reg->indices[1] = index2;
     93 }
     94 
     95 static void
     96 scan_register_dst(scan_register *reg,
     97                   struct tgsi_full_dst_register *dst)
     98 {
     99    if (dst->Register.Dimension) {
    100       /*FIXME: right now we don't support indirect
    101        * multidimensional addressing */
    102       fill_scan_register2d(reg,
    103                            dst->Register.File,
    104                            dst->Register.Index,
    105                            dst->Dimension.Index);
    106    } else {
    107       fill_scan_register1d(reg,
    108                            dst->Register.File,
    109                            dst->Register.Index);
    110    }
    111 }
    112 
    113 static void
    114 scan_register_src(scan_register *reg,
    115                   struct tgsi_full_src_register *src)
    116 {
    117    if (src->Register.Dimension) {
    118       /*FIXME: right now we don't support indirect
    119        * multidimensional addressing */
    120       fill_scan_register2d(reg,
    121                            src->Register.File,
    122                            src->Register.Index,
    123                            src->Dimension.Index);
    124    } else {
    125       fill_scan_register1d(reg,
    126                            src->Register.File,
    127                            src->Register.Index);
    128    }
    129 }
    130 
    131 static scan_register *
    132 create_scan_register_src(struct tgsi_full_src_register *src)
    133 {
    134    scan_register *reg = MALLOC(sizeof(scan_register));
    135    scan_register_src(reg, src);
    136 
    137    return reg;
    138 }
    139 
    140 static scan_register *
    141 create_scan_register_dst(struct tgsi_full_dst_register *dst)
    142 {
    143    scan_register *reg = MALLOC(sizeof(scan_register));
    144    scan_register_dst(reg, dst);
    145 
    146    return reg;
    147 }
    148 
    149 static void
    150 report_error(
    151    struct sanity_check_ctx *ctx,
    152    const char *format,
    153    ... )
    154 {
    155    va_list args;
    156 
    157    if (!ctx->print)
    158       return;
    159 
    160    debug_printf( "Error  : " );
    161    va_start( args, format );
    162    _debug_vprintf( format, args );
    163    va_end( args );
    164    debug_printf( "\n" );
    165    ctx->errors++;
    166 }
    167 
    168 static void
    169 report_warning(
    170    struct sanity_check_ctx *ctx,
    171    const char *format,
    172    ... )
    173 {
    174    va_list args;
    175 
    176    if (!ctx->print)
    177       return;
    178 
    179    debug_printf( "Warning: " );
    180    va_start( args, format );
    181    _debug_vprintf( format, args );
    182    va_end( args );
    183    debug_printf( "\n" );
    184    ctx->warnings++;
    185 }
    186 
    187 static boolean
    188 check_file_name(
    189    struct sanity_check_ctx *ctx,
    190    uint file )
    191 {
    192    if (file <= TGSI_FILE_NULL || file >= TGSI_FILE_COUNT) {
    193       report_error( ctx, "(%u): Invalid register file name", file );
    194       return FALSE;
    195    }
    196    return TRUE;
    197 }
    198 
    199 static boolean
    200 is_register_declared(
    201    struct sanity_check_ctx *ctx,
    202    const scan_register *reg)
    203 {
    204    void *data = cso_hash_find_data_from_template(
    205       ctx->regs_decl, scan_register_key(reg),
    206       (void*)reg, sizeof(scan_register));
    207    return  data ? TRUE : FALSE;
    208 }
    209 
    210 static boolean
    211 is_any_register_declared(
    212    struct sanity_check_ctx *ctx,
    213    uint file )
    214 {
    215    struct cso_hash_iter iter =
    216       cso_hash_first_node(ctx->regs_decl);
    217 
    218    while (!cso_hash_iter_is_null(iter)) {
    219       scan_register *reg = (scan_register *)cso_hash_iter_data(iter);
    220       if (reg->file == file)
    221          return TRUE;
    222       iter = cso_hash_iter_next(iter);
    223    }
    224 
    225    return FALSE;
    226 }
    227 
    228 static boolean
    229 is_register_used(
    230    struct sanity_check_ctx *ctx,
    231    scan_register *reg)
    232 {
    233    void *data = cso_hash_find_data_from_template(
    234       ctx->regs_used, scan_register_key(reg),
    235       reg, sizeof(scan_register));
    236    return  data ? TRUE : FALSE;
    237 }
    238 
    239 
    240 static boolean
    241 is_ind_register_used(
    242    struct sanity_check_ctx *ctx,
    243    scan_register *reg)
    244 {
    245    return cso_hash_contains(ctx->regs_ind_used, reg->file);
    246 }
    247 
    248 static 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    "PRED",
    259    "SV",
    260    "IMMX",
    261    "TEMPX",
    262    "RES"
    263 };
    264 
    265 static boolean
    266 check_register_usage(
    267    struct sanity_check_ctx *ctx,
    268    scan_register *reg,
    269    const char *name,
    270    boolean indirect_access )
    271 {
    272    if (!check_file_name( ctx, reg->file )) {
    273       FREE(reg);
    274       return FALSE;
    275    }
    276 
    277    if (indirect_access) {
    278       /* Note that 'index' is an offset relative to the value of the
    279        * address register.  No range checking done here.*/
    280       reg->indices[0] = 0;
    281       reg->indices[1] = 0;
    282       if (!is_any_register_declared( ctx, reg->file ))
    283          report_error( ctx, "%s: Undeclared %s register", file_names[reg->file], name );
    284       if (!is_ind_register_used(ctx, reg))
    285          cso_hash_insert(ctx->regs_ind_used, reg->file, reg);
    286       else
    287          FREE(reg);
    288    }
    289    else {
    290       if (!is_register_declared( ctx, reg )) {
    291          if (reg->dimensions == 2) {
    292             report_error( ctx, "%s[%d][%d]: Undeclared %s register", file_names[reg->file],
    293                           reg->indices[0], reg->indices[1], name );
    294          }
    295          else {
    296             report_error( ctx, "%s[%d]: Undeclared %s register", file_names[reg->file],
    297                           reg->indices[0], name );
    298          }
    299       }
    300       if (!is_register_used( ctx, reg ))
    301          cso_hash_insert(ctx->regs_used, scan_register_key(reg), reg);
    302       else
    303          FREE(reg);
    304    }
    305    return TRUE;
    306 }
    307 
    308 static boolean
    309 iter_instruction(
    310    struct tgsi_iterate_context *iter,
    311    struct tgsi_full_instruction *inst )
    312 {
    313    struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter;
    314    const struct tgsi_opcode_info *info;
    315    uint i;
    316 
    317    if (inst->Instruction.Opcode == TGSI_OPCODE_END) {
    318       if (ctx->index_of_END != ~0) {
    319          report_error( ctx, "Too many END instructions" );
    320       }
    321       ctx->index_of_END = ctx->num_instructions;
    322    }
    323 
    324    info = tgsi_get_opcode_info( inst->Instruction.Opcode );
    325    if (info == NULL) {
    326       report_error( ctx, "(%u): Invalid instruction opcode", inst->Instruction.Opcode );
    327       return TRUE;
    328    }
    329 
    330    if (info->num_dst != inst->Instruction.NumDstRegs) {
    331       report_error( ctx, "%s: Invalid number of destination operands, should be %u", info->mnemonic, info->num_dst );
    332    }
    333    if (info->num_src != inst->Instruction.NumSrcRegs) {
    334       report_error( ctx, "%s: Invalid number of source operands, should be %u", info->mnemonic, info->num_src );
    335    }
    336 
    337    /* Check destination and source registers' validity.
    338     * Mark the registers as used.
    339     */
    340    for (i = 0; i < inst->Instruction.NumDstRegs; i++) {
    341       scan_register *reg = create_scan_register_dst(&inst->Dst[i]);
    342       check_register_usage(
    343          ctx,
    344          reg,
    345          "destination",
    346          FALSE );
    347       if (!inst->Dst[i].Register.WriteMask) {
    348          report_error(ctx, "Destination register has empty writemask");
    349       }
    350    }
    351    for (i = 0; i < inst->Instruction.NumSrcRegs; i++) {
    352       scan_register *reg = create_scan_register_src(&inst->Src[i]);
    353       check_register_usage(
    354          ctx,
    355          reg,
    356          "source",
    357          (boolean)inst->Src[i].Register.Indirect );
    358       if (inst->Src[i].Register.Indirect) {
    359          scan_register *ind_reg = MALLOC(sizeof(scan_register));
    360 
    361          fill_scan_register1d(ind_reg,
    362                               inst->Src[i].Indirect.File,
    363                               inst->Src[i].Indirect.Index);
    364          check_register_usage(
    365             ctx,
    366             ind_reg,
    367             "indirect",
    368             FALSE );
    369       }
    370    }
    371 
    372    ctx->num_instructions++;
    373 
    374    return TRUE;
    375 }
    376 
    377 static void
    378 check_and_declare(struct sanity_check_ctx *ctx,
    379                   scan_register *reg)
    380 {
    381    if (is_register_declared( ctx, reg))
    382       report_error( ctx, "%s[%u]: The same register declared more than once",
    383                     file_names[reg->file], reg->indices[0] );
    384    cso_hash_insert(ctx->regs_decl,
    385                    scan_register_key(reg),
    386                    reg);
    387 }
    388 
    389 
    390 static boolean
    391 iter_declaration(
    392    struct tgsi_iterate_context *iter,
    393    struct tgsi_full_declaration *decl )
    394 {
    395    struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter;
    396    uint file;
    397    uint i;
    398 
    399    /* No declarations allowed after the first instruction.
    400     */
    401    if (ctx->num_instructions > 0)
    402       report_error( ctx, "Instruction expected but declaration found" );
    403 
    404    /* Check registers' validity.
    405     * Mark the registers as declared.
    406     */
    407    file = decl->Declaration.File;
    408    if (!check_file_name( ctx, file ))
    409       return TRUE;
    410    for (i = decl->Range.First; i <= decl->Range.Last; i++) {
    411       /* declared TGSI_FILE_INPUT's for geometry processor
    412        * have an implied second dimension */
    413       if (file == TGSI_FILE_INPUT &&
    414           ctx->iter.processor.Processor == TGSI_PROCESSOR_GEOMETRY) {
    415          uint vert;
    416          for (vert = 0; vert < ctx->implied_array_size; ++vert) {
    417             scan_register *reg = MALLOC(sizeof(scan_register));
    418             fill_scan_register2d(reg, file, i, vert);
    419             check_and_declare(ctx, reg);
    420          }
    421       } else {
    422          scan_register *reg = MALLOC(sizeof(scan_register));
    423          if (decl->Declaration.Dimension) {
    424             fill_scan_register2d(reg, file, i, decl->Dim.Index2D);
    425          } else {
    426             fill_scan_register1d(reg, file, i);
    427          }
    428          check_and_declare(ctx, reg);
    429       }
    430    }
    431 
    432    return TRUE;
    433 }
    434 
    435 static boolean
    436 iter_immediate(
    437    struct tgsi_iterate_context *iter,
    438    struct tgsi_full_immediate *imm )
    439 {
    440    struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter;
    441    scan_register *reg;
    442 
    443    /* No immediates allowed after the first instruction.
    444     */
    445    if (ctx->num_instructions > 0)
    446       report_error( ctx, "Instruction expected but immediate found" );
    447 
    448    /* Mark the register as declared.
    449     */
    450    reg = MALLOC(sizeof(scan_register));
    451    fill_scan_register1d(reg, TGSI_FILE_IMMEDIATE, ctx->num_imms);
    452    cso_hash_insert(ctx->regs_decl, scan_register_key(reg), reg);
    453    ctx->num_imms++;
    454 
    455    /* Check data type validity.
    456     */
    457    if (imm->Immediate.DataType != TGSI_IMM_FLOAT32 &&
    458        imm->Immediate.DataType != TGSI_IMM_UINT32 &&
    459        imm->Immediate.DataType != TGSI_IMM_INT32) {
    460       report_error( ctx, "(%u): Invalid immediate data type", imm->Immediate.DataType );
    461       return TRUE;
    462    }
    463 
    464    return TRUE;
    465 }
    466 
    467 
    468 static boolean
    469 iter_property(
    470    struct tgsi_iterate_context *iter,
    471    struct tgsi_full_property *prop )
    472 {
    473    struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter;
    474 
    475    if (iter->processor.Processor == TGSI_PROCESSOR_GEOMETRY &&
    476        prop->Property.PropertyName == TGSI_PROPERTY_GS_INPUT_PRIM) {
    477       ctx->implied_array_size = u_vertices_per_prim(prop->u[0].Data);
    478    }
    479    return TRUE;
    480 }
    481 
    482 static boolean
    483 epilog(
    484    struct tgsi_iterate_context *iter )
    485 {
    486    struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter;
    487 
    488    /* There must be an END instruction somewhere.
    489     */
    490    if (ctx->index_of_END == ~0) {
    491       report_error( ctx, "Missing END instruction" );
    492    }
    493 
    494    /* Check if all declared registers were used.
    495     */
    496    {
    497       struct cso_hash_iter iter =
    498          cso_hash_first_node(ctx->regs_decl);
    499 
    500       while (!cso_hash_iter_is_null(iter)) {
    501          scan_register *reg = (scan_register *)cso_hash_iter_data(iter);
    502          if (!is_register_used(ctx, reg) && !is_ind_register_used(ctx, reg)) {
    503             report_warning( ctx, "%s[%u]: Register never used",
    504                             file_names[reg->file], reg->indices[0] );
    505          }
    506          iter = cso_hash_iter_next(iter);
    507       }
    508    }
    509 
    510    /* Print totals, if any.
    511     */
    512    if (ctx->errors || ctx->warnings)
    513       debug_printf( "%u errors, %u warnings\n", ctx->errors, ctx->warnings );
    514 
    515    return TRUE;
    516 }
    517 
    518 static void
    519 regs_hash_destroy(struct cso_hash *hash)
    520 {
    521    struct cso_hash_iter iter = cso_hash_first_node(hash);
    522    while (!cso_hash_iter_is_null(iter)) {
    523       scan_register *reg = (scan_register *)cso_hash_iter_data(iter);
    524       iter = cso_hash_erase(hash, iter);
    525       assert(reg->file < TGSI_FILE_COUNT);
    526       FREE(reg);
    527    }
    528    cso_hash_delete(hash);
    529 }
    530 
    531 boolean
    532 tgsi_sanity_check(
    533    const struct tgsi_token *tokens )
    534 {
    535    struct sanity_check_ctx ctx;
    536 
    537    ctx.iter.prolog = NULL;
    538    ctx.iter.iterate_instruction = iter_instruction;
    539    ctx.iter.iterate_declaration = iter_declaration;
    540    ctx.iter.iterate_immediate = iter_immediate;
    541    ctx.iter.iterate_property = iter_property;
    542    ctx.iter.epilog = epilog;
    543 
    544    ctx.regs_decl = cso_hash_create();
    545    ctx.regs_used = cso_hash_create();
    546    ctx.regs_ind_used = cso_hash_create();
    547 
    548    ctx.num_imms = 0;
    549    ctx.num_instructions = 0;
    550    ctx.index_of_END = ~0;
    551 
    552    ctx.errors = 0;
    553    ctx.warnings = 0;
    554    ctx.implied_array_size = 0;
    555    ctx.print = debug_get_option_print_sanity();
    556 
    557    if (!tgsi_iterate_shader( tokens, &ctx.iter ))
    558       return FALSE;
    559 
    560    regs_hash_destroy(ctx.regs_decl);
    561    regs_hash_destroy(ctx.regs_used);
    562    regs_hash_destroy(ctx.regs_ind_used);
    563    return ctx.errors == 0;
    564 }
    565