package MaxAs::MaxAsGrammar;

use strict;
use Carp;
use Exporter;
use Data::Dumper;
our @ISA = qw(Exporter);

our @EXPORT = qw(
    %grammar %flags
    parseInstruct genCode genReuseCode
    processAsmLine processSassLine processSassCtrlLine
    replaceXMADs printCtrl readCtrl getRegNum getVecRegisters getAddrVecRegisters
);

require 5.10.0;

# Helper functions for operands
sub getI
{
    my ($orig, $pos, $mask) = @_;
    my $val = $orig;
    my $neg = $val =~ s|^\-||;

    # parse out our custom index immediates for addresses
    if ($val  =~ m'^(\d+)[xX]<([^>]+)>')
    {
        # allow any perl expression and multiply result by leading decimal.
        # also allow global scalar varibles in the expression.
        my $mul = $1;
        my $exp = $2;
        # strip leading zeros (don't interpret numbers as octal)
        $exp =~ s/(?<!\d)0+(?=[1-9])//g;
        my @globals = $exp =~ m'\$\w+'g;
        my $our = @globals ? ' our (' . join(',',@globals) . ');' : '';
        $val = $mul * eval "package MaxAs::MaxAs::CODE;$our $exp";
        #print "$val = $mul x $exp\n"; # if $our;
    }
    # hexidecial value
    elsif ($val  =~ m'^0x[0-9a-zA-Z]+')
    {
        $val = hex($val);
    }
    # otherwise val is a simple decimal value that doesn't need to be modified

    if ( $neg )
    {
        # if the mask removes the sign bit the "neg" flag adds it back on the code somewhere else
        $val = -$val;
        $val &= $mask;
    }
    if (($val & $mask) != $val)
    {
        die sprintf "Immediate value out of range(0x%x): 0x%x ($orig)\n", $mask, $val;
    }
    return $val << $pos;
}
sub getF
{
    my ($val, $pos, $type, $trunc) = @_;
    # hexidecial value
    if ($val  =~ m'^0x[0-9a-zA-Z]+')
    {
        $val = hex($val);
    }
    # support infinity
    elsif ($val =~ m'INF'i)
    {
        $val = $trunc ? ($type eq 'f' ? 0x7f800 : 0x7ff00) : 0x7f800000;
    }
    else
    {
        $val = unpack(($type eq 'f' ? 'L' : 'Q'), pack $type, $val);

        # strip off sign bit if truncating.  It will added elsewhere in the code by the flag capture.
        $val = ($val >> $trunc) & 0x7ffff if $trunc;
    }
    return $val << $pos;
}
sub getR
{
    my ($val, $pos) = @_;
    if ($val =~ m'^R(\d+|Z)$' && $1 < 255)
    {
        $val = $1 eq 'Z' ? 0xff : $1;
    }
    else
    {
        die "Bad register name found: $val\n";
    }
    return $val << $pos;
}
sub getP
{
    my ($val, $pos) = @_;
    if ($val =~ m'^P(\d|T)$' && $1 < 7)
    {
        $val = $1 eq 'T' ? 7 : $1;
    }
    else
    {
        die "Bad predicate name found: $val\n";
    }
    return $val << $pos;
}
sub getC { ((hex($_[0]) >> 2) & 0x7fff) << 20 }

# Map operands into their value and position in the op code.
my %operands =
(
    p0      => sub { getP($_[0], 0)  },
    p3      => sub { getP($_[0], 3)  },
    p12     => sub { getP($_[0], 12) },
    p29     => sub { getP($_[0], 29) },
    p39     => sub { getP($_[0], 39) },
    p45     => sub { getP($_[0], 45) },
    p48     => sub { getP($_[0], 48) },
    p58     => sub { getP($_[0], 58) },
    r0      => sub { getR($_[0], 0)  },
    r8      => sub { getR($_[0], 8)  },
    r20     => sub { getR($_[0], 20) },
    r28     => sub { getR($_[0], 28) },
    r39s20  => sub { getR($_[0], 39) },
    r39     => sub { getR($_[0], 39) },
    r39a    => sub { getR($_[0], 39) }, # does not modify op code, xor the r39 value again to whipe it out, register must be in sequence with r20
    c20     => sub { getC($_[0])     },
    c39     => sub { getC($_[0])     },
    c34     => sub { hex($_[0]) << 34 },
    c36     => sub { hex($_[0]) << 36 },
    f20w32  => sub { getF($_[0], 20, 'f')        },
    f20     => sub { getF($_[0], 20, 'f', 12)    },
    d20     => sub { getF($_[0], 20, 'd', 44)    },
    i8w4    => sub { getI($_[0], 8,  0xf)        },
    i20     => sub { getI($_[0], 20, 0x7ffff)    },
    i20w6   => sub { getI($_[0], 20, 0x3f)       },
    i20w7   => sub { getI($_[0], 20, 0x7f)       },
    i20w8   => sub { getI($_[0], 20, 0xff)       },
    i20w12  => sub { getI($_[0], 20, 0xfff)      },
    i20w24  => sub { getI($_[0], 20, 0xffffff)   },
    i20w32  => sub { getI($_[0], 20, 0xffffffff) },
    i31w4   => sub { getI($_[0], 31, 0xf)        },
    i34w13  => sub { getI($_[0], 34, 0x1fff)     },
    i36w20  => sub { getI($_[0], 36, 0xfffff)    },
    i39w8   => sub { getI($_[0], 39, 0xff)       },
    i28w8   => sub { getI($_[0], 28, 0xff)       },
    i28w20  => sub { getI($_[0], 28, 0xfffff)    },
    i48w8   => sub { getI($_[0], 48, 0xff)       },
    i51w5   => sub { getI($_[0], 51, 0x1f)       },
    i53w5   => sub { getI($_[0], 53, 0x1f)       },
);

# Rules for operands and their closely tied flags
my $hex     = qr"0[xX][0-9a-fA-F]+";
my $iAddr   = qr"\d+[xX]<[^>]+>";
my $immed   = qr"$hex|$iAddr|\d+"o;
my $reg     = qr"[a-zA-Z_]\w*"; # must start with letter or underscore\
my $p       = qr"P[0-6T]";
my $noPred  = qr"(?<noPred>)";
my $pred    = qr"\@(?<predNot>\!)?P(?<predNum>[0-6]) ";
my $p0      = qr"(?<p0>$p)"o;
my $p3      = qr"(?<p3>$p)"o;
my $p12     = qr"(?<p12not>\!)?(?<p12>$p)"o;
my $p29     = qr"(?<p29not>\!)?(?<p29>$p)"o;
my $p39     = qr"(?<p39not>\!)?(?<p39>$p)"o;
my $p45     = qr"(?<p45>$p)"o;
my $p48     = qr"(?<p48>$p)"o;
my $p58     = qr"(?<p58>$p)"o;
my $r0      = qr"(?<r0>$reg)";
my $r0cc    = qr"(?<r0>$reg)(?<CC>\.CC)?";
my $r8      = qr"(?<r8neg>\-)?(?<r8abs>\|)?(?<r8>$reg)\|?(?:\.(?<r8part>H0|H1|B0|B1|B2|B3|H0_H0|H1_H1|F32))?(?<reuse1>\.reuse)?";
my $r20     = qr"(?<r20neg>\-)?(?<r20abs>\|)?(?<r20>$reg)\|?(?:\.(?<r20part>H0|H1|B0|B1|B2|B3|H0_H0|H1_H1))?(?<reuse2>\.reuse)?";
my $r28     = qr"(?<r28>$reg)";
my $r39s20  = qr"(?<r20neg>\-)?(?<r20abs>\|)?(?<r39s20>(?<r20>$reg))\|?(?:\.(?<r39part>H0|H1))?(?<reuse2>\.reuse)?";
my $r39     = qr"(?<r39neg>\-)?(?<r39>$reg)(?:\.(?<r39part>H0|H1|H0_H0|H1_H1|F32))?(?<reuse3>\.reuse)?";
my $r39a    = qr"(?<r39a>(?<r39>$reg))(?<reuse3>\.reuse)?";
my $c20     = qr"(?<r20neg>\-)?(?<r20abs>\|)?c\[(?<c34>$hex)\]\s*\[(?<c20>$hex)\]\|?(?:\.(?<r20part>H0|H1|B0|B1|B2|B3))?"o;
my $c20x    = qr"(?<r20neg>\-)?(?<r20abs>\|)?c\[(?<c34>$hex)\]\s*\[(?<c20>$hex)\]\|?(?:\.(?<r20partx>H0|H1|B0|B1|B2|B3))?"o;
my $c20s39  = qr"(?<r39neg>\-)?c\[(?<c34>$hex)\]\s*\[(?<c39>$hex)\]"o;
my $f20w32  = qr"(?<f20w32>(?:\-|\+|)(?i:$hex|inf\s*|\d+(?:\.\d+(?:e[\+\-]\d+)?)?))";
my $f20     = qr"(?<f20>(?:(?<neg>\-)|\+|)(?i:inf\s*|\d+(?:\.\d+(?:e[\+\-]\d+)?)?))(?<r20neg>\.NEG)?"o;
my $d20     = qr"(?<d20>(?:(?<neg>\-)|\+|)(?i:inf\s*|\d+(?:\.\d+(?:e[\+\-]\d+)?)?))(?<r20neg>\.NEG)?"o;
my $i8w4    = qr"(?<i8w4>$immed)"o;
my $i20     = qr"(?<i20>(?<neg>\-)?$immed)(?<r20neg>\.NEG)?"o;
my $i20w6   = qr"(?<i20w6>$immed)"o;
my $i20w7   = qr"(?<i20w7>$immed)"o;
my $i20w8   = qr"(?<i20w8>$immed)"o;
my $i20w12  = qr"(?<i20w12>$immed)"o;
my $i20w24  = qr"(?<i20w24>\-?$immed)"o;
my $i20w32  = qr"(?<i20w32>\-?$immed)"o;
my $i39w8   = qr"(?<i39w8>\-?$immed)"o;
my $i28w8   = qr"(?<i28w8>$immed)"o;
my $i28w20  = qr"(?<i28w20>\-?$immed)"o;
my $i31w4   = qr"(?<i31w4>$immed)"o;
my $i34w13  = qr"(?<i34w13>$immed)"o;
my $i36w20  = qr"(?<i36w20>$immed)"o;
my $i48w8   = qr"(?<i48w8>$immed)"o;
my $i51w5   = qr"(?<i51w5>$immed)"o;
my $i53w5   = qr"(?<i53w5>$immed)"o;
my $ir20    = qr"$i20|$r20"o;
my $cr20    = qr"$c20|$r20"o;
my $icr20   = qr"$i20|$c20|$r20"o;
my $fcr20   = qr"$f20|$c20|$r20"o;
my $cr39    = qr"$c20s39|$r39"o;
my $dr20    = qr"$d20|$r20"o;

