Commit message: gallium/tgsi: fix oob access in parse instruction

When parsing texture instruction, it doesn't stop if the
'cur' is ',', the loop variable 'i' will also be increased
and be used to index the 'inst.TexOffsets' array. This can lead
an oob access issue. This patch avoid this.

Signed-off-by: Li Qiang <liq3ea@gmail.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
CVE: CVE-2017-5580
CWE: CWE-119
Vulnerability description: The parse_instruction function in gallium/auxiliary/tgsi/tgsi_text.c in virglrenderer before 0.6.0 allows local guest OS users to cause a denial of service (out-of-bounds array access and process crash) via a crafted texture instruction.

Project: virglrenderer
File: tgsi_text.c

match_inst(const char **pcur,
           unsigned *saturate,
           const struct tgsi_opcode_info *info)
{
   const char *cur = *pcur;

   /* simple case: the whole string matches the instruction name */
   if (str_match_nocase_whole(&cur, info->mnemonic)) {
      *pcur = cur;
      *saturate = 0;
      return TRUE;
   }

   if (str_match_no_case(&cur, info->mnemonic)) {
      /* the instruction has a suffix, figure it out */
      if (str_match_nocase_whole(&cur, "_SAT")) {
         *pcur = cur;
         *saturate = 1;
         return TRUE;
      }
   }

   return FALSE;
}

Project: virglrenderer
File: tgsi_text.c

