blob: 7e95f3e4670b0b2a2b272577b05a2bae9f3380ae [file] [log] [blame] [edit]
#!/usr/bin/perl
# **********************************************************
# Copyright (c) 2014 Google, Inc. All rights reserved.
# **********************************************************
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# * Neither the name of Google, Inc. nor the names of its contributors may be
# used to endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
# DAMAGE.
# Feed this the text from the ARM manual for the A32 instructions.
my $verbose = 0;
my $line = 0;
my $pred = 1; # Process predicated instrs, or non-pred?
my $simd = 0; # Assume SIMD?
while ($#ARGV >= 0) {
if ($ARGV[0] eq '-nopred') {
$pred = 0;
} elsif ($ARGV[0] eq '-v') {
$verbose++;
} elsif ($ARGV[0] eq '-simd') {
$simd = 1;
} else {
die "Unknown argument $ARGV[0]\n";
}
shift;
}
while (<>) {
$line++;
chomp;
chomp if (/\r$/); # DOS
print "xxx $line $_\n" if ($verbose > 1);
startover:
if (/^Encoding A/ || /^Encoding ..\/A/) {
my $name;
my $asm;
while (<>) {
$line++;
chomp;
chomp if (/\r$/); # DOS
if (/^ARMv/) {
$flags .= "|v8" if (/^ARMv8/);
last;
} elsif (/^[A-Z].*<.*<.*>$/) {
# Sometimes the encoding is after the name in the .text version
goto at_name;
}
goto startover if (/^Encoding /); # some descriptions have Encoding A...
}
while (<>) {
$line++;
chomp;
chomp if (/\r$/); # DOS
next if (/^ARMv/);
next if ($_ !~ /^[A-Z][A-Z]/ && $_ !~ /^[A-Z]</);
last;
}
last if eof();
at_name:
if (/^(\w+)/) {
$name = $1;
$asm = $_;
} else {
print "unexpected asm on line $line: $_\n";
}
print "found $name: $asm\n" if ($verbose);
my $last = "";
while (<>) {
$line++;
chomp;
chomp if (/\r$/); # DOS
my $prefix = '';
if (!$pred) {
if ($last =~ /^31 30 29/ && /^1 1 1 1/) {
$prefix = "1 1 1 1";
}
} elsif (/^cond/) {
$prefix = "cond";
}
if ($prefix ne '') {
# We encode the "x x x P U {D,R} W S" specifiers either into
# our opcodes or we have multiple entries with encoding chains.
my $enc = $_;
if (/^$prefix\s+((\(?[01PUWSRDQi]\)? ){8})(.*)/) {
my $opc = $1;
my $rest = $3;
print "matched $name $enc\n" if ($verbose);
# Ignore parens: go w/ value inside.
$opc =~ s/\(//g;
$opc =~ s/\)//g;
generate_entry(lc($name), $asm, $enc, $opc, $rest, 0);
} elsif (/^$prefix\s+((\(?[01PUWSRDQi]\)? ){6})(.*)/) {
my $opc = $1 . "0 0";
my $rest = $3;
print "matched $name $enc\n" if ($verbose);
# Ignore parens: go w/ value inside.
$opc =~ s/\(//g;
$opc =~ s/\)//g;
generate_entry(lc($name), $asm, $enc, $opc, $rest, 0);
} elsif (/^$prefix\s+((\(?[01PUWSRDQi]\)? ){4})(.*)/) {
my $opc = $1 . "0 0 0 0";
my $rest = $3;
print "matched $name $enc\n" if ($verbose);
# Ignore parens: go w/ value inside.
$opc =~ s/\(//g;
$opc =~ s/\)//g;
generate_entry(lc($name), $asm, $enc, $opc, $rest, 0);
} else {
print "no match for $name: $_\n";
}
last;
}
goto startover if (/^Encoding /);
$last = $_;
}
}
}
sub generate_entry($,$,$,$,$,$)
{
my ($name, $asm, $enc, $opc, $rest, $PUW) = @_;
my $eflags = "x";
my $other_opc;
my $other_enc;
my $other_rest;
my $negative = 0;
# Handle "x x x P U {D,R} W S" by expanding the chars
my @bits = split(' ', $opc);
my $hexopc = 0;
for (my $i = 0; $i <= $#bits; $i++) {
if ($bits[$i] eq 'S') {
$other_opc = $opc;
$other_opc =~ s/S/0/;
generate_entry($name, $asm, $enc, $other_opc, $rest, $PUW);
$name .= "s";
$bits[$i] = '1';
$eflags = "fWNZCV";
} elsif ($bits[$i] eq 'P') {
$PUW = 1;
$other_opc = $opc;
$other_opc =~ s/P/0/;
generate_entry($name, $asm, $enc, $other_opc, $rest, $PUW);
$opc =~ s/P/1/;
$bits[$i] = '1';
} elsif ($bits[$i] eq 'U') {
$PUW = 1;
$other_opc = $opc;
$other_opc =~ s/U/0/;
generate_entry($name, $asm, $enc, $other_opc, $rest, $PUW);
$opc =~ s/U/1/;
$bits[$i] = '1';
$negative = 1;
} elsif ($bits[$i] eq 'W') {
$PUW = 1;
$other_opc = $opc;
$other_opc =~ s/W/0/;
generate_entry($name, $asm, $enc, $other_opc, $rest, $PUW);
$bits[$i] = '1';
$opc =~ s/W/1/;
} elsif ($bits[$i] eq 'D' || $bits[$i] eq 'R' || $bits[$i] eq 'i') {
$other_opc = $opc;
$other_opc =~ s/$bits[$i]/0/;
generate_entry($name, $asm, $enc, $other_opc, $rest, $PUW);
$opc =~ s/$bits[$i]/1/;
$bits[$i] = '1';
} elsif ($bits[$i] eq 'Q') {
$other_opc = $opc;
$other_opc =~ s/Q/0/;
generate_entry($name, $asm, $enc, $other_opc, $rest, $PUW);
$bits[$i] = '1';
$opc =~ s/Q/1/;
$rest =~ s/ V/ VQ/g;
}
if ($bits[$i] eq '1' || $bits[$i] eq '0') {
$hexopc |= $bits[$i] << (27 - $i);
} else {
die "invalid code $bits[$i]\n";
}
}
# Floating-point precision bit: bit 8 == "sz"
if ($simd && $enc =~ / sz /) {
$other_name = $name . ".f32";
$other_enc = $enc;
$other_enc =~ s/ sz / sz=0 /;
generate_entry($other_name, $asm, $other_enc, $opc, $rest, $PUW);
$name .= ".f64";
$enc =~ s/ sz / sz=1 /;
$hexopc |= 0x100;
}
# For SIMD, Q bit is down low
if ($simd && $rest =~ / Q /) {
$other_rest = $rest;
$other_rest =~ s/Q //;
$other_rest =~ s/Vn/VAq/;
$other_rest =~ s/Vd/VBq/;
$other_rest =~ s/Vm/VCq/;
generate_entry($name, $asm, $enc, $opc, $other_rest, $PUW);
$rest =~ s/Q //;
$rest =~ s/Vn/VAdq/;
$rest =~ s/Vd/VBdq/;
$rest =~ s/Vm/VCdq/;
$hexopc |= 0x40;
}
# Data type: "<dt>" or "<size>"
if ($simd && $enc =~ / size /) {
# We bail on the precise hex encoding: we just try to pre-generate
# entries that can be manually tweaked
my @subtypes;
if ($asm =~ /.<dt>/) {
if ($enc =~ / U /) {
@subtypes = ('s8', 's16', 's32', 'u8', 'u16', 'u32');
} else {
@subtypes = ('i8', 'i16', 'i32', 'i64');
}
} else {
@subtypes = ('8', '16', '32', '64');
}
$rest =~ s/size\s*//;
foreach my $sub (@subtypes) {
$other_name = $name . "." . $sub;
$other_enc = $enc;
$other_enc =~ s/ size / size=$sub /;
generate_entry($other_name, $asm, $other_enc, $opc, $rest, $PUW);
}
}
if (!$pred) {
$hexopc |= 0xf0000000;
}
$opname = $name;
$opname =~ s/\./_/g;
$opname .= ",";
$name .= "\",";
printf " {OP_%-8s 0x%08x, \"%-8s ", $opname, $hexopc, $name;
# Clean up extra spaces, parens, digits
$enc =~ s/\s\s+/ /g;
$rest =~ s/\s\s+/ /g;
$rest =~ s/\(//g;
$rest =~ s/\)//g;
$rest =~ s/\s\d+\s/ /g;
# Put Rd or Rt first, as dst
$rest =~ s/(.*) (R[dt])/\2 \1/;
# Put shift last, in disasm order
$rest =~ s/imm5 type (.*)/\1 type imm5/;
$rest =~ s/Rs type (.*)/\1 type Rs/;
# Rn is (usually) before Rm
$rest =~ s/Rm (.*) Rn/Rn \1 Rm/;
# Names of types
$rest =~ s/imm(\d+)/i\1/g;
$rest =~ s/type/sh2/g;
$rest =~ s/Rm/-Rm/ if ($negative);
# Get the 2nd empty dest in there for SIMD with Q.
# XXX: do the same for the others!
$rest =~ s/(VA\w+) (VB\w+)/\2 xx \1/;
my @opnds = split(' ', $rest);
my $opcnt = 0;
for (my $i = 0; $i <= $#opnds; $i++) {
if ($opnds[$i] ne '0' && $opnds[$i] ne '1' &&
(!$simd || ($opnds[$i] ne 'sz' && $opnds[$i] ne 'N' &&
$opnds[$i] ne 'M' && $opnds[$i] ne 'F'))) {
print "$opnds[$i], ";
$opcnt++;
}
}
for (my $i = $opcnt; $i < 5; $i++) {
print "xx, ";
}
print ($pred ? "pred" : "no");
print ", $eflags, END_LIST},";
if ($PUW) {
$PUW_str = $bits[3] . $bits[4] . $bits[6];
print "/*PUW=$PUW_str*/";
}
print "/* ($asm) */ /* <$enc> */\n";
}