# Instruction specific rules for capturing various flags
my $u32   = qr"(?<U32>\.U32)?";
my $ftz   = qr"(?<FTZ>\.FTZ)?";
my $fmz   = qr"(?<FMZ>\.FMZ)?";
my $sat   = qr"(?<SAT>\.SAT)?";
my $rnd   = qr"(?:\.(?<rnd>RN|RM|RP|RZ))?";
my $round = qr"(?:\.(?<round>ROUND|FLOOR|CEIL|TRUNC))?";
my $fcmp  = qr"(?<cmp>\.LT|\.EQ|\.LE|\.GT|\.NE|\.GE|\.NUM|\.NAN|\.LTU|\.EQU|\.LEU|\.GTU|\.NEU|\.GEU|)";
my $icmp  = qr"\.(?<cmp>LT|EQ|LE|GT|NE|GE)";
my $bool  = qr"\.(?<bool>AND|OR|XOR|PASS_B)";
my $bool2 = qr"\.(?<bool2>AND|OR|XOR)";
my $func  = qr"\.(?<func>COS|SIN|EX2|LG2|RCP|RSQ|RCP64H|RSQ64H)";
my $rro   = qr"\.(?<func>SINCOS|EX2)";
my $add3  = qr"(?:\.(?<type>X|RS|LS))?";
my $lopz  = qr"(?:\.(?<z>NZ|Z) $p48,|(?<noz>))"o;
my $X     = qr"(?<X>\.X)?";
my $tld   = qr"(?<NODEP>NODEP\.)?(?:(?<reuse1>T)|(?<reuse2>P))";
my $chnls = qr"(?<chnls>R|RGBA)";
my $sr    = qr"SR_(?<sr>\S+)";
my $shf   = qr"(?<W>\.W)?(?:\.(?<type>U64|S64))?(?<HI>\.HI)?";
my $xmad  = qr"(?:\.(?<type1>U16|S16))?(?:\.(?<type2>U16|S16))?(?:\.(?<mode>MRG|PSL\.CLO|PSL|CHI|CLO|CSFU))?(?<CBCC>\.CBCC)?";
my $xmadc = qr"(?:\.(?<type1>U16|S16))?(?:\.(?<type2>U16|S16))?(?:\.(?<modec>MRG|PSL\.CLO|PSL|CHI|CLO|CSFU))?(?<CBCC>\.CBCC)?";
my $vmad8 = qr"\.(?<sign1>[SU])(?<size1>8|16)\.(?<sign2>[SU])(?<size2>8|16)(?<PO>\.PO)?(?<SHR_7>\.SHR_7)?(?<SHR_15>\.SHR_15)?(?<SAT>\.SAT)?";
my $vmad16= qr"\.(?<sign1>[SU])(?<size1>16)\.(?<sign2>[SU])(?<size2>16)";
my $hilo  = qr"(?:\.(?<mode>XHI|XLO))?";
my $vaddType = qr"(?:\.(?<UD>UD))?(?:\.(?<SD>SD))?(?:\.(?<sign1>[SU])(?<size1>8|16|32))?(?:\.(?<sign2>[SU])(?<size2>8|16|32))?";
my $vaddMode = qr"(?:\.(?<mode>MRG_16[HL]|MRG_8B[0-3]|ACC|MIN|MAX))?";
my $vmnmx = qr"(?:\.(?<MX>MX))?";
my $x2x   = qr"\.(?<destSign>F|U|S)(?<destWidth>8|16|32|64)\.(?<srcSign>F|U|S)(?<srcWidth>8|16|32|64)";
my $prmt  = qr"(?:\.(?<mode>F4E|B4E|RC8|ECL|ECR|RC16))?";
my $shfl  = qr"\.(?<mode>IDX|UP|DOWN|BFLY)";
my $bar   = qr"\.(?<mode>SYNC|ARV|RED)(?:\.(?<red>POPC|AND|OR))? (?:$i8w4|$r8)(?:, (?:$i20w12|$r20))?(?(<r20>)|(?<nor20>))(?(<red>), $p39|(?<nop39>))"o;
my $b2r   = qr"\.RESULT $r0(?:, $p45|(?<nop45>))"o;
my $dbar  = qr"(?<SB>SB0|SB1|SB2|SB3|SB4|SB5)";
my $dbar2 = qr" {(?<db5>5)?,?(?<db4>4)?,?(?<db3>3)?,?(?<db2>2)?,?(?<db1>1)?,?(?<db0>0)?}";
my $mbar  = qr"\.(?<mode>CTA|GL|SYS)";
my $addr  = qr"\[(?:(?<r8>$reg)|(?<nor8>))(?:\s*\+?\s*$i20w24)?\]"o;
my $addr2 = qr"\[(?:(?<r8>$reg)|(?<nor8>))(?:\s*\+?\s*$i28w20)?\]"o;
my $ldc   = qr"c\[(?<c36>$hex)\]\s*$addr"o;
my $atom  = qr"(?<E>\.E)?(?:\.(?<mode>ADD|MIN|MAX|INC|DEC|AND|OR|XOR|EXCH|CAS))(?<type>|\.S32|\.U64|\.F(?:16x2|32)\.FTZ\.RN|\.S64|\.64)";
my $vote  = qr"\.(?<mode>ALL|ANY|EQ)"o;
my $memType  = qr"(?<type>\.U8|\.S8|\.U16|\.S16||\.32|\.64|\.128)";
my $memCache = qr"(?<E>\.E)?(?<U>\.U)?(?:\.(?<cache>CG|CI|CS|CV|IL|WT))?";
my $dptype = qr"(?:\.(?<type1>U32|S32))?(?:\.(?<type2>U32|S32))?";
my $dpmode = qr"\.(?<mode>LO|HI)";
my $hmode  = qr"(?:\.(?<mode>F32|MRG_H0|MRG_H1))?$ftz";

# class: hardware resource that shares characteristics with types
# lat  : pipeline depth where relevent, placeholder for memory ops
# blat : barrier latency, typical fetch time for memory operations. Highly variable.
# rlat : operand read latency for memory ops
# rhold: clock cycles that a memory op typically holds onto a register before it's free to be written by another op.
# tput : throughput, clock cycles an op takes when two ops of the same class are issued in succession.
# dual : whether this instruction type can be dual issued
# reuse: whether this instruction type accepts register reuse flags.

# Some of these values are guesses and need to be updated from micro benchmarks.
# We may need to split these classes up further.
my $s2rT  = {class => 's2r',   lat => 2,   blat => 25,  rlat => 0, rhold => 0,  tput => 1,   dual => 0, reuse => 0};
my $smemT = {class => 'mem',   lat => 2,   blat => 30,  rlat => 2, rhold => 20, tput => 1,   dual => 1, reuse => 0};
my $gmemT = {class => 'mem',   lat => 2,   blat => 200, rlat => 4, rhold => 20, tput => 1,   dual => 1, reuse => 0};
my $x32T  = {class => 'x32',   lat => 6,   blat => 0,   rlat => 0, rhold => 0,  tput => 1,   dual => 0, reuse => 1};
my $x64T  = {class => 'x64',   lat => 2,   blat => 128, rlat => 0, rhold => 0,  tput => 128, dual => 0, reuse => 1};
my $shftT = {class => 'shift', lat => 6,   blat => 0,   rlat => 0, rhold => 0,  tput => 2,   dual => 0, reuse => 1};
my $cmpT  = {class => 'cmp',   lat => 13,  blat => 0,   rlat => 0, rhold => 0,  tput => 2,   dual => 0, reuse => 1};
my $qtrT  = {class => 'qtr',   lat => 8,   blat => 0,   rlat => 4, rhold => 0,  tput => 1,   dual => 1, reuse => 0};
my $rroT  = {class => 'rro',   lat => 2,   blat => 0,   rlat => 0, rhold => 0,  tput => 1,   dual => 0, reuse => 0};
my $voteT = {class => 'vote',  lat => 2,   blat => 0,   rlat => 0, rhold => 0,  tput => 1,   dual => 0, reuse => 0};