static boolean parse_declaration( struct translate_ctx *ctx )
{
   struct tgsi_full_declaration decl;
   uint file;
   struct parsed_dcl_bracket brackets[2];
   int num_brackets;
   uint writemask;
   const char *cur, *cur2;
   uint advance;
   boolean is_vs_input;

   if (!eat_white( &ctx->cur )) {
      report_error( ctx, "Syntax error" );
      return FALSE;
   }
   if (!parse_register_dcl( ctx, &file, brackets, &num_brackets))
      return FALSE;
   if (!parse_opt_writemask( ctx, &writemask ))
      return FALSE;

   decl = tgsi_default_full_declaration();
   decl.Declaration.File = file;
   decl.Declaration.UsageMask = writemask;

   if (num_brackets == 1) {
      decl.Range.First = brackets[0].first;
      decl.Range.Last = brackets[0].last;
   } else {
      decl.Range.First = brackets[1].first;
      decl.Range.Last = brackets[1].last;

      decl.Declaration.Dimension = 1;
      decl.Dim.Index2D = brackets[0].first;
   }

   is_vs_input = (file == TGSI_FILE_INPUT &&
                  ctx->processor == TGSI_PROCESSOR_VERTEX);

   cur = ctx->cur;
   eat_opt_white( &cur );
   if (*cur == ',') {
      cur2 = cur;
      cur2++;
      eat_opt_white( &cur2 );
      if (str_match_nocase_whole( &cur2, "ARRAY" )) {
         int arrayid;
         if (*cur2 != '(') {
            report_error( ctx, "Expected `('" );
            return FALSE;
         }
         cur2++;
         eat_opt_white( &cur2 );
         if (!parse_int( &cur2, &arrayid )) {
            report_error( ctx, "Expected `,'" );
            return FALSE;
         }
         eat_opt_white( &cur2 );
         if (*cur2 != ')') {
            report_error( ctx, "Expected `)'" );
            return FALSE;
         }
         cur2++;
         decl.Declaration.Array = 1;
         decl.Array.ArrayID = arrayid;
         ctx->cur = cur = cur2;
      }
   }

   if (*cur == ',' && !is_vs_input) {
      uint i, j;

      cur++;
      eat_opt_white( &cur );
      if (file == TGSI_FILE_RESOURCE) {
         for (i = 0; i < TGSI_TEXTURE_COUNT; i++) {
            if (str_match_nocase_whole(&cur, tgsi_texture_names[i])) {
               decl.Resource.Resource = i;
               break;
            }
         }
         if (i == TGSI_TEXTURE_COUNT) {
            report_error(ctx, "Expected texture target");
            return FALSE;
         }

         cur2 = cur;
         eat_opt_white(&cur2);
         while (*cur2 == ',') {
            cur2++;
            eat_opt_white(&cur2);
            if (str_match_nocase_whole(&cur2, "RAW")) {
               decl.Resource.Raw = 1;

            } else if (str_match_nocase_whole(&cur2, "WR")) {
               decl.Resource.Writable = 1;

            } else {
               break;
            }
            cur = cur2;
            eat_opt_white(&cur2);
         }

         ctx->cur = cur;

      } else if (file == TGSI_FILE_SAMPLER_VIEW) {
         for (i = 0; i < TGSI_TEXTURE_COUNT; i++) {
            if (str_match_nocase_whole(&cur, tgsi_texture_names[i])) {
               decl.SamplerView.Resource = i;
               break;
            }
         }
         if (i == TGSI_TEXTURE_COUNT) {
            report_error(ctx, "Expected texture target");
            return FALSE;
         }
         eat_opt_white( &cur );
         if (*cur != ',') {
            report_error( ctx, "Expected `,'" );
            return FALSE;
         }
         ++cur;
         eat_opt_white( &cur );
         for (j = 0; j < 4; ++j) {
            for (i = 0; i < TGSI_RETURN_TYPE_COUNT; ++i) {
               if (str_match_nocase_whole(&cur, tgsi_return_type_names[i])) {
                  switch (j) {
                  case 0:
                     decl.SamplerView.ReturnTypeX = i;
                     break;
                  case 1:
                     decl.SamplerView.ReturnTypeY = i;
                     break;
                  case 2:
                     decl.SamplerView.ReturnTypeZ = i;
                     break;
                  case 3:
                     decl.SamplerView.ReturnTypeW = i;
                     break;
                  default:
                     assert(0);
                  }
                  break;
               }
            }
            if (i == TGSI_RETURN_TYPE_COUNT) {
               if (j == 0 || j >  2) {
                  report_error(ctx, "Expected type name");
                  return FALSE;
               }
               break;
            } else {
               cur2 = cur;
               eat_opt_white( &cur2 );
               if (*cur2 == ',') {
                  cur2++;
                  eat_opt_white( &cur2 );
                  cur = cur2;
                  continue;
               } else
                  break;
            }
         }
         if (j < 4) {
            decl.SamplerView.ReturnTypeY =
               decl.SamplerView.ReturnTypeZ =
               decl.SamplerView.ReturnTypeW =
               decl.SamplerView.ReturnTypeX;
         }
         ctx->cur = cur;
      } else {
         if (str_match_nocase_whole(&cur, "LOCAL")) {
            decl.Declaration.Local = 1;
            ctx->cur = cur;
         }

         cur = ctx->cur;
         eat_opt_white( &cur );
         if (*cur == ',') {
            cur++;
            eat_opt_white( &cur );

            for (i = 0; i < TGSI_SEMANTIC_COUNT; i++) {
               if (str_match_nocase_whole(&cur, tgsi_semantic_names[i])) {
                  uint index;

                  cur2 = cur;
                  eat_opt_white( &cur2 );
                  if (*cur2 == '[') {
                     cur2++;
                     eat_opt_white( &cur2 );
                     if (!parse_uint( &cur2, &index )) {
                        report_error( ctx, "Expected literal integer" );
                        return FALSE;
                     }
                     eat_opt_white( &cur2 );
                     if (*cur2 != ']') {
                        report_error( ctx, "Expected `]'" );
                        return FALSE;
                     }
                     cur2++;

                     decl.Semantic.Index = index;

                     cur = cur2;
                  }

                  decl.Declaration.Semantic = 1;
                  decl.Semantic.Name = i;

                  ctx->cur = cur;
                  break;
               }
            }
         }
      }
   }

   cur = ctx->cur;
   eat_opt_white( &cur );
   if (*cur == ',' && !is_vs_input) {
      uint i;

      cur++;
      eat_opt_white( &cur );
      for (i = 0; i < TGSI_INTERPOLATE_COUNT; i++) {
         if (str_match_nocase_whole( &cur, tgsi_interpolate_names[i] )) {
            decl.Declaration.Interpolate = 1;
            decl.Interp.Interpolate = i;

            ctx->cur = cur;
            break;
         }
      }
      if (i == TGSI_INTERPOLATE_COUNT) {
         report_error( ctx, "Expected semantic or interpolate attribute" );
         return FALSE;
      }
   }

   cur = ctx->cur;
   eat_opt_white( &cur );
   if (*cur == ',' && !is_vs_input) {
      uint i;

      cur++;
      eat_opt_white( &cur );
      for (i = 0; i < TGSI_INTERPOLATE_LOC_COUNT; i++) {
         if (str_match_nocase_whole( &cur, tgsi_interpolate_locations[i] )) {
            decl.Interp.Location = i;

            ctx->cur = cur;
            break;
         }
      }
   }

   advance = tgsi_build_full_declaration(
      &decl,
      ctx->tokens_cur,
      ctx->header,
      (uint) (ctx->tokens_end - ctx->tokens_cur) );

   if (advance == 0)
      return FALSE;
   ctx->tokens_cur += advance;

   return TRUE;
}

