суббота, 8 сентября 2012 г.

AVX structure

It seems that AVX has crazy structure. Obvious first step is order on opcode byte. Then for each opcode we need yet 4 tables for pp. Next for 66 prefix we need yet 3 tables for 0f, 0f38 & 0f3a. And anyway we have ambiguity:
  • for W field: vmovd - 128.W0 vs vmovq - 128.W1
  • for vvvv field: vmovss - NDS.LIG.WIG vs vmovss - LIG.WIG
  • for L field: vzeroall - 256.WIG vs vzeroupper - 128.WIG
Wrote simple perl script for instructions parsing and simple ordering


#!perl -w
# lame script for AVX instruction analysis
# 7 sep 2012 (c) RedPlait

use strict;
use warnings;
use Data::Dumper;
use Getopt::Std;

use vars qw/$opt_s $opt_l $opt_v/;

sub usage()
{
    print STDERR <<EOF;
Usage is $0 [options] avx_opcodes_file
Options: 
  -l -- include all opcodes (not merge 128/256)
  -s -- simple opcodes grouping
  -v -- Verbose mode
EOF
    exit (8);
}

sub parse
{
  my($fname, $href) = @_;
  my($fh, $name, $opcode, $vex, $str, $state, $l, $prev_name, $cnt, $ln);
  # open file
  open($fh, '<', $fname) or die("Cannot open $fname, error $!\n");
  $state = 0;
  $prev_name = '';
  while( $str = <$fh> )
  {
    $ln++;
    chomp $str;
    next if ( $str eq '' );
    if ( $state )
    {
      # read opcode name
      die("Bad opcode line $ln: $str\n") if ( $str !~ /^(\w+) / &&
                                              $str !~ /^(\w+)$/
                                            );
      $name = lc($1);
      if ( defined($opt_l) || $name ne $prev_name )
      {
        if ( exists $href->{$opcode} )
        {
          my $aref = $href->{$opcode};
          push @$aref, [ $name, $vex ];
          $cnt++;
        } else {
          $href->{$opcode} = [ [ $name, $vex ] ];
          $cnt++;
        }
        $prev_name = $name;
      }
    } else {
      # read VEX
      if ( $str =~ /^VEX\.(.*) ([0-9a-f]{2})\s?(.*)$/i )
      {
        $opcode = hex($2);
        $vex = $1 . ' ' . $3;
      } else {
        die("Bad VEX line $ln: $str\n");
      }
    }
    $state ^= 1;
  }
  close $fh;
  return $cnt;
}

# find max length of opcodes 
sub find_max_name_len
{
  my $href = shift;
  my($iter, $aref, $aiter, $res);
  $res = 0;
  foreach $iter ( keys %$href )
  {
    $aref = $href->{$iter};
    foreach $aiter ( @$aref )
    {
      my $len = length($aiter->[0]);
      $res = $len if ( $res < $len );
    }
  }
  return $res;
}

# Args:
#  list
#  margin
#  max_length of opcode name
sub dump_list
{
  my($aref, $margin, $max_len) = @_;
  my($pad, $aiter, $cnt);
  $cnt = 0;
  foreach $aiter ( @$aref )
  {
    $cnt++;
    $pad = ' ' x ( $max_len - length($aiter->[0]));
    printf("%s%s%s: %s\n", ' ' x $margin, $aiter->[0], $pad, $aiter->[1]);
  }
}

# simple dump opcodes in order
sub lame_dump
{
  my $href = shift;
  my($iter, $aref, $aiter, $max_len, $pad);
  # find max instruction opcode
  $max_len = find_max_name_len($href);
  foreach $iter ( sort { $a <=> $b } keys %$href )
  {
    printf("%X:\n", $iter);
    $aref = $href->{$iter};
    if ( defined $opt_s )
    {
      foreach $aiter ( @$aref )
      {
        $pad = ' ' x ( $max_len - length($aiter->[0]));
        printf("%s%s: %s\n", $aiter->[0], $pad, $aiter->[1]);
      }
    } else {
      # check for pp prefix
      #    0f    66    f3    f2
      my(@pp0, $pp1, @pp2, @pp3);
      $pp1 = 0;
      # for 66 we need 3 array
      #  0f    0f38   0f3a
      my(@of, @of38, @of3a);
      foreach $aiter ( @$aref )
      {
        my $what = $aiter->[1];
        if ( $what =~ s/\.F3//i )
        {
          $what =~ s/\.0f//i;
          push @pp2, [ $aiter->[0], $what ];
        } elsif ( $what =~ s/\.F2//i )
        {
          $what =~ s/\.0f//i;
          push @pp3, [ $aiter->[0], $what ];
        } elsif ( $what =~ s/\.66// )
        {
          $pp1++;
          if ( $what =~ s/\.0F3A//i )
          {
            push @of3a, [ $aiter->[0], $what ];
          } elsif ( $what =~ s/\.0F38//i )
          {
            push @of38, [ $aiter->[0], $what ];
          } else {
            $what =~ s/\.0f//i;
            push @of, [ $aiter->[0], $what ];
          }
        } elsif ( $what =~ s/\.0F//i )
        {
          push @pp0, [ $aiter->[0], $what ];
        } else {
          die("Bad instruction %s\n", $aiter->[0]);
        }
      }
      # dump 4 pp array
      printf("  0F:\n"); dump_list(\@pp0, 8, $max_len);
      printf("  66:\n");
      if ( $pp1 )
      {
        printf("    0F:\n");   dump_list(\@of,   12, $max_len);
        printf("    0F38:\n"); dump_list(\@of38, 12, $max_len);
        printf("    0F3A:\n"); dump_list(\@of3a, 12, $max_len);
      }
      printf("  F3:\n"); dump_list(\@pp2, 8, $max_len);
      printf("  F2:\n"); dump_list(\@pp3, 8, $max_len);
    }
  }
}

# main
my $status = getopts("slv");
if ($status == 0)
{
    usage();
}
my %hdb;
my $res = parse($ARGV[0], \%hdb);
if ( defined($opt_v) )
{
  printf("Added %d\n", $res);
  print Dumper(\%hdb);
}
lame_dump(\%hdb);

Комментариев нет:

Отправить комментарий