# Create map of op names to rules
our %grammar =
(
    #Floating Point Instructions
    FADD     => [ { type => $x32T,  code => 0x5c58000000000000, rule => qr"^$pred?FADD$ftz$rnd$sat $r0, $r8, $fcr20;"o,               } ],
    FADD32I  => [ { type => $x32T,  code => 0x0800000000000000, rule => qr"^$pred?FADD32I$ftz $r0, $r8, $f20w32;"o,                   } ],
    FCHK     => [ { type => $x32T,  code => 0x5c88000000000000, rule => qr"^$pred?FCHK\.DIVIDE $p0, $r8, $r20;"o,                     } ], #Partial?
    FCMP     => [ { type => $cmpT,  code => 0x5ba0000000000000, rule => qr"^$pred?FCMP$fcmp$ftz $r0, $r8, $fcr20, $r39;"o,            } ],
    FFMA     => [
                  { type => $x32T,  code => 0x5980000000000000, rule => qr"^$pred?FFMA$ftz$rnd$sat $r0, $r8, $fcr20, $r39;"o,         },
                  { type => $x32T,  code => 0x5980000000000000, rule => qr"^$pred?FFMA$ftz$rnd$sat $r0, $r8, $r39s20, $c20s39;"o,     },
                ],
    FMNMX    => [ { type => $shftT, code => 0x5c60000000000000, rule => qr"^$pred?FMNMX$ftz $r0, $r8, $fcr20, $p39;"o,                } ],
    FMUL     => [ { type => $x32T,  code => 0x5c68000000000000, rule => qr"^$pred?FMUL$ftz$rnd$sat $r0, $r8, $fcr20;"o,               } ],
    FMUL32I  => [ { type => $x32T,  code => 0x1e00000000000000, rule => qr"^$pred?FMUL32I$ftz $r0, $r8, $f20w32;"o,                   } ],
    FSET     => [ { type => $shftT, code => 0x5800000000000000, rule => qr"^$pred?FSET$fcmp$ftz$bool $r0, $r8, $fcr20, $p39;"o,       } ],
    FSETP    => [ { type => $cmpT,  code => 0x5bb0000000000000, rule => qr"^$pred?FSETP$fcmp$ftz$bool $p3, $p0, $r8, $fcr20, $p39;"o, } ],
    MUFU     => [ { type => $qtrT,  code => 0x5080000000000000, rule => qr"^$pred?MUFU$func $r0, $r8;"o,                              } ],
    RRO      => [ { type => $rroT,  code => 0x5c90000000000000, rule => qr"^$pred?RRO$rro $r0, $r20;"o,                               } ],
    DADD     => [ { type => $x64T,  code => 0x5c70000000000000, rule => qr"^$pred?DADD$rnd $r0, $r8, $dr20;"o,                        } ],
    DFMA     => [ { type => $x64T,  code => 0x5b70000000000000, rule => qr"^$pred?DFMA$rnd $r0, $r8, $dr20, $r39;"o,                  } ],
    DMNMX    => [ { type => $cmpT,  code => 0x5c50000000000000, rule => qr"^$pred?DMNMX $r0, $r8, $dr20, $p39;"o,                     } ],
    DMUL     => [ { type => $x64T,  code => 0x5c80000000000000, rule => qr"^$pred?DMUL$rnd $r0, $r8, $dr20;"o,                        } ],
    DSET     => [ { type => $cmpT,  code => 0x5900000000000000, rule => qr"^$pred?DSET$fcmp$bool $r0, $r8, $dr20, $p39;"o,            } ],
    DSETP    => [ { type => $cmpT,  code => 0x5b80000000000000, rule => qr"^$pred?DSETP$fcmp$bool $p3, $p0, $r8, $dr20, $p39;"o,      } ],
    FSWZADD  => [ { type => $x32T,  code => 0x0000000000000000, rule => qr"^$pred?FSWZADD[^;]*;"o,                                    } ], #TODO

    HADD2     => [ { type => $x32T,  code => 0x5d10000000000000, rule => qr"^$pred?HADD2$hmode$fmz$ftz$sat $r0, $r8, $r20;"o,               } ],
    HMUL2     => [ { type => $x32T,  code => 0x5d08000000000000, rule => qr"^$pred?HMUL2$hmode$fmz$ftz$sat $r0, $r8, $r20;"o,               } ],
    HFMA2     => [ { type => $x32T,  code => 0x5d00000000000000, rule => qr"^$pred?HFMA2$hmode$fmz$ftz$sat $r0, $r8, $r20, $r39;"o,         } ],
    HSETP2    => [ { type => $cmpT,  code => 0x5d20000000000000, rule => qr"^$pred?HSETP2$fcmp$bool $p3, $p0, $r8, $fcr20, $p39;"o, } ], #Partial

    #Integer Instructions
    BFE       => [ { type => $shftT,  code => 0x5c01000000000000, rule => qr"^$pred?BFE$u32 $r0, $r8, $icr20;"o,                          } ],
    BFI       => [ { type => $shftT,  code => 0x5bf0000000000000, rule => qr"^$pred?BFI $r0, $r8, $ir20, $cr39;"o,                        } ],
    FLO       => [ { type => $s2rT,   code => 0x5c30000000000000, rule => qr"^$pred?FLO\.U32 $r0, $icr20;"o,                              } ],
    IADD      => [ { type => $x32T,   code => 0x5c10000000000000, rule => qr"^$pred?IADD$sat$X $r0cc, $r8, $icr20;"o,                         } ],
    IADD32I   => [ { type => $x32T,   code => 0x1c00000000000000, rule => qr"^$pred?IADD32I$X $r0cc, $r8, $i20w32;"o,                         } ],
    IADD3     => [ { type => $x32T,   code => 0x5cc0000000000000, rule => qr"^$pred?IADD3$add3 $r0cc, $r8, $icr20, $r39;"o,                 } ],
    ICMP      => [ { type => $cmpT,   code => 0x5b41000000000000, rule => qr"^$pred?ICMP$icmp$u32 $r0, $r8, $icr20, $r39;"o,              } ],
    IMNMX     => [ { type => $shftT,  code => 0x5c21000000000000, rule => qr"^$pred?IMNMX$u32$hilo $r0cc, $r8, $icr20, $p39;"o,                  } ],
    ISET      => [ { type => $shftT,  code => 0x5b51000000000000, rule => qr"^$pred?ISET$icmp$u32$X$bool $r0, $r8, $icr20, $p39;"o,       } ],
    ISETP     => [ { type => $cmpT,   code => 0x5b61000000000000, rule => qr"^$pred?ISETP$icmp$u32$X$bool $p3, $p0, $r8, $icr20, $p39;"o, } ],
    ISCADD    => [ { type => $shftT,  code => 0x5c18000000000000, rule => qr"^$pred?ISCADD $r0, $r8, $icr20, $i39w8;"o,                   } ],
    ISCADD32I => [ { type => $shftT,  code => 0x1400000000000000, rule => qr"^$pred?ISCADD32I $r0, $r8, $i20w32, $i53w5;"o,               } ],
    LEA       => [
                   { type => $cmpT,   code => 0x5bd0000000000000, rule => qr"^$pred?LEA $p48, $r0cc, $r8, $icr20;"o,                      },
                   { type => $shftT,  code => 0x5bd7000000000000, rule => qr"^$pred?LEA $r0cc, $r8, $icr20, $i39w8;"o,                    },
                   { type => $shftT,  code => 0x5bdf004000000000, rule => qr"^$pred?LEA\.HI$X $r0cc, $r8, $r20, $r39, $i28w8;"o,          },
                   { type => $shftT,  code => 0x0a07000000000000, rule => qr"^$pred?LEA\.HI$X $r0cc, $r8, $c20, $r39, $i51w5;"o,          },
                 ],
    LOP       => [ { type => $x32T,   code => 0x5c40000000000000, rule => qr"^$pred?LOP$bool$lopz $r0, $r8, (?<INV>~)?$icr20(?<INV>\.INV)?;"o, } ],
    LOP32I    => [ { type => $x32T,   code => 0x0400000000000000, rule => qr"^$pred?LOP32I$bool $r0, $r8, $i20w32;"o,                     } ],
    LOP3      => [
                   { type => $x32T,   code => 0x5be7000000000000, rule => qr"^$pred?LOP3\.LUT $r0, $r8, $r20, $r39, $i28w8;"o,            },
                   { type => $x32T,   code => 0x3c00000000000000, rule => qr"^$pred?LOP3\.LUT $r0, $r8, $i20, $r39, $i48w8;"o,            },
                 ],
    POPC      => [ { type => $s2rT,   code => 0x5c08000000000000, rule => qr"^$pred?POPC $r0, $r20;"o,                                    } ],
    SHF       => [
                   { type => $shftT,  code => 0x5bf8000000000000, rule => qr"^$pred?SHF\.L$shf $r0, $r8, $ir20, $r39;"o,                  },
                   { type => $shftT,  code => 0x5cf8000000000000, rule => qr"^$pred?SHF\.R$shf $r0, $r8, $ir20, $r39;"o,                  },
                 ],
    SHL       => [ { type => $shftT,  code => 0x5c48000000000000, rule => qr"^$pred?SHL(?<W>\.W)? $r0, $r8, $icr20;"o,                    } ],
    SHR       => [ { type => $shftT,  code => 0x5c29000000000000, rule => qr"^$pred?SHR$u32 $r0, $r8, $icr20;"o,                          } ],
    XMAD      => [
                   { type => $x32T,   code => 0x5b00000000000000, rule => qr"^$pred?XMAD$xmad $r0cc, $r8, $ir20, $r39;"o,                 },
                   { type => $x32T,   code => 0x5900000000000000, rule => qr"^$pred?XMAD$xmad $r0cc, $r8, $r39s20, $c20s39;"o,            },
                   { type => $x32T,   code => 0x5e00000000000000, rule => qr"^$pred?XMAD$xmadc $r0cc, $r8, $c20x, $r39;"o,                  },
                 ],
    # XMAD replaces these
    IMAD      => [ { type => $x32T,   code => 0x0000000000000000, rule => qr"^$pred?IMAD[^;]*;"o,   } ], #TODO
    IMADSP    => [ { type => $x32T,   code => 0x0000000000000000, rule => qr"^$pred?IMADSP[^;]*;"o, } ], #TODO
    IMUL      => [ { type => $x32T,   code => 0x0000000000000000, rule => qr"^$pred?IMUL[^;]*;"o,   } ], #TODO

    #Conversion Instructions
    F2F => [ { type => $qtrT,  code => 0x5ca8000000000000, rule => qr"^$pred?F2F$ftz$x2x$rnd$round$sat $r0, $cr20;"o, } ],
    F2I => [ { type => $qtrT,  code => 0x5cb0000000000000, rule => qr"^$pred?F2I$ftz$x2x$round $r0, $cr20;"o,         } ],
    I2F => [ { type => $qtrT,  code => 0x5cb8000000000000, rule => qr"^$pred?I2F$x2x$rnd $r0, $cr20;"o,               } ],
    I2I => [ { type => $qtrT,  code => 0x5ce0000000000000, rule => qr"^$pred?I2I$x2x$sat $r0, $cr20;"o,               } ],

    #Movement Instructions
    MOV    => [ { type => $x32T,  code => 0x5c98078000000000, rule => qr"^$pred?MOV $r0, $icr20;"o,                   } ],
    MOV32I => [ { type => $x32T,  code => 0x010000000000f000, rule => qr"^$pred?MOV32I $r0, (?:$i20w32|$f20w32);"o,   } ],
    PRMT   => [ { type => $x32T,  code => 0x5bc0000000000000, rule => qr"^$pred?PRMT$prmt $r0, $r8, $icr20, $cr39;"o, } ],
    SEL    => [ { type => $x32T,  code => 0x5ca0000000000000, rule => qr"^$pred?SEL $r0, $r8, $icr20, $p39;"o,        } ],
    SHFL   => [ { type => $smemT, code => 0xef10000000000000, rule => qr"^$pred?SHFL$shfl $p48, $r0, $r8, (?:$i20w8|$r20), (?:$i34w13|$r39);"o, } ],

    #Predicate/CC Instructions
    PSET   => [ { type => $cmpT,  code => 0x5088000000000000, rule => qr"^$pred?PSET$bool2$bool $r0, $p12, $p29, $p39;"o,       } ],
    PSETP  => [ { type => $cmpT,  code => 0x5090000000000000, rule => qr"^$pred?PSETP$bool2$bool $p3, $p0, $p12, $p29, $p39;"o, } ],
    CSET   => [ { type => $x32T,  code => 0x0000000000000000, rule => qr"^$pred?CSET[^;]*;"o,  } ], #TODO
    CSETP  => [ { type => $x32T,  code => 0x0000000000000000, rule => qr"^$pred?CSETP[^;]*;"o, } ], #TODO
    P2R    => [ { type => $x32T,  code => 0x38e8000000000000, rule => qr"^$pred?P2R $r0, PR, $r8, $i20w7;"o,   } ],
    R2P    => [ { type => $shftT, code => 0x38f0000000000000, rule => qr"^$pred?R2P PR, $r8, $i20w7;"o,   } ],

    #Texture Instructions
    # Handle the commonly used 1D texture functions.. but save the others for later
    TLD    => [ { type => $gmemT, code => 0xdd38000000000000, rule => qr"^$pred?TLD\.B\.LZ\.$tld $r0, $r8, $r20, $hex, \dD, $i31w4;"o, } ], #Partial
    TLDS   => [ { type => $gmemT, code => 0xda0000000ff00000, rule => qr"^$pred?TLDS\.LZ\.$tld $r28, $r0, $r8, $i36w20, \dD, $chnls;"o,} ], #Partial
    TEX    => [ { type => $gmemT, code => 0x0000000000000000, rule => qr"^$pred?TEX[^;]*;"o,   } ], #TODO
    TLD4   => [ { type => $gmemT, code => 0x0000000000000000, rule => qr"^$pred?TLD4[^;]*;"o,  } ], #TODO
    TXQ    => [ { type => $gmemT, code => 0x0000000000000000, rule => qr"^$pred?TXQ[^;]*;"o,   } ], #TODO
    TEXS   => [ { type => $gmemT, code => 0x0000000000000000, rule => qr"^$pred?TEXS[^;]*;"o,  } ], #TODO
    TLD4S  => [ { type => $gmemT, code => 0x0000000000000000, rule => qr"^$pred?TLD4S[^;]*;"o, } ], #TODO

    #Compute Load/Store Instructions
    LD     => [ { type => $gmemT, code => 0x8000000000000000, rule => qr"^$pred?LD$memCache$memType $r0, $addr, $p58;"o,      } ],
    ST     => [ { type => $gmemT, code => 0xa000000000000000, rule => qr"^$pred?ST$memCache$memType $addr, $r0, $p58;"o,      } ],
    LDG    => [ { type => $gmemT, code => 0xeed0000000000000, rule => qr"^$pred?LDG$memCache$memType $r0, $addr;"o,           } ],
    STG    => [ { type => $gmemT, code => 0xeed8000000000000, rule => qr"^$pred?STG$memCache$memType $addr, $r0;"o,           } ],
    LDS    => [ { type => $smemT, code => 0xef48000000000000, rule => qr"^$pred?LDS$memCache$memType $r0, $addr;"o,           } ],
    STS    => [ { type => $smemT, code => 0xef58000000000000, rule => qr"^$pred?STS$memCache$memType $addr, $r0;"o,           } ],
    LDL    => [ { type => $gmemT, code => 0xef40000000000000, rule => qr"^$pred?LDL$memCache$memType $r0, $addr;"o,           } ],
    STL    => [ { type => $gmemT, code => 0xef50000000000000, rule => qr"^$pred?STL$memCache$memType $addr, $r0;"o,           } ],
    LDC    => [ { type => $gmemT, code => 0xef90000000000000, rule => qr"^$pred?LDC$memCache$memType $r0, $ldc;"o,            } ],
    # Note for ATOM(S).CAS operations the last register needs to be in sequence with the second to last (as it's not encoded).
    ATOM   => [ { type => $gmemT, code => 0xed00000000000000, rule => qr"^$pred?ATOM$atom $r0, $addr2, $r20(?:, $r39a)?;"o,   } ],
    ATOMS  => [ { type => $smemT, code => 0xec00000000000000, rule => qr"^$pred?ATOMS$atom $r0, $addr2, $r20(?:, $r39a)?;"o,  } ],
    RED    => [ { type => $gmemT, code => 0xebf8000000000000, rule => qr"^$pred?RED$atom $addr2, $r0;"o,                      } ],
    CCTL   => [ { type => $x32T,  code => 0x5c88000000000000, rule => qr"^$pred?CCTL[^;]*;"o,  } ], #TODO
    CCTLL  => [ { type => $x32T,  code => 0x5c88000000000000, rule => qr"^$pred?CCTLL[^;]*;"o, } ], #TODO
    CCTLT  => [ { type => $x32T,  code => 0x5c88000000000000, rule => qr"^$pred?CCTLT[^;]*;"o, } ], #TODO

    #Surface Memory Instructions (haven't gotten to these yet..)
    SUATOM => [ { type => $x32T, code => 0x0000000000000000, rule => qr"^$pred?SUATOM[^;]*;"o, } ], #TODO
    SULD   => [ { type => $x32T, code => 0x0000000000000000, rule => qr"^$pred?SULD[^;]*;"o,   } ], #TODO
    SURED  => [ { type => $x32T, code => 0x0000000000000000, rule => qr"^$pred?SURED[^;]*;"o,  } ], #TODO
    SUST   => [ { type => $x32T, code => 0x0000000000000000, rule => qr"^$pred?SUST[^;]*;"o,   } ], #TODO

    #Control Instructions
    BRA    => [
                { type => $x32T, code => 0xe24000000000000f, rule => qr"^$pred?BRA(?<U>\.U)? $i20w24;"o,         },
                { type => $x32T, code => 0xe240000000000002, rule => qr"^$pred?BRA(?<U>\.U)? CC\.EQ, $i20w24;"o, },
              ],
    BRX    => [ { type => $x32T, code => 0x0000000000000000, rule => qr"^$pred?BRX[^;]*;"o,                      } ], #TODO
    JMP    => [ { type => $x32T, code => 0x0000000000000000, rule => qr"^$pred?JMP[^;]*;"o,                      } ], #TODO
    JMX    => [ { type => $x32T, code => 0x0000000000000000, rule => qr"^$pred?JMX[^;]*;"o,                      } ], #TODO
    SSY    => [ { type => $x32T, code => 0xe290000000000000, rule => qr"^$noPred?SSY $i20w24;"o,                 } ],
    SYNC   => [ { type => $x32T, code => 0xf0f800000000000f, rule => qr"^$pred?SYNC;"o,                          } ],
    CAL    => [ { type => $x32T, code => 0xe260000000000040, rule => qr"^$noPred?CAL $i20w24;"o,                 } ],
    JCAL   => [ { type => $x32T, code => 0xe220000000000040, rule => qr"^$noPred?JCAL $i20w24;"o,                } ],
    PRET   => [ { type => $x32T, code => 0x0000000000000000, rule => qr"^$pred?PRET[^;]*;"o,                     } ], #TODO
    RET    => [ { type => $x32T, code => 0xe32000000000000f, rule => qr"^$pred?RET;"o,                           } ],
    BRK    => [ { type => $x32T, code => 0xe34000000000000f, rule => qr"^$pred?BRK;"o,                           } ],
    PBK    => [ { type => $x32T, code => 0xe2a0000000000000, rule => qr"^$noPred?PBK $i20w24;"o,                 } ],
    CONT   => [ { type => $x32T, code => 0xe35000000000000f, rule => qr"^$pred?CONT;"o,                          } ],
    PCNT   => [ { type => $x32T, code => 0xe2b0000000000000, rule => qr"^$noPred?PCNT $i20w24;"o,                } ],
    EXIT   => [ { type => $x32T, code => 0xe30000000000000f, rule => qr"^$pred?EXIT;"o,                          } ],
    PEXIT  => [ { type => $x32T, code => 0x0000000000000000, rule => qr"^$pred?PEXIT[^;]*;"o,                    } ], #TODO
    BPT    => [ { type => $x32T, code => 0xe3a00000000000c0, rule => qr"^$noPred?BPT\.TRAP $i20w24;"o,           } ],

    #Miscellaneous Instructions
    NOP    => [ { type => $x32T,  code => 0x50b0000000000f00, rule => qr"^$pred?NOP;"o,                                     } ],
    CS2R   => [ { type => $x32T,  code => 0x50c8000000000000, rule => qr"^$pred?CS2R $r0, $sr;"o,                           } ],
    S2R    => [ { type => $s2rT,  code => 0xf0c8000000000000, rule => qr"^$pred?S2R $r0, $sr;"o,                            } ],
    B2R    => [ { type => $x32T,  code => 0xf0b800010000ff00, rule => qr"^$pred?B2R$b2r;"o,                                 } ],
    BAR    => [ { type => $gmemT, code => 0xf0a8000000000000, rule => qr"^$pred?BAR$bar;"o,                                 } ],
    DEPBAR => [
                { type => $gmemT, code => 0xf0f0000000000000, rule => qr"^$pred?DEPBAR$icmp $dbar, $i20w6;"o, },
                { type => $gmemT, code => 0xf0f0000000000000, rule => qr"^$pred?DEPBAR$dbar2;"o,              },
              ],
    MEMBAR => [ { type => $x32T,  code => 0xef98000000000000, rule => qr"^$pred?MEMBAR$mbar;"o,                             } ],
    VOTE   => [ { type => $voteT, code => 0x50d8000000000000, rule => qr"^$pred?VOTE$vote (?:$r0, |(?<nor0>))$p45, $p39;"o, } ],
    R2B    => [ { type => $x32T,  code => 0x0000000000000000, rule => qr"^$pred?R2B[^;]*;"o,                                } ], #TODO

    #Video Instructions... Need to finish
    VADD   => [   { type => $shftT, code => 0x2044000000000000, rule => qr"^$pred?VADD$vaddType$sat$vaddMode $r0, $r8, $r20, $r39;"o, } ], #Partial 0x2044000000000000
    VMAD   => [
                  { type => $x32T,  code => 0x5f04000000000000, rule => qr"^$pred?VMAD$vmad16 $r0, $r8, $r20, $r39;"o, },
                  { type => $shftT, code => 0x5f04000000000000, rule => qr"^$pred?VMAD$vmad8 $r0, $r8, $r20, $r39;"o, },
              ],
    VABSDIFF => [ { type => $shftT, code => 0x5427000000000000, rule => qr"^$pred?VABSDIFF$vaddType$sat$vaddMode $r0, $r8, $r20, $r39;"o, } ], #Partial 0x2044000000000000
    VMNMX    => [ { type => $shftT, code => 0x3a44000000000000, rule => qr"^$pred?VMNMX$vaddType$vmnmx$sat$vaddMode $r0, $r8, $r20, $r39;"o, } ], #Partial 0x2044000000000000

    VSET => [ { type => $shftT, code => 0x4004000000000000, rule => qr"^$pred?VSET$icmp$vaddType$vaddMode $r0, $r8, $r20, $r39;"o, } ], #Partial 0x2044000000000000

    DP4A => [ { type => $x32T,   code => 0x53f8000000000000, rule => qr"^$pred?DP4A$dptype $r0, $r8, $icr20, $r39;"o, } ],
    DP2A => [ { type => $x32T,   code => 0x53f9000000000000, rule => qr"^$pred?DP2A$dpmode$dptype $r0, $r8, $icr20, $r39;"o, } ],
);