Project: virglrenderer
File: tgsi_text.c

static boolean parse_double( const char **pcur, uint32_t *val0, uint32_t *val1)
{
   const char *cur = *pcur;
   union {
      double dval;
      uint32_t uval[2];
   } v;

   v.dval = strtod(cur, (char**)pcur);
   if (*pcur == cur)
      return FALSE;

   *val0 = v.uval[0];
   *val1 = v.uval[1];

   return TRUE;
}

Project: virglrenderer
File: tgsi_text.c

parse_dst_operand(
   struct translate_ctx *ctx,
   struct tgsi_full_dst_register *dst )
{
   uint file;
   uint writemask;
   const char *cur;
   struct parsed_bracket bracket[2];
   int parsed_opt_brackets;

   if (!parse_register_dst( ctx, &file, &bracket[0] ))
      return FALSE;
   if (!parse_opt_register_src_bracket(ctx, &bracket[1], &parsed_opt_brackets))
      return FALSE;

   cur = ctx->cur;
   eat_opt_white( &cur );

   if (!parse_opt_writemask( ctx, &writemask ))
      return FALSE;

   dst->Register.File = file;
   if (parsed_opt_brackets) {
      dst->Register.Dimension = 1;
      dst->Dimension.Indirect = 0;
      dst->Dimension.Dimension = 0;
      dst->Dimension.Index = bracket[0].index;

      if (bracket[0].ind_file != TGSI_FILE_NULL) {
         dst->Dimension.Indirect = 1;
         dst->DimIndirect.File = bracket[0].ind_file;
         dst->DimIndirect.Index = bracket[0].ind_index;
         dst->DimIndirect.Swizzle = bracket[0].ind_comp;
         dst->DimIndirect.ArrayID = bracket[0].ind_array;
      }
      bracket[0] = bracket[1];
   }
   dst->Register.Index = bracket[0].index;
   dst->Register.WriteMask = writemask;
   if (bracket[0].ind_file != TGSI_FILE_NULL) {
      dst->Register.Indirect = 1;
      dst->Indirect.File = bracket[0].ind_file;
      dst->Indirect.Index = bracket[0].ind_index;
      dst->Indirect.Swizzle = bracket[0].ind_comp;
      dst->Indirect.ArrayID = bracket[0].ind_array;
   }
   return TRUE;
}

Project: virglrenderer
File: tgsi_text.c

parse_file( const char **pcur, uint *file )
{
   uint i;

   for (i = 0; i < TGSI_FILE_COUNT; i++) {
      const char *cur = *pcur;

      if (str_match_nocase_whole( &cur, tgsi_file_name(i) )) {
         *pcur = cur;
         *file = i;
         return TRUE;
      }
   }
   return FALSE;
}

Project: virglrenderer
File: tgsi_text.c

static boolean parse_float( const char **pcur, float *val )
{
   const char *cur = *pcur;
   boolean integral_part = FALSE;
   boolean fractional_part = FALSE;

   if (*cur == '0' && *(cur + 1) == 'x') {
      union fi fi;
      fi.ui = strtoul(cur, NULL, 16);
      *val = fi.f;
      cur += 10;
      goto out;
   }

   *val = (float) atof( cur );
   if (*cur == '-' || *cur == '+')
      cur++;
   if (is_digit( cur )) {
      cur++;
      integral_part = TRUE;
      while (is_digit( cur ))
         cur++;
   }
   if (*cur == '.') {
      cur++;
      if (is_digit( cur )) {
         cur++;
         fractional_part = TRUE;
         while (is_digit( cur ))
            cur++;
      }
   }
   if (!integral_part && !fractional_part)
      return FALSE;
   if (uprcase( *cur ) == 'E') {
      cur++;
      if (*cur == '-' || *cur == '+')
         cur++;
      if (is_digit( cur )) {
         cur++;
         while (is_digit( cur ))
            cur++;
      }
      else
         return FALSE;
   }

out:
   *pcur = cur;
   return TRUE;
}

