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 
     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    "RES"
    261 };
    262 
    263 static boolean
    264 check_register_usage(
    265    struct sanity_check_ctx *ctx,
    266    scan_register *reg,
    267    const char *name,
    268    boolean indirect_access )
    269 {
    270    if (!check_file_name( ctx, reg->file )) {
    271       FREE(reg);
    272       return FALSE;
    273    }
    274 
    275    if (indirect_access) {
    276       /* Note that 'index' is an offset relative to the value of the
    277        * address register.  No range checking done here.*/
    278       reg->indices[0] = 0;
    279       reg->indices[1] = 0;
    280       if (!is_any_register_declared( ctx, reg->file ))
    281          report_error( ctx, "%s: Undeclared %s register", file_names[reg->file], name );
    282       if (!is_ind_register_used(ctx, reg))
    283          cso_hash_insert(ctx->regs_ind_used, reg->file, reg);
    284       else
    285          FREE(reg);
    286    }
    287    else {
    288       if (!is_register_declared( ctx, reg )) {
    289          if (reg->dimensions == 2) {
    290             report_error( ctx, "%s[%d][%d]: Undeclared %s register", file_names[reg->file],
    291                           reg->indices[0], reg->indices[1], name );
    292          }
    293          else {
    294             report_error( ctx, "%s[%d]: Undeclared %s register", file_names[reg->file],
    295                           reg->indices[0], name );
    296          }
    297       }
    298       if (!is_register_used( ctx, reg ))
    299          cso_hash_insert(ctx->regs_used, scan_register_key(reg), reg);
    300       else
    301          FREE(reg);
    302    }
    303    return TRUE;
    304 }
    305 
    306 static boolean
    307 iter_instruction(
    308    struct tgsi_iterate_context *iter,
    309    struct tgsi_full_instruction *inst )
    310 {
    311    struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter;
    312    const struct tgsi_opcode_info *info;
    313    uint i;
    314 
    315    if (inst->Instruction.Opcode == TGSI_OPCODE_END) {
    316       if (ctx->index_of_END != ~0u) {
    317          report_error( ctx, "Too many END instructions" );
    318       }
    319       ctx->index_of_END = ctx->num_instructions;
    320    }
    321 
    322    info = tgsi_get_opcode_info( inst->Instruction.Opcode );
    323    if (info == NULL) {
    324       report_error( ctx, "(%u): Invalid instruction opcode", inst->Instruction.Opcode );
    325       return TRUE;
    326    }
    327 
    328    if (info->num_dst != inst->Instruction.NumDstRegs) {
    329       report_error( ctx, "%s: Invalid number of destination operands, should be %u", info->mnemonic, info->num_dst );
    330    }
    331    if (info->num_src != inst->Instruction.NumSrcRegs) {
    332       report_error( ctx, "%s: Invalid number of source operands, should be %u", info->mnemonic, info->num_src );
    333    }
    334 
    335    /* Check destination and source registers' validity.
    336     * Mark the registers as used.
    337     */
    338    for (i = 0; i < inst->Instruction.NumDstRegs; i++) {
    339       scan_register *reg = create_scan_register_dst(&inst->Dst[i]);
    340       check_register_usage(
    341          ctx,
    342          reg,
    343          "destination",
    344          FALSE );
    345       if (!inst->Dst[i].Register.WriteMask) {
    346          report_error(ctx, "Destination register has empty writemask");
    347       }
    348    }
    349    for (i = 0; i < inst->Instruction.NumSrcRegs; i++) {
    350       scan_register *reg = create_scan_register_src(&inst->Src[i]);
    351       check_register_usage(
    352          ctx,
    353          reg,
    354          "source",
    355          (boolean)inst->Src[i].Register.Indirect );
    356       if (inst->Src[i].Register.Indirect) {
    357          scan_register *ind_reg = MALLOC(sizeof(scan_register));
    358 
    359          fill_scan_register1d(ind_reg,
    360                               inst->Src[i].Indirect.File,
    361                               inst->Src[i].Indirect.Index);
    362          check_register_usage(
    363             ctx,
    364             ind_reg,
    365             "indirect",
    366             FALSE );
    367       }
    368    }
    369 
    370    ctx->num_instructions++;
    371 
    372    return TRUE;
    373 }
    374 
    375 static void
    376 check_and_declare(struct sanity_check_ctx *ctx,
    377                   scan_register *reg)
    378 {
    379    if (is_register_declared( ctx, reg))
    380       report_error( ctx, "%s[%u]: The same register declared more than once",
    381                     file_names[reg->file], reg->indices[0] );
    382    cso_hash_insert(ctx->regs_decl,
    383                    scan_register_key(reg),
    384                    reg);
    385 }
    386 
    387 
    388 static boolean
    389 iter_declaration(
    390    struct tgsi_iterate_context *iter,
    391    struct tgsi_full_declaration *decl )
    392 {
    393    struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter;
    394    uint file;
    395    uint i;
    396 
    397    /* No declarations allowed after the first instruction.
    398     */
    399    if (ctx->num_instructions > 0)
    400       report_error( ctx, "Instruction expected but declaration found" );
    401 
    402    /* Check registers' validity.
    403     * Mark the registers as declared.
    404     */
    405    file = decl->Declaration.File;
    406    if (!check_file_name( ctx, file ))
    407       return TRUE;
    408    for (i = decl->Range.First; i <= decl->Range.Last; i++) {
    409       /* declared TGSI_FILE_INPUT's for geometry processor
    410        * have an implied second dimension */
    411       if (file == TGSI_FILE_INPUT &&
    412           ctx->iter.processor.Processor == TGSI_PROCESSOR_GEOMETRY) {
    413          uint vert;
    414          for (vert = 0; vert < ctx->implied_array_size; ++vert) {
    415             scan_register *reg = MALLOC(sizeof(scan_register));
    416             fill_scan_register2d(reg, file, i, vert);
    417             check_and_declare(ctx, reg);
    418          }
    419       } else {
    420          scan_register *reg = MALLOC(sizeof(scan_register));
    421          if (decl->Declaration.Dimension) {
    422             fill_scan_register2d(reg, file, i, decl->Dim.Index2D);
    423          } else {
    424             fill_scan_register1d(reg, file, i);
    425          }
    426          check_and_declare(ctx, reg);
    427       }
    428    }
    429 
    430    return TRUE;
    431 }
    432 
    433 static boolean
    434 iter_immediate(
    435    struct tgsi_iterate_context *iter,
    436    struct tgsi_full_immediate *imm )
    437 {
    438    struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter;
    439    scan_register *reg;
    440 
    441    /* No immediates allowed after the first instruction.
    442     */
    443    if (ctx->num_instructions > 0)
    444       report_error( ctx, "Instruction expected but immediate found" );
    445 
    446    /* Mark the register as declared.
    447     */
    448    reg = MALLOC(sizeof(scan_register));
    449    fill_scan_register1d(reg, TGSI_FILE_IMMEDIATE, ctx->num_imms);
    450    cso_hash_insert(ctx->regs_decl, scan_register_key(reg), reg);
    451    ctx->num_imms++;
    452 
    453    /* Check data type validity.
    454     */
    455    if (imm->Immediate.DataType != TGSI_IMM_FLOAT32 &&
    456        imm->Immediate.DataType != TGSI_IMM_UINT32 &&
    457        imm->Immediate.DataType != TGSI_IMM_INT32 &&
    458        imm->Immediate.DataType != TGSI_IMM_FLOAT64) {
    459       report_error( ctx, "(%u): Invalid immediate data type", imm->Immediate.DataType );
    460       return TRUE;
    461    }
    462 
    463    return TRUE;
    464 }
    465 
    466 
    467 static boolean
    468 iter_property(
    469    struct tgsi_iterate_context *iter,
    470    struct tgsi_full_property *prop )
    471 {
    472    struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter;
    473 
    474    if (iter->processor.Processor == TGSI_PROCESSOR_GEOMETRY &&
    475        prop->Property.PropertyName == TGSI_PROPERTY_GS_INPUT_PRIM) {
    476       ctx->implied_array_size = u_vertices_per_prim(prop->u[0].Data);
    477    }
    478    return TRUE;
    479 }
    480 
    481 static boolean
    482 epilog(
    483    struct tgsi_iterate_context *iter )
    484 {
    485    struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter;
    486 
    487    /* There must be an END instruction somewhere.
    488     */
    489    if (ctx->index_of_END == ~0u) {
    490       report_error( ctx, "Missing END instruction" );
    491    }
    492 
    493    /* Check if all declared registers were used.
    494     */
    495    {
    496       struct cso_hash_iter iter =
    497          cso_hash_first_node(ctx->regs_decl);
    498 
    499       while (!cso_hash_iter_is_null(iter)) {
    500          scan_register *reg = (scan_register *)cso_hash_iter_data(iter);
    501          if (!is_register_used(ctx, reg) && !is_ind_register_used(ctx, reg)) {
    502             report_warning( ctx, "%s[%u]: Register never used",
    503                             file_names[reg->file], reg->indices[0] );
    504          }
    505          iter = cso_hash_iter_next(iter);
    506       }
    507    }
    508 
    509    /* Print totals, if any.
    510     */
    511    if (ctx->errors || ctx->warnings)
    512       debug_printf( "%u errors, %u warnings\n", ctx->errors, ctx->warnings );
    513 
    514    return TRUE;
    515 }
    516 
    517 static void
    518 regs_hash_destroy(struct cso_hash *hash)
    519 {
    520    struct cso_hash_iter iter = cso_hash_first_node(hash);
    521    while (!cso_hash_iter_is_null(iter)) {
    522       scan_register *reg = (scan_register *)cso_hash_iter_data(iter);
    523       iter = cso_hash_erase(hash, iter);
    524       assert(reg->file < TGSI_FILE_COUNT);
    525       FREE(reg);
    526    }
    527    cso_hash_delete(hash);
    528 }
    529 
    530 boolean
    531 tgsi_sanity_check(
    532    const struct tgsi_token *tokens )
    533 {
    534    struct sanity_check_ctx ctx;
    535    boolean retval;
    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    retval = tgsi_iterate_shader( tokens, &ctx.iter );
    558    regs_hash_destroy(ctx.regs_decl);
    559    regs_hash_destroy(ctx.regs_used);
    560    regs_hash_destroy(ctx.regs_ind_used);
    561    if (retval == FALSE)
    562       return FALSE;
    563 
    564    return ctx.errors == 0;
    565 }
    566