# Create map of capture groups to op code flags that need to be added (or removed)
my @flags = grep /\S/, split "\n", q{;

BFE, BFI, FLO, IADD, IADD3, ICMP, IMNMX, ISCADD, ISET, ISETP, LEA, LOP, LOP3, MOV, PRMT, SEL, SHF, SHL, SHR, XMAD
0x0100000000000000 neg

FADD, FCMP, FFMA, FMNMX, FMUL, FSET, FSETP, DADD, DFMA, DMNMX, DMUL, DSET, DSETP
0x0100000000000000 neg

PSET, PSETP
0x0000000000008000 p12not
0x0000000100000000 p29not

FMNMX, FSET, FSETP, DMNMX, DSET, DSETP, IMNMX, ISET, ISETP, SEL, PSET, PSETP, BAR, VOTE
0x0000040000000000 p39not

IADD, IADD3, XMAD, LEA, IMNMX
0x0000800000000000 CC

IADD32I
0x0010000000000000 CC

LEA
0x0000000000000000 X

SHF
0x0004000000000000 W
0x0001000000000000 HI

SHF: type
0x0000004000000000 U64
0x0000006000000000 S64

SHR, IMNMX, ISETP, ISET, ICMP, BFE
0x0001000000000000 U32

SHL
0x0000008000000000 W

SHFL
0x0000000010000000 i20w8
0x0000000020000000 i34w13

SHFL: mode
0x0000000000000000 IDX
0x0000000040000000 UP
0x0000000080000000 DOWN
0x00000000c0000000 BFLY

IMNMX: mode
0x0000080000000000 XLO
0x0000180000000000 XHI

ISETP, ISET, ICMP: cmp
0x0002000000000000 LT
0x0004000000000000 EQ
0x0006000000000000 LE
0x0008000000000000 GT
0x000a000000000000 NE
0x000c000000000000 GE

ISETP, ISET, PSETP, PSET: bool
0x0000000000000000 AND
0x0000200000000000 OR
0x0000400000000000 XOR

PSETP, PSET: bool2
0x0000000000000000 AND
0x0000000001000000 OR
0x0000000002000000 XOR

ISETP, ISET
0x0000080000000000 X

LOP: bool
0x0000000000000000 AND
0x0000020000000000 OR
0x0000040000000000 XOR
0x0000060000000000 PASS_B

LOP:
0x0000010000000000 INV

LOP: z
0x0000200000000000 Z
0x0000300000000000 NZ

LOP
0x0007000000000000 noz

LOP32I: bool
0x0000000000000000 AND
0x0020000000000000 OR
0x0040000000000000 XOR

PRMT: mode
0x0001000000000000 F4E
0x0002000000000000 B4E
0x0003000000000000 RC8
0x0004000000000000 ECL
0x0005000000000000 ECR
0x0006000000000000 RC16

XMAD: type1
0x0000000000000000 U16
0x0001000000000000 S16

XMAD: type2
0x0000000000000000 U16
0x0002000000000000 S16

XMAD: mode
0x0000002000000000 MRG
0x0000001000000000 PSL
0x0008000000000000 CHI
0x0004000000000000 CLO
0x000c000000000000 CSFU
0x0004001000000000 PSL.CLO

XMAD: modec
0x0004000000000000 CLO
0x0008000000000000 CHI
0x000c000000000000 CSFU
0x0040000000000000 X
0x0080000000000000 PSL
0x0100000000000000 MRG
0x0084000000000000 PSL.CLO

XMAD
0x0010000000000000 CBCC

XMAD: r8part
0x0000000000000000 H0
0x0020000000000000 H1

XMAD: r20part
0x0000000000000000 H0
0x0000000800000000 H1

XMAD: r20partx
0x0000000000000000 H0
0x0010000000000000 H1

XMAD: r39part
0x0000000000000000 H0
0x0010000000000000 H1

VMAD, VADD, VABSDIFF, VMNMX, VSET: r8part
0x0000000000000000 B0
0x0000001000000000 B1
0x0000002000000000 B2
0x0000003000000000 B3
0x0000001000000000 H1
0x0000000000000000 H0

VMAD, VADD, VABSDIFF, VMNMX, VSET: r20part
0x0000000000000000 B0
0x0000000010000000 B1
0x0000000020000000 B2
0x0000000030000000 B3
0x0000000010000000 H1
0x0000000000000000 H0

VMAD
0x0040000000000000 r8neg
0x0020000000000000 r39neg
0x0008000000000000 SHR_7
0x0010000000000000 SHR_15
0x0060000000000000 PO
0x0080000000000000 SAT

VMNMX
0x0100000000000000 MX

VADD, VABSDIFF, VMNMX
0x0080000000000000 SAT
0x0040000000000000 UD
0x0040000000000000 SD

VSET: cmp
0x0040000000000000 LT
0x0080000000000000 EQ
0x00c0000000000000 LE
0x0100000000000000 GT
0x0140000000000000 NE
0x0180000000000000 GE

VADD, VSET: mode
0x0020000000000000 ACC
0x0028000000000000 MIN
0x0030000000000000 MAX
0x0000000000000000 MRG_16H
0x0008000000000000 MRG_16L
0x0010000000000000 MRG_8B0
0x0000000000000000 MRG_8B1
0x0018000000000000 MRG_8B2
0x0000000000000000 MRG_8B3

VABSDIFF: mode
0x0003000000000000 ACC
0x000b000000000000 MIN
0x0013000000000000 MAX
0x0023000000000000 MRG_16H
0x002b000000000000 MRG_16L
0x0033000000000000 MRG_8B0
0x0000000000000000 MRG_8B1
0x003b000000000000 MRG_8B2
0x0000000000000000 MRG_8B3

VMNMX: mode
0x0020000000000000 ACC
0x0028000000000000 MIN
0x0030000000000000 MAX
0x0000000000000000 MRG_16H
0x0008000000000000 MRG_16L
0x0010000000000000 MRG_8B0
0x0000000000000000 MRG_8B1
0x0018000000000000 MRG_8B2
0x0000000000000000 MRG_8B3

VMAD, VADD, VABSDIFF, VMNMX, VSET: sign1
0x0000000000000000 U
0x0001000000000000 S

VMAD, VADD, VABSDIFF, VMNMX, VSET: sign2
0x0000000000000000 U
0x0002000000000000 S

VMAD, VADD, VABSDIFF, VMNMX, VSET: size1
0x0000000000000000 8
0x0000004000000000 16
0x0000006000000000 32

VMAD, VADD, VABSDIFF, VMNMX, VSET: size2
0x0000000000000000 8
0x0000000040000000 16
0x0000000060000000 32

IADD3: type
0x0001000000000000 X
0x0000002000000000 RS
0x0000004000000000 LS

IADD3: r8part
0x0000000000000000 H0
0x0000001000000000 H1

IADD3: r20part
0x0000000080000000 H0

IADD3: r39part
0x0000000200000000 H0

IADD3
0x0008000000000000 r8neg
0x0004000000000000 r20neg
0x0002000000000000 r39neg

IADD
0x0000080000000000 X
0x0004000000000000 SAT

IADD, ISCADD
0x0002000000000000 r8neg
0x0001000000000000 r20neg

IADD32I
0x0100000000000000 r8neg
0x0020000000000000 X

DEPBAR: SB
0x0000000000000000 SB0
0x0000000004000000 SB1
0x0000000008000000 SB2
0x000000000c000000 SB3
0x0000000010000000 SB4
0x0000000014000000 SB5

DEPBAR: cmp
0x0000000020000000 LE

DEPBAR
0x0000000000000001 db0
0x0000000000000002 db1
0x0000000000000004 db2
0x0000000000000008 db3
0x0000000000000010 db4
0x0000000000000020 db5

F2F, F2I, I2F, I2I: destWidth
0x0000000000000000 8
0x0000000000000100 16
0x0000000000000200 32
0x0000000000000300 64

F2F, F2I, I2F, I2I: srcWidth
0x0000000000000000 8
0x0000000000000400 16
0x0000000000000800 32
0x0000000000000c00 64

F2F, F2I, I2F, I2I: destSign
0x0000000000000000 F
0x0000000000000000 U
0x0000000000001000 S

F2F, F2I, I2F, I2I: srcSign
0x0000000000000000 F
0x0000000000000000 U
0x0000000000002000 S

F2I, I2F, I2I: r20part
0x0000000000000000 H0
0x0000040000000000 H1
0x0000000000000000 B0
0x0000020000000000 B1
0x0000040000000000 B2
0x0000060000000000 B3

F2F: r20part
0x0000000000000000 H0
0x0000020000000000 H1

F2F: round
0x0000040000000000 ROUND
0x0000048000000000 FLOOR
0x0000050000000000 CEIL
0x0000058000000000 TRUNC

F2I: round
0x0000000000000000 ROUND
0x0000008000000000 FLOOR
0x0000010000000000 CEIL
0x0000018000000000 TRUNC

HADD2, HMUL2, HFMA2: r8part
0x0001000000000000 H0_H0
0x0001800000000000 H1_H1
0x0000800000000000 F32

HADD2, HMUL2, HFMA2: r20part
0x0000000020000000 H0_H0
0x0000000030000000 H1_H1

HFMA2: r39part
0x0000000800000000 F32
0x0000001000000000 H0_H0
0x0000001800000000 H1_H1

HADD2, HMUL2, HFMA2
0x0000000080000000 r20neg
0x0000000040000000 r39neg

HADD2, HMUL2, HFMA2: mode
0x0002000000000000 F32
0x0004000000000000 MRG_H0
0x0006000000000000 MRG_H1

HADD2, HMUL2
0x0000008000000000 FTZ

HFMA2
0x0000002000000000 FTZ

HFMA2
0x0000004000000000 FMZ

HADD2, HMUL2, HFMA2
0x0000000100000000 SAT

FADD, DADD, FMUL, DMUL, F2F, I2F: rnd
0x0000000000000000 RN
0x0000008000000000 RM
0x0000010000000000 RP
0x0000018000000000 RZ

DFMA: rnd
0x0000000000000000 RN
0x0004000000000000 RM
0x0008000000000000 RP
0x000c000000000000 RZ

FFMA: rnd
0x0000000000000000 RN
0x0008000000000000 RM
0x0010000000000000 RP
0x0018000000000000 RZ

FFMA
0x0020000000000000 FTZ

F2F, F2I, FADD, FMUL, FMNMX
0x0000100000000000 FTZ

FADD32I
0x0080000000000000 FTZ

FMUL32I
0x0020000000000000 FTZ

FSET
0x0080000000000000 FTZ

FSETP, FCMP
0x0000800000000000 FTZ

FADD, FFMA, FMUL, F2F, I2I
0x0004000000000000 SAT

FADD, DADD, FMNMX, DMNMX, MUFU
0x0001000000000000 r8neg

FADD, DADD, FMNMX, DMNMX, RRO, F2F, F2I, I2F, I2I
0x0000200000000000 r20neg

FMUL, DMUL, FFMA, DFMA
0x0001000000000000 r20neg

FFMA, DFMA
0x0002000000000000 r39neg

FADD, DADD, FMNMX, DMNMX
0x0000400000000000 r8abs

FADD, DADD, FMNMX, DMNMX, F2F, F2I, I2F, I2I
0x0002000000000000 r20abs

FSETP, DSETP, FSET, DSET
0x0000080000000000 r8neg
0x0000000000000040 r20neg
0x0000000000000080 r8abs
0x0000100000000000 r20abs

RRO: func
0x0000000000000000 SINCOS
0x0000008000000000 EX2

MUFU: func
0x0000000000000000 COS
0x0000000000100000 SIN
0x0000000000200000 EX2
0x0000000000300000 LG2
0x0000000000400000 RCP
0x0000000000500000 RSQ
0x0000000000600000 RCP64H
0x0000000000700000 RSQ64H

FSETP, DSETP, FSET, DSET, FCMP: cmp
0x0001000000000000 .LT
0x0002000000000000 .EQ
0x0003000000000000 .LE
0x0004000000000000 .GT
0x0004000000000000
0x0005000000000000 .NE
0x0006000000000000 .GE
0x0007000000000000 .NUM
0x0008000000000000 .NAN
0x0009000000000000 .LTU
0x000a000000000000 .EQU
0x000b000000000000 .LEU
0x000c000000000000 .GTU
0x000d000000000000 .NEU
0x000e000000000000 .GEU

FSETP, DSETP, FSET, DSET: bool
0x0000000000000000 AND
0x0000200000000000 OR
0x0000400000000000 XOR

HSETP2: cmp
0x0000002800000000 .NE

HSETP2: bool
0x0000000000000000 AND

S2R: sr
0x0000000000000000 LANEID
0x0000000000200000 VIRTCFG
0x0000000000300000 VIRTID
0x0000000002100000 TID.X
0x0000000002200000 TID.Y
0x0000000002300000 TID.Z
0x0000000002500000 CTAID.X
0x0000000002600000 CTAID.Y
0x0000000002700000 CTAID.Z
0x0000000003800000 EQMASK
0x0000000003900000 LTMASK
0x0000000003a00000 LEMASK
0x0000000003b00000 GTMASK
0x0000000003c00000 GEMASK

CS2R: sr
0x0000000005000000 CLOCKLO
0x0000000005100000 CLOCKHI
0x0000000005200000 GLOBALTIMERLO
0x0000000005300000 GLOBALTIMERHI

B2R
0x0000e00000000000 nop45

BAR
0x0000100000000000 i8w4
0x0000080000000000 nor20
0x0000038000000000 nop39

BAR: mode
0x0000000000000000 SYNC
0x0000000100000000 ARV
0x0000000200000000 RED

BAR: red
0x0000000000000000 POPC
0x0000000800000000 AND
0x0000001000000000 OR

MEMBAR: mode
0x0000000000000000 CTA
0x0000000000000100 GL
0x0000000000000200 SYS

VOTE: mode
0x0000000000000000 ALL
0x0001000000000000 ANY
0x0002000000000000 EQ

VOTE
0x00000000000000ff nor0

BRA
0x0000000000000080 U

TLDS: chnls
0x0010000000000000 RGBA

TLDS
0x0002000000000000 NODEP

LD, ST, LDG, STG, LDS, STS, LDL, STL, LDC, RED, ATOM, ATOMS
0x000000000000ff00 nor8

LD, ST: type
0x0000000000000000 .U8
0x0020000000000000 .S8
0x0040000000000000 .U16
0x0060000000000000 .S16
0x0080000000000000
0x0080000000000000 .32
0x00a0000000000000 .64
0x00c0000000000000 .128

LD, ST: cache
0x0100000000000000 CG
0x0200000000000000 CS
0x0300000000000000 CV
0x0300000000000000 WT

LDG, STG, LDS, STS, LDL, STL, LDC: type
0x0000000000000000 .U8
0x0001000000000000 .S8
0x0002000000000000 .U16
0x0003000000000000 .S16
0x0004000000000000
0x0004000000000000 .32
0x0005000000000000 .64
0x0006000000000000 .128

LDG, STG: cache
0x0000400000000000 CG
0x0000800000000000 CI
0x0000800000000000 CS
0x0000c00000000000 CV
0x0000c00000000000 WT

LDL: cache
0x0000200000000000 CI

LDC: cache
0x0000100000000000 IL

LDG, STG, LDS, STS, LDL, STL, LDC
0x0000200000000000 E

LDS
0x0000100000000000 U

RED: type
0x0000000000000000
0x0000000000100000 .S32
0x0000000000200000 .U64
0x0000000000300000 .F32.FTZ.RN
0x0000000000400000 .F16x2.FTZ.RN
0x0000000000500000 .S64

RED: mode
0x0000000000000000 ADD
0x0000000000800000 MIN
0x0000000001000000 MAX
0x0000000001800000 INC
0x0000000002000000 DEC
0x0000000002800000 AND
0x0000000003000000 OR
0x0000000003800000 XOR

ATOM: type
0x0000000000000000
0x0002000000000000 .S32
0x0004000000000000 .U64
0x0006000000000000 .F32.FTZ.RN
0x0008000000000000 .F16x2.FTZ.RN
0x000a000000000000 .S64
0x0002000000000000 .64

ATOM, RED
0x0001000000000000 E

ATOM: mode
0x0000000000000000 ADD
0x0010000000000000 MIN
0x0020000000000000 MAX
0x0030000000000000 INC
0x0040000000000000 DEC
0x0050000000000000 AND
0x0060000000000000 OR
0x0070000000000000 XOR
0x0080000000000000 EXCH
0x03f0000000000000 CAS

ATOMS: type
0x0000000000000000
0x0000000010000000 .S32
0x0000000020000000 .U64
0x0000000030000000 .S64
0x0010000000000000 .64

ATOMS: mode
0x0000000000000000 ADD
0x0010000000000000 MIN
0x0020000000000000 MAX
0x0030000000000000 INC
0x0040000000000000 DEC
0x0050000000000000 AND
0x0060000000000000 OR
0x0070000000000000 XOR
0x0080000000000000 EXCH
0x0240000000000000 CAS

DP4A, DP2A: type1
0x0000000000000000 U32
0x0002000000000000 S32

DP4A, DP2A: type2
0x0000000000000000 U32
0x0000800000000000 S32

DP2A: mode
0x0000000000000000 LO
0x0004000000000000 HI
};