Project: virglrenderer
File: tgsi_text.c

parse_fs_coord_origin( const char **pcur, uint *fs_coord_origin )
{
   uint i;

   for (i = 0; i < Elements(tgsi_fs_coord_origin_names); i++) {
      const char *cur = *pcur;

      if (str_match_nocase_whole( &cur, tgsi_fs_coord_origin_names[i])) {
         *fs_coord_origin = i;
         *pcur = cur;
         return TRUE;
      }
   }
   return FALSE;
}

Project: virglrenderer
File: tgsi_text.c

parse_fs_coord_pixel_center( const char **pcur, uint *fs_coord_pixel_center )
{
   uint i;

   for (i = 0; i < Elements(tgsi_fs_coord_pixel_center_names); i++) {
      const char *cur = *pcur;

      if (str_match_nocase_whole( &cur, tgsi_fs_coord_pixel_center_names[i])) {
         *fs_coord_pixel_center = i;
         *pcur = cur;
         return TRUE;
      }
   }
   return FALSE;
}

Project: virglrenderer
File: tgsi_text.c

static boolean parse_header( struct translate_ctx *ctx )
{
   uint processor;

   if (str_match_nocase_whole( &ctx->cur, "FRAG" ))
      processor = TGSI_PROCESSOR_FRAGMENT;
   else if (str_match_nocase_whole( &ctx->cur, "VERT" ))
      processor = TGSI_PROCESSOR_VERTEX;
   else if (str_match_nocase_whole( &ctx->cur, "GEOM" ))
      processor = TGSI_PROCESSOR_GEOMETRY;
   else if (str_match_nocase_whole( &ctx->cur, "TESS_CTRL" ))
      processor = TGSI_PROCESSOR_TESS_CTRL;
   else if (str_match_nocase_whole( &ctx->cur, "TESS_EVAL" ))
      processor = TGSI_PROCESSOR_TESS_EVAL;
   else if (str_match_nocase_whole( &ctx->cur, "COMP" ))
      processor = TGSI_PROCESSOR_COMPUTE;
   else {
      report_error( ctx, "Unknown header" );
      return FALSE;
   }

   if (ctx->tokens_cur >= ctx->tokens_end)
      return FALSE;
   ctx->header = (struct tgsi_header *) ctx->tokens_cur++;
   *ctx->header = tgsi_build_header();

   if (ctx->tokens_cur >= ctx->tokens_end)
      return FALSE;
   *(struct tgsi_processor *) ctx->tokens_cur++ = tgsi_build_processor( processor, ctx->header );
   ctx->processor = processor;

   return TRUE;
}

Project: virglrenderer
File: tgsi_text.c

static boolean parse_identifier( const char **pcur, char *ret, size_t len )
{
   const char *cur = *pcur;
   int i = 0;
   if (is_alpha_underscore( cur )) {
      ret[i++] = *cur++;
      while (is_alpha_underscore( cur ) || is_digit( cur )) {
         if (i == len - 1)
            return FALSE;
         ret[i++] = *cur++;
      }
      ret[i++] = '\0';
      *pcur = cur;
      return TRUE;
   }
   return FALSE;
}

Project: virglrenderer
File: tgsi_text.c

