Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | Download | RSS feed

  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.    "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 != ~0) {
  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.       report_error( ctx, "(%u): Invalid immediate data type", imm->Immediate.DataType );
  459.       return TRUE;
  460.    }
  461.  
  462.    return TRUE;
  463. }
  464.  
  465.  
  466. static boolean
  467. iter_property(
  468.    struct tgsi_iterate_context *iter,
  469.    struct tgsi_full_property *prop )
  470. {
  471.    struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter;
  472.  
  473.    if (iter->processor.Processor == TGSI_PROCESSOR_GEOMETRY &&
  474.        prop->Property.PropertyName == TGSI_PROPERTY_GS_INPUT_PRIM) {
  475.       ctx->implied_array_size = u_vertices_per_prim(prop->u[0].Data);
  476.    }
  477.    return TRUE;
  478. }
  479.  
  480. static boolean
  481. epilog(
  482.    struct tgsi_iterate_context *iter )
  483. {
  484.    struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter;
  485.  
  486.    /* There must be an END instruction somewhere.
  487.     */
  488.    if (ctx->index_of_END == ~0) {
  489.       report_error( ctx, "Missing END instruction" );
  490.    }
  491.  
  492.    /* Check if all declared registers were used.
  493.     */
  494.    {
  495.       struct cso_hash_iter iter =
  496.          cso_hash_first_node(ctx->regs_decl);
  497.  
  498.       while (!cso_hash_iter_is_null(iter)) {
  499.          scan_register *reg = (scan_register *)cso_hash_iter_data(iter);
  500.          if (!is_register_used(ctx, reg) && !is_ind_register_used(ctx, reg)) {
  501.             report_warning( ctx, "%s[%u]: Register never used",
  502.                             file_names[reg->file], reg->indices[0] );
  503.          }
  504.          iter = cso_hash_iter_next(iter);
  505.       }
  506.    }
  507.  
  508.    /* Print totals, if any.
  509.     */
  510.    if (ctx->errors || ctx->warnings)
  511.       debug_printf( "%u errors, %u warnings\n", ctx->errors, ctx->warnings );
  512.  
  513.    return TRUE;
  514. }
  515.  
  516. static void
  517. regs_hash_destroy(struct cso_hash *hash)
  518. {
  519.    struct cso_hash_iter iter = cso_hash_first_node(hash);
  520.    while (!cso_hash_iter_is_null(iter)) {
  521.       scan_register *reg = (scan_register *)cso_hash_iter_data(iter);
  522.       iter = cso_hash_erase(hash, iter);
  523.       assert(reg->file < TGSI_FILE_COUNT);
  524.       FREE(reg);
  525.    }
  526.    cso_hash_delete(hash);
  527. }
  528.  
  529. boolean
  530. tgsi_sanity_check(
  531.    const struct tgsi_token *tokens )
  532. {
  533.    struct sanity_check_ctx ctx;
  534.  
  535.    ctx.iter.prolog = NULL;
  536.    ctx.iter.iterate_instruction = iter_instruction;
  537.    ctx.iter.iterate_declaration = iter_declaration;
  538.    ctx.iter.iterate_immediate = iter_immediate;
  539.    ctx.iter.iterate_property = iter_property;
  540.    ctx.iter.epilog = epilog;
  541.  
  542.    ctx.regs_decl = cso_hash_create();
  543.    ctx.regs_used = cso_hash_create();
  544.    ctx.regs_ind_used = cso_hash_create();
  545.  
  546.    ctx.num_imms = 0;
  547.    ctx.num_instructions = 0;
  548.    ctx.index_of_END = ~0;
  549.  
  550.    ctx.errors = 0;
  551.    ctx.warnings = 0;
  552.    ctx.implied_array_size = 0;
  553.    ctx.print = debug_get_option_print_sanity();
  554.  
  555.    if (!tgsi_iterate_shader( tokens, &ctx.iter ))
  556.       return FALSE;
  557.  
  558.    regs_hash_destroy(ctx.regs_decl);
  559.    regs_hash_destroy(ctx.regs_used);
  560.    regs_hash_destroy(ctx.regs_ind_used);
  561.    return ctx.errors == 0;
  562. }
  563.