# The existence of a capture group can map directly to an op code adjustment, or...
# The named capture group value can map the op code adjustmemt from among several options
our %flags;
my (@ops, $flag);
foreach my $line (@flags)
{
    if ($line =~ m'^(0x[0-9a-z]+)\s*(.*)')
    {
        my $val = hex($1);
        # named rules (op: name)
        if ($flag)
            { $flags{$_}{$flag}{$2} = $val foreach @ops; }
        # simple existence check rules
        else
            { $flags{$_}{$2}        = $val foreach @ops; }
    }
    else
    {
        my ($ops, $name) = split ':\s*', $line;
        @ops = split ',\s*', $ops;
        $flag = $name;
    }
}

sub parseInstruct
{
    my ($inst, $grammar) = @_;
    return unless $inst =~ $grammar->{rule};
    my %capData = %+;
    return \%capData;
}

# for immediate or constant operands and a given opcode, bits 56-63 get transformed
my %immedOps = map { $_ => 1 } qw(i20 f20 d20);
my %immedCodes =
(
    0x5c => 0x64,
    0x5b => 0x6d,
    0x59 => 0x6b,
    0x58 => 0x68,
);
my %constCodes =
(
    c20 => 0x10,
    c39 => 0x08,
);
my %reuseCodes = (reuse1 => 1, reuse2 => 2, reuse3 => 4);

