Merge branch 'master' into asm-shader-rework-1
[mesa.git] / src / gallium / auxiliary / tgsi / tgsi_sanity.c
index 2e3ec96b5b5c91e9727d40d83500902708d589b5..4fe8553c4236fc50bdad4342f6060f8dda069438 100644 (file)
  * 
  **************************************************************************/
 
-#include "pipe/p_debug.h"
+#include "util/u_debug.h"
 #include "tgsi_sanity.h"
+#include "tgsi_info.h"
 #include "tgsi_iterate.h"
 
-#define MAX_REGISTERS 256
-
 typedef uint reg_flag;
 
 #define BITS_IN_REG_FLAG (sizeof( reg_flag ) * 8)
 
+#define MAX_REGISTERS 256
+#define MAX_REG_FLAGS ((MAX_REGISTERS + BITS_IN_REG_FLAG - 1) / BITS_IN_REG_FLAG)
+
 struct sanity_check_ctx
 {
    struct tgsi_iterate_context iter;
 
-   reg_flag regs_decl[TGSI_FILE_COUNT][MAX_REGISTERS / BITS_IN_REG_FLAG];
-   reg_flag regs_used[TGSI_FILE_COUNT][MAX_REGISTERS / BITS_IN_REG_FLAG];
+   reg_flag regs_decl[TGSI_FILE_COUNT][MAX_REG_FLAGS];
+   reg_flag regs_used[TGSI_FILE_COUNT][MAX_REG_FLAGS];
    boolean regs_ind_used[TGSI_FILE_COUNT];
    uint num_imms;
    uint num_instructions;
@@ -88,7 +90,7 @@ check_file_name(
    uint file )
 {
    if (file <= TGSI_FILE_NULL || file >= TGSI_FILE_COUNT) {
-      report_error( ctx, "Invalid register file name" );
+      report_error( ctx, "(%u): Invalid register file name", file );
       return FALSE;
    }
    return TRUE;
@@ -112,7 +114,7 @@ is_any_register_declared(
 {
    uint i;
 
-   for (i = 0; i < MAX_REGISTERS / BITS_IN_REG_FLAG; i++)
+   for (i = 0; i < MAX_REG_FLAGS; i++)
       if (ctx->regs_decl[file][i])
          return TRUE;
    return FALSE;
@@ -129,7 +131,7 @@ is_register_used(
    return (ctx->regs_used[file][index / BITS_IN_REG_FLAG] & (1 << (index % BITS_IN_REG_FLAG))) ? TRUE : FALSE;
 }
 
-static const char *file_names[] =
+static const char *file_names[TGSI_FILE_COUNT] =
 {
    "NULL",
    "CONST",
@@ -138,7 +140,8 @@ static const char *file_names[] =
    "TEMP",
    "SAMP",
    "ADDR",
-   "IMM"
+   "IMM",
+   "LOOP"
 };
 
 static boolean
@@ -151,12 +154,21 @@ check_register_usage(
 {
    if (!check_file_name( ctx, file ))
       return FALSE;
+
    if (indirect_access) {
+      /* Note that 'index' is an offset relative to the value of the
+       * address register.  No range checking done here.
+       */
       if (!is_any_register_declared( ctx, file ))
          report_error( ctx, "%s: Undeclared %s register", file_names[file], name );
       ctx->regs_ind_used[file] = TRUE;
    }
    else {
+      if (index < 0 || index >= MAX_REGISTERS) {
+         report_error( ctx, "%s[%d]: Invalid %s index", file_names[file], index, name );
+         return FALSE;
+      }
+
       if (!is_register_declared( ctx, file, index ))
          report_error( ctx, "%s[%d]: Undeclared %s register", file_names[file], index, name );
       ctx->regs_used[file][index / BITS_IN_REG_FLAG] |= (1 << (index % BITS_IN_REG_FLAG));
@@ -170,17 +182,29 @@ iter_instruction(
    struct tgsi_full_instruction *inst )
 {
    struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter;
+   const struct tgsi_opcode_info *info;
    uint i;
 
-   /* There must be no other instructions after END.
-    */
-   if (ctx->index_of_END != ~0) {
-      report_error( ctx, "Unexpected instruction after END" );
-   }
-   else if (inst->Instruction.Opcode == TGSI_OPCODE_END) {
+   if (inst->Instruction.Opcode == TGSI_OPCODE_END) {
+      if (ctx->index_of_END != ~0) {
+         report_error( ctx, "Too many END instructions" );
+      }
       ctx->index_of_END = ctx->num_instructions;
    }
 
+   info = tgsi_get_opcode_info( inst->Instruction.Opcode );
+   if (info == NULL) {
+      report_error( ctx, "(%u): Invalid instruction opcode", inst->Instruction.Opcode );
+      return TRUE;
+   }
+
+   if (info->num_dst != inst->Instruction.NumDstRegs) {
+      report_error( ctx, "Invalid number of destination operands, should be %u", info->num_dst );
+   }
+   if (info->num_src != inst->Instruction.NumSrcRegs) {
+      report_error( ctx, "Invalid number of source operands, should be %u", info->num_src );
+   }
+
    /* Check destination and source registers' validity.
     * Mark the registers as used.
     */
@@ -211,9 +235,29 @@ iter_instruction(
             index,
             "indirect",
             FALSE );
-         if (file != TGSI_FILE_ADDRESS || index != 0)
-            report_warning( ctx, "Indirect register not ADDR[0]" );
+         if (!(file == TGSI_FILE_ADDRESS || file == TGSI_FILE_LOOP) || index != 0) {
+            report_warning(ctx, "Indirect register neither ADDR[0] nor LOOP[0]");
+         }
+      }
+   }
+
+   switch (inst->Instruction.Opcode) {
+   case TGSI_OPCODE_BGNFOR:
+   case TGSI_OPCODE_ENDFOR:
+      if (inst->FullDstRegisters[0].DstRegister.File != TGSI_FILE_LOOP ||
+          inst->FullDstRegisters[0].DstRegister.Index != 0) {
+         report_error(ctx, "Destination register must be LOOP[0]");
+      }
+      break;
+   }
+
+   switch (inst->Instruction.Opcode) {
+   case TGSI_OPCODE_BGNFOR:
+      if (inst->FullSrcRegisters[0].SrcRegister.File != TGSI_FILE_CONSTANT &&
+          inst->FullSrcRegisters[0].SrcRegister.File != TGSI_FILE_IMMEDIATE) {
+         report_error(ctx, "Source register file must be either CONST or IMM");
       }
+      break;
    }
 
    ctx->num_instructions++;
@@ -243,7 +287,7 @@ iter_declaration(
       return TRUE;
    for (i = decl->DeclarationRange.First; i <= decl->DeclarationRange.Last; i++) {
       if (is_register_declared( ctx, file, i ))
-         report_error( ctx, "The same register declared twice" );
+         report_error( ctx, "%s[%u]: The same register declared more than once", file_names[file], i );
       ctx->regs_decl[file][i / BITS_IN_REG_FLAG] |= (1 << (i % BITS_IN_REG_FLAG));
    }
 
@@ -272,7 +316,7 @@ iter_immediate(
    /* Check data type validity.
     */
    if (imm->Immediate.DataType != TGSI_IMM_FLOAT32) {
-      report_error( ctx, "Invalid immediate data type" );
+      report_error( ctx, "(%u): Invalid immediate data type", imm->Immediate.DataType );
       return TRUE;
    }
 
@@ -286,10 +330,10 @@ epilog(
    struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter;
    uint file;
 
-   /* There must be an END instruction at the end.
+   /* There must be an END instruction somewhere.
     */
-   if (ctx->index_of_END == ~0 || ctx->index_of_END != ctx->num_instructions - 1) {
-      report_error( ctx, "Expected END at end of instruction sequence" );
+   if (ctx->index_of_END == ~0) {
+      report_error( ctx, "Missing END instruction" );
    }
 
    /* Check if all declared registers were used.
@@ -299,7 +343,7 @@ epilog(
 
       for (i = 0; i < MAX_REGISTERS; i++) {
          if (is_register_declared( ctx, file, i ) && !is_register_used( ctx, file, i ) && !ctx->regs_ind_used[file]) {
-            report_warning( ctx, "Register never used" );
+            report_warning( ctx, "%s[%u]: Register never used", file_names[file], i );
          }
       }
    }
@@ -307,7 +351,7 @@ epilog(
    /* Print totals, if any.
     */
    if (ctx->errors || ctx->warnings)
-      debug_printf( "\n%u errors, %u warnings", ctx->errors, ctx->warnings );
+      debug_printf( "%u errors, %u warnings\n", ctx->errors, ctx->warnings );
 
    return TRUE;
 }