Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  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 == NULL) {
  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 == TGSI_PROCESSOR_GEOMETRY ||
  418.                 processor == TGSI_PROCESSOR_TESSCTRL ||
  419.                 processor == TGSI_PROCESSOR_TESSEVAL)) {
  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 == TGSI_PROCESSOR_TESSCTRL) {
  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 == TGSI_PROCESSOR_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 == TGSI_PROCESSOR_TESSCTRL &&
  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 == TGSI_PROCESSOR_TESSCTRL ||
  503.        iter->processor.Processor == TGSI_PROCESSOR_TESSEVAL)
  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.  
  563.    ctx.iter.prolog = prolog;
  564.    ctx.iter.iterate_instruction = iter_instruction;
  565.    ctx.iter.iterate_declaration = iter_declaration;
  566.    ctx.iter.iterate_immediate = iter_immediate;
  567.    ctx.iter.iterate_property = iter_property;
  568.    ctx.iter.epilog = epilog;
  569.  
  570.    ctx.regs_decl = cso_hash_create();
  571.    ctx.regs_used = cso_hash_create();
  572.    ctx.regs_ind_used = cso_hash_create();
  573.  
  574.    ctx.num_imms = 0;
  575.    ctx.num_instructions = 0;
  576.    ctx.index_of_END = ~0;
  577.  
  578.    ctx.errors = 0;
  579.    ctx.warnings = 0;
  580.    ctx.implied_array_size = 0;
  581.    ctx.print = debug_get_option_print_sanity();
  582.  
  583.    if (!tgsi_iterate_shader( tokens, &ctx.iter ))
  584.       return FALSE;
  585.  
  586.    regs_hash_destroy(ctx.regs_decl);
  587.    regs_hash_destroy(ctx.regs_used);
  588.    regs_hash_destroy(ctx.regs_ind_used);
  589.    return ctx.errors == 0;
  590. }
  591.