# just pick out the reuse code and nothing else
sub genReuseCode
{
    my $capData = shift;
    my $reuse = 0;
    $reuse |= $reuseCodes{$_} foreach grep $capData->{$_}, keys %reuseCodes;
    return $reuse;
}

# Generate an op code from regex capture data
# if you pass in a test array ref it will populate it with the matching capture groups
sub genCode
{
    my ($op, $grammar, $capData, $test) = @_;

    my $flags     = $flags{$op};
    my $code      = $grammar->{code};
    my $reuse     = 0;
    my $immedCode = $immedCodes{$code >> 56};

    #print map "$_: $capData->{$_}\n", keys %capData if $op eq 'I2I';

    # process the instruction predicate (if valid for this instuction)
    if (exists $capData->{noPred})
    {
        delete $capData->{noPred};
        push @$test, 'noPred' if $test;
    }
    else
    {
        my $p = defined($capData->{predNum}) ? $capData->{predNum} : 7;
        push @$test, 'predNum' if $test;
        if (exists $capData->{predNot})
        {
            $p |= 8;
            push @$test, 'predNot' if $test;
        }
        $code ^= $p << 16;
        delete @{$capData}{qw(predNum predNot)};

    }
    # process the register reuse flags
    foreach my $rcode (qw(reuse1 reuse2 reuse3))
    {
        if (delete $capData->{$rcode})
        {
            $reuse |= $reuseCodes{$rcode};
            push @$test, $rcode if $test;
        }
    }

    foreach my $capture (keys %$capData)
    {
        # change the base code for immediate versions of the op
        if (exists $immedOps{$capture})
            { $code ^= $immedCode << 56; }
        # change the base code for constant versions of the op
        elsif (exists $constCodes{$capture})
            { $code ^= $constCodes{$capture} << 56; }

        # if capture group is an operand then process and add that data to code
        if (exists $operands{$capture})
        {
            # don't process the r20 that comes with the r39s20 capture
            unless ($capture eq 'r20' && exists $capData->{r39s20})
            {
                $code ^= $operands{$capture}->($capData->{$capture});
                push @$test, $capture if $test;
            }
        }

        # Add matching flags (an operand might also add/remove a flag)
        if (exists $flags->{$capture})
        {
            # a named multivalue flag
            if (ref $flags->{$capture})
            {
                $code ^= $flags->{$capture}{$capData->{$capture}};
                push @$test, "$capture:$capData->{$capture}" if $test;
            }
            # a simple exists flag
            else
            {
                $code ^= $flags->{$capture};
                push @$test, $capture if $test;
            }
        }
        elsif (!exists $operands{$capture} && !$test)
        {
            # Every capture group should be acted upon.  Missing one is a bug.
            warn "UNUSED: $op: $capture: $capData->{$capture}\n";
            warn Dumper($flags);
        }
    }

    return $code, $reuse;
}