static boolean parse_immediate( struct translate_ctx *ctx )
{
   struct tgsi_full_immediate imm;
   uint advance;
   int type;

   if (*ctx->cur == '[') {
      uint uindex;

      ++ctx->cur;

      eat_opt_white( &ctx->cur );
      if (!parse_uint( &ctx->cur, &uindex )) {
         report_error( ctx, "Expected literal unsigned integer" );
         return FALSE;
      }

      if (uindex != ctx->num_immediates) {
         report_error( ctx, "Immediates must be sorted" );
         return FALSE;
      }

      eat_opt_white( &ctx->cur );
      if (*ctx->cur != ']') {
         report_error( ctx, "Expected `]'" );
         return FALSE;
      }

      ctx->cur++;
   }

   if (!eat_white( &ctx->cur )) {
      report_error( ctx, "Syntax error" );
      return FALSE;
   }
   for (type = 0; type < Elements(tgsi_immediate_type_names); ++type) {
      if (str_match_nocase_whole(&ctx->cur, tgsi_immediate_type_names[type]))
         break;
   }
   if (type == Elements(tgsi_immediate_type_names)) {
      report_error( ctx, "Expected immediate type" );
      return FALSE;
   }

   imm = tgsi_default_full_immediate();
   imm.Immediate.NrTokens += 4;
   imm.Immediate.DataType = type;
   parse_immediate_data(ctx, type, imm.u);

   advance = tgsi_build_full_immediate(
      &imm,
      ctx->tokens_cur,
      ctx->header,
      (uint) (ctx->tokens_end - ctx->tokens_cur) );
   if (advance == 0)
      return FALSE;
   ctx->tokens_cur += advance;

   ctx->num_immediates++;

   return TRUE;
}

Project: virglrenderer
File: tgsi_text.c

static boolean parse_immediate_data(struct translate_ctx *ctx, unsigned type,
                                    union tgsi_immediate_data *values)
{
   unsigned i;
   int ret;

   eat_opt_white( &ctx->cur );
   if (*ctx->cur != '{') {
      report_error( ctx, "Expected `{'" );
      return FALSE;
   }
   ctx->cur++;
   for (i = 0; i < 4; i++) {
      eat_opt_white( &ctx->cur );
      if (i > 0) {
         if (*ctx->cur != ',') {
            report_error( ctx, "Expected `,'" );
            return FALSE;
         }
         ctx->cur++;
         eat_opt_white( &ctx->cur );
      }

      switch (type) {
      case TGSI_IMM_FLOAT64:
         ret = parse_double(&ctx->cur, &values[i].Uint, &values[i+1].Uint);
         i++;
         break;
      case TGSI_IMM_FLOAT32:
         ret = parse_float(&ctx->cur, &values[i].Float);
         break;
      case TGSI_IMM_UINT32:
         ret = parse_uint(&ctx->cur, &values[i].Uint);
         break;
      case TGSI_IMM_INT32:
         ret = parse_int(&ctx->cur, &values[i].Int);
         break;
      default:
         assert(0);
         ret = FALSE;
         break;
      }

      if (!ret) {
         report_error( ctx, "Expected immediate constant" );
         return FALSE;
      }
   }
   eat_opt_white( &ctx->cur );
   if (*ctx->cur != '}') {
      report_error( ctx, "Expected `}'" );
      return FALSE;
   }
   ctx->cur++;

   return TRUE;
}

Project: virglrenderer
File: tgsi_text.c

static boolean parse_label( struct translate_ctx *ctx, uint *val )
{
   const char *cur = ctx->cur;

   if (parse_uint( &cur, val )) {
      eat_opt_white( &cur );
      if (*cur == ':') {
         cur++;
         ctx->cur = cur;
         return TRUE;
      }
   }
   return FALSE;
}

Project: virglrenderer
File: tgsi_text.c

parse_opt_register_src_bracket(
   struct translate_ctx *ctx,
   struct parsed_bracket *brackets,
   int *parsed_brackets)
{
   const char *cur = ctx->cur;

   *parsed_brackets = 0;

   eat_opt_white( &cur );
   if (cur[0] == '[') {
      ++cur;
      ctx->cur = cur;

      if (!parse_register_bracket(ctx, brackets))
         return FALSE;

      *parsed_brackets = 1;
   }

   return TRUE;
}

Project: virglrenderer
File: tgsi_text.c

parse_optional_swizzle(
   struct translate_ctx *ctx,
   uint *swizzle,
   boolean *parsed_swizzle,
   int components)
{
   const char *cur = ctx->cur;

   *parsed_swizzle = FALSE;

   eat_opt_white( &cur );
   if (*cur == '.') {
      uint i;

      cur++;
      eat_opt_white( &cur );
      for (i = 0; i < components; i++) {
         if (uprcase( *cur ) == 'X')
            swizzle[i] = TGSI_SWIZZLE_X;
         else if (uprcase( *cur ) == 'Y')
            swizzle[i] = TGSI_SWIZZLE_Y;
         else if (uprcase( *cur ) == 'Z')
            swizzle[i] = TGSI_SWIZZLE_Z;
         else if (uprcase( *cur ) == 'W')
            swizzle[i] = TGSI_SWIZZLE_W;
         else {
	    report_error( ctx, "Expected register swizzle component `x', `y', `z' or `w'" );
	    return FALSE;
         }
         cur++;
      }
      *parsed_swizzle = TRUE;
      ctx->cur = cur;
   }
   return TRUE;
}

Project: virglrenderer
File: tgsi_text.c

parse_primitive( const char **pcur, uint *primitive )
{
   uint i;

   for (i = 0; i < PIPE_PRIM_MAX; i++) {
      const char *cur = *pcur;

      if (str_match_nocase_whole( &cur, tgsi_primitive_names[i])) {
         *primitive = i;
         *pcur = cur;
         return TRUE;
      }
   }
   return FALSE;
}

Project: virglrenderer
File: tgsi_text.c

static boolean parse_property( struct translate_ctx *ctx )
{
   struct tgsi_full_property prop;
   uint property_name;
   uint values[8];
   uint advance;
   char id[64];

   if (!eat_white( &ctx->cur )) {
      report_error( ctx, "Syntax error" );
      return FALSE;
   }
   if (!parse_identifier( &ctx->cur, id, sizeof(id) )) {
      report_error( ctx, "Syntax error" );
      return FALSE;
   }
   for (property_name = 0; property_name < TGSI_PROPERTY_COUNT;
        ++property_name) {
      if (streq_nocase_uprcase(tgsi_property_names[property_name], id)) {
         break;
      }
   }
   if (property_name >= TGSI_PROPERTY_COUNT) {
      eat_until_eol( &ctx->cur );
      report_error(ctx, "\nError: Unknown property : '%s'\n", id);
      return TRUE;
   }

   eat_opt_white( &ctx->cur );
   switch(property_name) {
   case TGSI_PROPERTY_GS_INPUT_PRIM:
   case TGSI_PROPERTY_GS_OUTPUT_PRIM:
      if (!parse_primitive(&ctx->cur, &values[0] )) {
         report_error( ctx, "Unknown primitive name as property!" );
         return FALSE;
      }
      if (property_name == TGSI_PROPERTY_GS_INPUT_PRIM &&
          ctx->processor == TGSI_PROCESSOR_GEOMETRY) {
         ctx->implied_array_size = u_vertices_per_prim(values[0]);
      }
      break;
   case TGSI_PROPERTY_FS_COORD_ORIGIN:
      if (!parse_fs_coord_origin(&ctx->cur, &values[0] )) {
         report_error( ctx, "Unknown coord origin as property: must be UPPER_LEFT or LOWER_LEFT!" );
         return FALSE;
      }
      break;
   case TGSI_PROPERTY_FS_COORD_PIXEL_CENTER:
      if (!parse_fs_coord_pixel_center(&ctx->cur, &values[0] )) {
         report_error( ctx, "Unknown coord pixel center as property: must be HALF_INTEGER or INTEGER!" );
         return FALSE;
      }
      break;
   case TGSI_PROPERTY_FS_COLOR0_WRITES_ALL_CBUFS:
   default:
      if (!parse_uint(&ctx->cur, &values[0] )) {
         report_error( ctx, "Expected unsigned integer as property!" );
         return FALSE;
      }
   }

   prop = tgsi_default_full_property();
   prop.Property.PropertyName = property_name;
   prop.Property.NrTokens += 1;
   prop.u[0].Data = values[0];

   advance = tgsi_build_full_property(
      &prop,
      ctx->tokens_cur,
      ctx->header,
      (uint) (ctx->tokens_end - ctx->tokens_cur) );
   if (advance == 0)
      return FALSE;
   ctx->tokens_cur += advance;

   return TRUE;
}

Project: virglrenderer
File: tgsi_text.c