my $CtrlRe = qr'(?<ctrl>[0-9a-fA-F\-]{2}:[1-6\-]:[1-6\-]:[\-yY]:[0-9a-fA-F])';
my $PredRe = qr'(?<pred>@!?(?<predReg>P\d)\s+)';
my $InstRe = qr"$PredRe?(?<op>\w+)(?<rest>[^;]*;)"o;
my $CommRe = qr'(?<comment>.*)';

sub processAsmLine
{
    my ($line, $lineNum) = @_;

    if ($line =~ m"^$CtrlRe(?<space>\s+)$InstRe$CommRe"o)
    {
        return {
            lineNum => $lineNum,
            pred    => $+{pred},
            predReg => $+{predReg},
            space   => $+{space},
            op      => $+{op},
            comment => $+{comment},
            inst    => normalizeSpacing($+{pred} . $+{op} . $+{rest}),
            ctrl    => readCtrl($+{ctrl}, $line),
        };
    }
    return undef;
}

sub processSassLine
{
    my $line = shift;

    if ($line =~ m"^\s+/\*(?<num>[0-9a-f]+)\*/\s+$InstRe\s+/\* (?<code>0x[0-9a-f]+)"o)
    {
        return {
            num     => hex($+{num}),
            pred    => $+{pred},
            op      => $+{op},
            ins     => normalizeSpacing($+{op} . $+{rest}),
            inst    => normalizeSpacing($+{pred} . $+{op} . $+{rest}),
            code    => hex($+{code}),
        };
    }
    return undef;
}