parse_register_1d(struct translate_ctx *ctx,
                  uint *file,
                  int *index )
{
   if (!parse_register_file_bracket_index( ctx, file, index ))
      return FALSE;
   eat_opt_white( &ctx->cur );
   if (*ctx->cur != ']') {
      report_error( ctx, "Expected `]'" );
      return FALSE;
   }
   ctx->cur++;
   return TRUE;
}

Project: virglrenderer
File: tgsi_text.c

parse_register_dcl(
   struct translate_ctx *ctx,
   uint *file,
   struct parsed_dcl_bracket *brackets,
   int *num_brackets)
{
   const char *cur;

   *num_brackets = 0;

   if (!parse_register_file_bracket( ctx, file ))
      return FALSE;
   if (!parse_register_dcl_bracket( ctx, &brackets[0] ))
      return FALSE;

   *num_brackets = 1;

   cur = ctx->cur;
   eat_opt_white( &cur );

   if (cur[0] == '[') {
      bool is_in = *file == TGSI_FILE_INPUT;
      bool is_out = *file == TGSI_FILE_OUTPUT;

      ++cur;
      ctx->cur = cur;
      if (!parse_register_dcl_bracket( ctx, &brackets[1] ))
         return FALSE;
      /* for geometry shader we don't really care about
       * the first brackets it's always the size of the
       * input primitive. so we want to declare just
       * the index relevant to the semantics which is in
       * the second bracket */

      /* tessellation has similar constraints to geometry shader */
      if ((ctx->processor == TGSI_PROCESSOR_GEOMETRY && is_in) ||
          (ctx->processor == TGSI_PROCESSOR_TESS_EVAL && is_in) ||
          (ctx->processor == TGSI_PROCESSOR_TESS_CTRL && (is_in || is_out))) {
         brackets[0] = brackets[1];
         *num_brackets = 1;
      } else {
         *num_brackets = 2;
      }
   }

   return TRUE;
}

Project: virglrenderer
File: tgsi_text.c

parse_register_dst(
   struct translate_ctx *ctx,
   uint *file,
   struct parsed_bracket *brackets)
{
   brackets->ind_comp = TGSI_SWIZZLE_X;
   if (!parse_register_file_bracket( ctx, file ))
      return FALSE;
   if (!parse_register_bracket( ctx, brackets ))
       return FALSE;

   return TRUE;
}

Project: virglrenderer
File: tgsi_text.c

parse_register_file_bracket(
   struct translate_ctx *ctx,
   uint *file )
{
   if (!parse_file( &ctx->cur, file )) {
      report_error( ctx, "Unknown register file" );
      return FALSE;
   }
   eat_opt_white( &ctx->cur );
   if (*ctx->cur != '[') {
      report_error( ctx, "Expected `['" );
      return FALSE;
   }
   ctx->cur++;
   return TRUE;
}

Project: virglrenderer
File: tgsi_text.c

parse_src_operand(
   struct translate_ctx *ctx,
   struct tgsi_full_src_register *src )
{
   uint file;
   uint swizzle[4];
   boolean parsed_swizzle;
   struct parsed_bracket bracket[2];
   int parsed_opt_brackets;

   if (*ctx->cur == '-') {
      ctx->cur++;
      eat_opt_white( &ctx->cur );
      src->Register.Negate = 1;
   }

   if (*ctx->cur == '|') {
      ctx->cur++;
      eat_opt_white( &ctx->cur );
      src->Register.Absolute = 1;
   }

   if (!parse_register_src(ctx, &file, &bracket[0]))
      return FALSE;
   if (!parse_opt_register_src_bracket(ctx, &bracket[1], &parsed_opt_brackets))
      return FALSE;

   src->Register.File = file;
   if (parsed_opt_brackets) {
      src->Register.Dimension = 1;
      src->Dimension.Indirect = 0;
      src->Dimension.Dimension = 0;
      src->Dimension.Index = bracket[0].index;
      if (bracket[0].ind_file != TGSI_FILE_NULL) {
         src->Dimension.Indirect = 1;
         src->DimIndirect.File = bracket[0].ind_file;
         src->DimIndirect.Index = bracket[0].ind_index;
         src->DimIndirect.Swizzle = bracket[0].ind_comp;
         src->DimIndirect.ArrayID = bracket[0].ind_array;
      }
      bracket[0] = bracket[1];
   }
   src->Register.Index = bracket[0].index;
   if (bracket[0].ind_file != TGSI_FILE_NULL) {
      src->Register.Indirect = 1;
      src->Indirect.File = bracket[0].ind_file;
      src->Indirect.Index = bracket[0].ind_index;
      src->Indirect.Swizzle = bracket[0].ind_comp;
      src->Indirect.ArrayID = bracket[0].ind_array;
   }

   /* Parse optional swizzle.
    */
   if (parse_optional_swizzle( ctx, swizzle, &parsed_swizzle, 4 )) {
      if (parsed_swizzle) {
         src->Register.SwizzleX = swizzle[0];
         src->Register.SwizzleY = swizzle[1];
         src->Register.SwizzleZ = swizzle[2];
         src->Register.SwizzleW = swizzle[3];
      }
   }

   if (src->Register.Absolute) {
      eat_opt_white( &ctx->cur );
      if (*ctx->cur != '|') {
         report_error( ctx, "Expected `|'" );
         return FALSE;
      }
      ctx->cur++;
   }


   return TRUE;
}

Project: virglrenderer
File: tgsi_text.c

parse_texoffset_operand(
   struct translate_ctx *ctx,
   struct tgsi_texture_offset *src )
{
   uint file;
   uint swizzle[3];
   boolean parsed_swizzle;
   struct parsed_bracket bracket;

   if (!parse_register_src(ctx, &file, &bracket))
      return FALSE;

   src->File = file;
   src->Index = bracket.index;

   /* Parse optional swizzle.
    */
   if (parse_optional_swizzle( ctx, swizzle, &parsed_swizzle, 3 )) {
      if (parsed_swizzle) {
         src->SwizzleX = swizzle[0];
         src->SwizzleY = swizzle[1];
         src->SwizzleZ = swizzle[2];
      }
   }

   return TRUE;
}

Project: virglrenderer
File: tgsi_text.c

static void report_error(struct translate_ctx *ctx, const char *format, ...)
{
   va_list args;
   int line = 1;
   int column = 1;
   const char *itr = ctx->text;

   debug_printf("\nTGSI asm error: ");

   va_start(args, format);
   _debug_vprintf(format, args);
   va_end(args);

   while (itr != ctx->cur) {
      if (*itr == '\n') {
         column = 1;
         ++line;
      }
      ++column;
      ++itr;
   }

   debug_printf(" [%d : %d] \n", line, column);
}

Project: virglrenderer
File: tgsi_text.c

tgsi_text_translate(
   const char *text,
   struct tgsi_token *tokens,
   uint num_tokens )
{
   struct translate_ctx ctx = {0};

   ctx.text = text;
   ctx.cur = text;
   ctx.tokens = tokens;
   ctx.tokens_cur = tokens;
   ctx.tokens_end = tokens + num_tokens;

   if (!translate( &ctx ))
      return FALSE;

   return tgsi_sanity_check( tokens );
}

Project: virglrenderer
File: tgsi_text.c

static boolean translate( struct translate_ctx *ctx )
{
   eat_opt_white( &ctx->cur );
   if (!parse_header( ctx ))
      return FALSE;

   if (ctx->processor == TGSI_PROCESSOR_TESS_CTRL ||
       ctx->processor == TGSI_PROCESSOR_TESS_EVAL)
       ctx->implied_array_size = 32;

   while (*ctx->cur != '\0') {
      uint label_val = 0;
      if (!eat_white( &ctx->cur )) {
         report_error( ctx, "Syntax error" );
         return FALSE;
      }

      if (*ctx->cur == '\0')
         break;
      if (parse_label( ctx, &label_val )) {
         if (!parse_instruction( ctx, TRUE ))
            return FALSE;
      }
      else if (str_match_nocase_whole( &ctx->cur, "DCL" )) {
         if (!parse_declaration( ctx ))
            return FALSE;
      }
      else if (str_match_nocase_whole( &ctx->cur, "IMM" )) {
         if (!parse_immediate( ctx ))
            return FALSE;
      }
      else if (str_match_nocase_whole( &ctx->cur, "PROPERTY" )) {
         if (!parse_property( ctx ))
            return FALSE;
      }
      else if (!parse_instruction( ctx, FALSE )) {
         return FALSE;
      }
   }

   return TRUE;
}