sub processSassCtrlLine
{
    my ($line, $ctrl, $ruse) = @_;

    return 0 unless $line =~ m'^\s+\/\* (0x[0-9a-f]+)';

    my $code = hex($1);
    if (ref $ctrl)
    {
        push @$ctrl, ($code & 0x000000000001ffff) >> 0;
        push @$ctrl, ($code & 0x0000003fffe00000) >> 21;
        push @$ctrl, ($code & 0x07fffc0000000000) >> 42;
    }
    if (ref $ruse)
    {
        push @$ruse, ($code & 0x00000000001e0000) >> 17;
        push @$ruse, ($code & 0x000003c000000000) >> 38;
        push @$ruse, ($code & 0x7800000000000000) >> 59;
    }
    return 1;
}

sub replaceXMADs
{
    my $file = shift;

# XMAD.LO d, a, b, c, x;
# ----------------------
# XMAD.MRG x, a, b.H1, RZ;
# XMAD d, a, b, c;
# XMAD.PSL.CBCC d, a.H1, x.H1, d;
# ----------------------
# XMAD d, a, 0xffff, c;
# XMAD.PSL d, a.H1, 0xffff, d;
    $file =~ s/\n\s*$CtrlRe(?<space>\s+)($PredRe)?XMAD\.LO\s+(?<d>\w+)\s*,\s*(?<a>\w+)\s*,\s*(?<b>c\[$hex\]\[$hex\]|\w+)\s*,\s*(?<c>c\[$hex\]\[$hex\]|\w+)\s*,\s*(?<x>\w+)\s*;$CommRe/

        die "XMAD.LO: Destination and first operand cannot be the same register ($+{d})." if $+{d} eq $+{a};
        sprintf '
%1$s%2$s%3$sXMAD.MRG %8$s, %5$s, %6$s.H1, RZ;%9$s
%1$s%2$s%3$sXMAD %4$s, %5$s, %6$s, %7$s;
%1$s%2$s%3$sXMAD.PSL.CBCC %4$s, %5$s.H1, %8$s.H1, %4$s;',
                @+{qw(ctrl space pred d a b c x comment)}
    /egmos;

    $file =~ s/\n\s*$CtrlRe(?<space>\s+)($PredRe)?XMAD(?<mod>(?:\.[SU]16)(?:\.[SU]16))?\.LO2\s+(?<d>\w+)\s*,\s*(?<a>\w+)\s*,\s*(?<b>-?$immed|\w+)\s*,\s*(?<c>c\[$hex\]\[$hex\]|\w+)\s*;$CommRe/

        die "XMAD.LO2: Destination and first operand cannot be the same register ($+{d})." if $+{d} eq $+{a};
        sprintf '
%1$s%2$s%3$sXMAD%9$s %4$s, %5$s, %6$s, %7$s;%8$s
%1$s%2$s%3$sXMAD%9$s.PSL %4$s, %5$s.H1, %6$s, %4$s;',
            @+{qw(ctrl space pred d a b c comment mod)}
    /egmos;

    $file =~ s/\n\s*$CtrlRe(?<space>\s+)($PredRe)?XMAD(?<mod>(?:\.[SU]16)(?:\.[SU]16))?\.LO2C\s+(?<d>\w+)\s*,\s*(?<a>\w+)\s*,\s*(?<b>c\[$hex\]\[$hex\]|\w+)\s*,\s*(?<c>\w+)\s*;$CommRe/

        die "XMAD.LO2C: Destination and first operand cannot be the same register ($+{d})." if $+{d} eq $+{a};
        sprintf '
%1$s%2$s%3$sXMAD%9$s %4$s, %5$s, %6$s, %7$s;%8$s
%1$s%2$s%3$sXMAD%9$s.PSL %4$s, %5$s, %6$s.H1, %4$s;',
            @+{qw(ctrl space pred d a b c comment mod)}
    /egmos;

    #TODO: add more XMAD macros
    return $file;
}
# convert extra spaces to single spacing to make our re's simplier
sub normalizeSpacing
{
    my $inst = shift;
    $inst =~ s/\t/ /g;
    $inst =~ s/\s{2,}/ /g;
    return $inst;
}


# map binary control notation on to easier to work with format.
sub printCtrl
{
    my $code = shift;

    my $stall = ($code & 0x0000f) >> 0;
    my $yield = ($code & 0x00010) >> 4;
    my $wrtdb = ($code & 0x000e0) >> 5;  # write dependency barier
    my $readb = ($code & 0x00700) >> 8;  # read  dependency barier
    my $watdb = ($code & 0x1f800) >> 11; # wait on dependency barier

    $yield = $yield ? '-' : 'Y';
    $wrtdb = $wrtdb == 7 ? '-' : $wrtdb + 1;
    $readb = $readb == 7 ? '-' : $readb + 1;
    $watdb = $watdb ? sprintf('%02x', $watdb) : '--';

    return sprintf '%s:%s:%s:%s:%x', $watdb, $readb, $wrtdb, $yield, $stall;
}
sub readCtrl
{
    my ($ctrl, $context) = @_;
    my ($watdb, $readb, $wrtdb, $yield, $stall) = split ':', $ctrl;

    $watdb = $watdb eq '--' ? 0 : hex $watdb;
    $readb = $readb eq '-'  ? 7 : $readb - 1;
    $wrtdb = $wrtdb eq '-'  ? 7 : $wrtdb - 1;
    $yield = $yield eq 'y' || $yield eq 'Y'  ? 0 : 1;
    $stall = hex $stall;

    die sprintf('wait dep out of range(0x00-0x3f): %x at %s',   $watdb, $context) if $watdb != ($watdb & 0x3f);

    return
        $watdb << 11 |
        $readb << 8  |
        $wrtdb << 5  |
        $yield << 4  |
        $stall << 0;
}

sub getRegNum
{
    my ($regMap, $regName) = @_;

    return !exists($regMap->{$regName}) || ref($regMap->{$regName}) ? $regName : $regMap->{$regName};
}

sub getVecRegisters
{
    my ($vectors, $capData) = @_;
    my $regName = $capData->{r0} or return;

    return if $regName eq 'RZ';

    if ($capData->{type} eq '.64' || $capData->{i31w4} eq '0x3')
    {
        if ($regName =~ m'^R(\d+)$')
        {
            return map "R$_", ($1 .. $1+1);
        }
        confess "$regName not a 64bit vector register" unless exists $vectors->{$regName};
        return @{$vectors->{$regName}}[0,1];
    }
    if ($capData->{type} eq '.128' || $capData->{i31w4} eq '0xf')
    {
        if ($regName =~ m'^R(\d+)$')
        {
            return map "R$_", ($1 .. $1+3);
        }
        confess "$regName not a 128bit vector register" unless exists($vectors->{$regName}) && @{$vectors->{$regName}} == 4;
        return @{$vectors->{$regName}};
    }
    return $regName;
}

sub getAddrVecRegisters
{
    my ($vectors, $capData) = @_;
    my $regName = $capData->{r8} or return;

    return if $regName eq 'RZ';

    if (exists $capData->{E})
    {
        if ($regName =~ m'^R(\d+)$')
        {
            return map "R$_", ($1 .. $1+1);
        }
        print Dumper($vectors) unless exists $vectors->{$regName};
        confess "$regName not a 64bit vector register" unless exists $vectors->{$regName};
        return @{$vectors->{$regName}}[0,1];
    }
    return $regName;
}

__END__



