blob: a0fc8bcd728829e63618275760d12d942bde02f1 [file] [log] [blame]
#!/usr/bin/perl
# **********************************************************
# Copyright (c) 2014-2020 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.
# Pass this a set of decoding table files from which to extract
# INSTR_CREATE_ macro signature information.
# It will construct the different sets of macros needed for each
# instruction form.
# It has assumptions on the precise format of the decoding tables and
# on the decoding table file names (specifically, that *t32* is Thumb).
#
# To run on one opcode:
#
# tools/arm_macros_gen.pl -v -o OP_add core/ir/arm/table_{a32,t32}*.[ch]
#
# To run on everything:
#
# tools/arm_macros_gen.pl core/ir/arm/table_{a32,t32}*.[ch]
#
# XXX i#1563: should we swap src and dst for stores and for OP_cdp and
# other instructions to more closely match the assembly order?
#
# XXX i#1563: currently we have "Rd" for the dest of a load but asm
# has it as "Rt".
my $verbose = 0;
die "Usage: $0 [-o OP_<opcode>] <table-files>\n" if ($#ARGV < 1);
my $single_op = '';
if ($ARGV[0] eq '-v') {
shift;
$verbose++;
}
if ($ARGV[0] eq '-o') {
shift;
$single_op = shift;
}
my @infiles = @ARGV;
# Must process headers first
@infiles = sort({return -1 if($a =~ /\.h$/);return 1 if($b =~ /\.h$/); return 0;}
@infiles);
foreach $infile (@infiles) {
print "Processing $infile\n" if ($verbose > 0);
my $extra_num = 0;
my $thumb = ($infile =~ /t32/) ? 1 : 0;
open(INFILE, "< $infile") || die "Couldn't open $file\n";
while (<INFILE>) {
print "xxx $_\n" if ($verbose > 2);
if (/^\s*{(OP_\w+)[ ,]/ && ($single_op eq '' || $single_op eq $1) &&
$1 ne 'OP_CONTD') {
my $opc = $1;
$instance{$opc} = 0 if (!defined($instance{$opc}));
my $encoding = $_;
my $flags = '';
# Get the 5 operands plus the flags and exop chain
if ($encoding =~ /exop\[(\w+)\]},/) {
$entry{$opc}[$instance{$opc}]{'exop'} = hex($1);
}
$encoding =~ s|/\*dup\*/||;
$encoding =~ s/^[^"]+"\S+",\s*//;
$encoding =~ s/,[^,]+,[^,]+}.*$//;
$encoding =~ s/\s//g;
if ($encoding =~ /,([^,]+)$/) {
$flags = $1;
if ($flags =~ /(xop_\w+)/) {
$entry{$opc}[$instance{$opc}]{'xop'} = $1;
}
$encoding =~ s/,[^,]+$//;
} else {
die "Cannot find flags: $_";
}
my $opstr = collapse_types($encoding, $infile);
my @opnds = split(',', $opstr);
my @dsts = ();
my @srcs = ();
push(@dsts, $opnds[0]) unless ($opnds[0] eq 'xx');
if ($flags =~ /srcX4/) {
push(@srcs, $opnds[1]) unless ($opnds[1] eq 'xx');
} else {
push(@dsts, $opnds[1]) unless ($opnds[1] eq 'xx');
}
if ($flags =~ /dstX3/) {
push(@dsts, $opnds[2]) unless ($opnds[2] eq 'xx');
} else {
push(@srcs, $opnds[2]) unless ($opnds[2] eq 'xx');
}
push(@srcs, $opnds[3]) unless ($opnds[3] eq 'xx');
push(@srcs, $opnds[4]) unless ($opnds[4] eq 'xx');
$entry{$opc}[$instance{$opc}]{'line'} = $_;
$entry{$opc}[$instance{$opc}]{'encoding'} = $encoding;
$entry{$opc}[$instance{$opc}]{'opstr'} = $opstr;
@{$entry{$opc}[$instance{$opc}]{'srcs'}} = @srcs;
@{$entry{$opc}[$instance{$opc}]{'dsts'}} = @dsts;
$entry{$opc}[$instance{$opc}]{'file'} = $infile;
if ($verbose > 0) {
print "$opstr <== $_";
}
$instance{$opc}++;
} elsif (/^\s*{OP_CONTD/) {
my $opnds = $_;
die "Two extra-op lines not supported: $_" unless ($opnds =~ /END_LIST/);
if ($opnds =~ /",(\s*\w+,\s*\w+,\s*\w+,\s*\w+,\s*\w+,)/) {
my $encoding = collapse_types($1, $infile);
my @opnds = split(',', $encoding);
my @dsts = ();
my @srcs = ();
push(@dsts, $opnds[0]) unless ($opnds[0] eq 'xx');
push(@dsts, $opnds[1]) unless ($opnds[1] eq 'xx');
push(@srcs, $opnds[2]) unless ($opnds[2] eq 'xx');
push(@srcs, $opnds[3]) unless ($opnds[3] eq 'xx');
push(@srcs, $opnds[4]) unless ($opnds[4] eq 'xx');
# Separate extra-args for Thumb and ARM
$extra{$thumb}[$extra_num]{'line'} = $_;
@{$extra{$thumb}[$extra_num]{'srcs'}} = @srcs;
@{$extra{$thumb}[$extra_num]{'dsts'}} = @dsts;
} else {
die "Failed to parse extra-op line $_";
}
$extra_num++;
}
}
close(INFILE);
}
foreach my $opc (keys %entry) {
my %sigs = ();
my $check_list = 0;
my $shift_variant = '';
my $shift_arg_str = '';
my $shift_call_str = '';
my $arg_str = '';
for (my $i = 0; $i < @{$entry{$opc}}; $i++) {
# Get extra args
my $infile = $entry{$opc}[$i]{'file'};
my $thumb = ($infile =~ /t32/) ? 1 : 0;
if (defined($entry{$opc}[$i]{'xop'})) {
my $xop = $entry{$opc}[$i]{'xop'};
for (my $j = 0; $j < @{$extra{$thumb}}; $j++) {
if ($extra{$thumb}[$j]{'line'} =~ /$xop\W/) {
push(@{$entry{$opc}[$i]{'srcs'}}, @{$extra{$thumb}[$j]{'srcs'}});
push(@{$entry{$opc}[$i]{'dsts'}}, @{$extra{$thumb}[$j]{'dsts'}});
}
}
} elsif (defined($entry{$opc}[$i]{'exop'})) {
my $exop = $entry{$opc}[$i]{'exop'};
push(@{$entry{$opc}[$i]{'srcs'}}, @{$extra{$thumb}[$exop]{'srcs'}});
push(@{$entry{$opc}[$i]{'dsts'}}, @{$extra{$thumb}[$exop]{'dsts'}});
}
}
# Prefer the ARM register names, as the Thumb ones are often completely
# different, and it would be a real pain to add another set of
# fragile, hacky mappings to "semantic names".
# We want to map from each Thumb encoding to a "similar" ARM encoding.
# We can't do global reg slots b/c they do vary for wb vs non-wb so we
# store the ARM reg for each slot per operand count.
# First, store the ARM reg values:
my %arm_src = ();
my %arm_dst = ();
for (my $i = 0; $i < @{$entry{$opc}}; $i++) {
if ($entry{$opc}[$i]{'file'} !~ /t32/) {
my $count = @{$entry{$opc}[$i]{'srcs'}};
for (my $j = 0; $j < $count; $j++) {
if ($entry{$opc}[$i]{'srcs'}[$j] =~ /\bR/) {
if (defined($arm_src{$count}{$j})) {
die "Src regs for $opc in slot x$count $j do not match: ".
$arm_src{$count}{$j} ." vs ".
$entry{$opc}[$i]{'srcs'}[$j] ."\n"
unless ($arm_src{$count}{$j} eq $entry{$opc}[$i]{'srcs'}[$j]);
}
$arm_src{$count}{$j} = $entry{$opc}[$i]{'srcs'}[$j];
}
}
$count = @{$entry{$opc}[$i]{'dsts'}};
for (my $j = 0; $j < $count; $j++) {
if ($entry{$opc}[$i]{'dsts'}[$j] =~ /\bR/) {
if (defined($arm_dst{$j})) {
die "Dst regs for $opc in slot $j do not match: ".
$arm_dst{$count}{$j} ." vs ".
$entry{$opc}[$i]{'dsts'}[$j] ."\n"
unless ($arm_dst{$count}{$j} eq $entry{$opc}[$i]{'dsts'}[$j]);
}
$arm_dst{$count}{$j} = $entry{$opc}[$i]{'dsts'}[$j];
}
}
}
}
# Now, substitute the ARM reg values into the corresponding Thumb slots:
for (my $i = 0; $i < @{$entry{$opc}}; $i++) {
if ($entry{$opc}[$i]{'file'} =~ /t32/) {
my $count = @{$entry{$opc}[$i]{'srcs'}};
for (my $j = 0; $j < $count; $j++) {
if (($entry{$opc}[$i]{'srcs'}[$j] =~ /\bR/ ||
$entry{$opc}[$i]{'srcs'}[$j] =~ /\bSPw/) &&
defined($arm_src{$count}{$j})) {
print "Setting src $j from ".$entry{$opc}[$i]{'srcs'}[$j].
" to ". $arm_src{$count}{$j} ."\n" if ($verbose > 1);
$entry{$opc}[$i]{'srcs'}[$j] = $arm_src{$count}{$j};
}
}
$count = @{$entry{$opc}[$i]{'dsts'}};
for (my $j = 0; $j < $count; $j++) {
if (($entry{$opc}[$i]{'dsts'}[$j] =~ /\bR/ ||
$entry{$opc}[$i]{'dsts'}[$j] =~ /\bSPw/) &&
defined($arm_dst{$count}{$j})) {
print "Setting dst $j from ".$entry{$opc}[$i]{'dsts'}[$j].
" to ". $arm_dst{$count}{$j} ."\n" if ($verbose > 1);
$entry{$opc}[$i]{'dsts'}[$j] = $arm_dst{$count}{$j};
}
}
}
}
for (my $i = 0; $i < @{$entry{$opc}}; $i++) {
# Create a single-string signature line
my $sig = join " ",@{$entry{$opc}[$i]{'dsts'}};
$sig .= "; ";
$sig .= join " ",@{$entry{$opc}[$i]{'srcs'}};
# Combine first and consec into single var-arg list
$sig =~ s/WBd LCd/LCd/;
$sig =~ s/VBq LCq/LCq/;
# All types of lists look the same
$sig =~ s/LX\w+/LX/;
$sig =~ s/LC\w+/LCx/;
# We collapse SIMD reg sizes
$sig =~ s/([VW][A-Z]+)[a-z]+/\1x/g;
# We use V for everything like AArch64 does (XXX i#1563: could be nice
# to list the S or D name for asm but we are collapsing sizes...)
$sig =~ s/W/V/g;
# Special case vmrs
$sig =~ s/\bCPSR/RBd/ if ($opc eq 'OP_vmrs');
# Collapse spsr and cpsr
$sig =~ s/\bCPSR/statreg/;
$sig =~ s/\bSPSR/statreg/;
if (!defined($sigs{$sig})) {
$sigs{$sig} = $sig;
$check_list = 1 if ($sig =~ /\bL[XC]/);
$shift_variant = $sig if (($sig =~ /\ssh2 i/ || $sig =~ /\ssh2 R/) &&
# only arith srcs, not shifts in mem opnds
$sig !~ /M/);
}
print "Entry #$i: $sig\n" if ($verbose > 0);
}
if ($check_list) {
# Avoid a separate instance for singleton vs list of regs
my @ksig = keys %sigs;
foreach my $sig (@ksig) {
# We have to replace in this direction b/c there can be multiple single reg
# opnds and we only want the one that lines up w/ the list
if ($sig =~ /\bLX/) {
my $sig_singleB = $sig;
my $sig_singleA = $sig;
$sig_singleB =~ s/\bLX\w*/VBx/; # V[AB]x only singleton type so far
$sig_singleA =~ s/\bLX\w*/VAx/; # V[AB]x only singleton type so far
if (defined($sigs{$sig_singleB})) {
delete $sigs{$sig_singleB};
} elsif (defined($sigs{$sig_singleA})) {
delete $sigs{$sig_singleA};
}
}
}
}
my $immed_name = 0;
my @ksig = keys %sigs;
if ($#ksig == 1) {
if (($ksig[0] =~ /\bi/ && $ksig[1] !~ /\bi/) ||
($ksig[1] =~ /\bi/ && $ksig[0] !~ /\bi/)) {
# See if have same # args and thus candidate for "Rm_or_immed"
my @sp0 = $ksig[0] =~ / /g;
my @sp1 = $ksig[1] =~ / /g;
if ($#sp0 == $#sp1 &&
# Rule out FPSCR vs immed (OP_vmrs).
!($ksig[0] =~ /; FPSCR/ && $ksig[1] =~ /; i/) &&
!($ksig[0] =~ /; i/ && $ksig[1] =~ /; FPSCR/)) {
# Distinguished by immed but let's make the macros more versatile
# by combining into a variable-type "Rm_or_immed" arg rather than
# naming $name_imm.
my $newsig;
$newsig = ($ksig[0] =~ /\bi/) ? $ksig[1] : $ksig[0];
$newsig =~ s/\s*$/_or_imm/;
delete $sigs{$ksig[0]};
delete $sigs{$ksig[1]};
$sigs{$newsig} = $newsig;
} else {
$immed_name = 1;
}
}
}
if ($shift_variant ne '') {
# Add a simple version w/o the shifted reg
my $replaced = 0;
foreach my $sig (keys %sigs) {
# When we add a simple reg-reg DR_SHIFT_NONE, and a reg-imm or reg-reg
# exists, combine them into a variable-type "Rm_or_immed" arg
# rather than forcing the reg-imm to be $name_imm.
if ($sig =~ /^R\w+;\s*\bi$/ ||
$sig =~ /^;\s*R\w+\s*\bi$/ ||
$sig =~ /^;\s*R\w+\s*R\w+$/ ||
$sig =~ /^R\w+;\s*R\w+$/ ||
$sig =~ /^R\w+;\s*R\w+\s*\bi$/ ||
$sig =~ /^R\w+;\s*R\w+\s*\bR\w+$/) {
my $newsig = $sig;
my $with_reg = $shift_variant;
$with_reg =~ s/\s*sh2\s*\w+$//;
my $check = $with_reg;
$check =~ s/R\w+\s*$//;
if ($newsig =~ /\bi/) {
$newsig =~ s/\bi\s*$//;
} else {
$newsig =~ s/\bR\w+\s*$//;
}
die "Unknown shift variant $opc |$shift_variant|: |$check| vs |$newsig|\n"
unless ($check eq $newsig);
$newsig = $with_reg . "_or_imm_specialshift";
delete $sigs{$sig};
$sigs{$newsig} = $newsig;
$replaced = 1;
}
}
if (!$replaced) {
my $newsig = $shift_variant;
$sigs{$newsig} = $newsig;
$newsig =~ s/\s*sh2\s*\w+$/_specialshift/;
$sigs{$newsig} = $newsig;
}
}
foreach my $sig (sort keys %sigs) {
print "Sig: $sig\n" if ($verbose > 0);
my $name = $opc;
$name =~ s/^OP_//;
# Friendly names for things we don't care about for macro names
$sig =~ s/\bi\w*/imm/g;
$sig =~ s/\bj\w*/pc/g;
$sig =~ s/\bM/mem/g;
$sig =~ s/\bsh\d/shift/g;
$sig =~ s/\bSPw/sp/g;
$sig =~ s/\bCR\w+/cpreg/g;
# Ordinals for dup names
$sig =~ s/imm\b(.*)imm\b(.*)imm\b/imm\1imm2\2imm3/;
$sig =~ s/imm\b(.*)imm\b/imm\1imm2/;
$sig =~ s/cpreg\b(.*)cpreg\b(.*)cpreg\b/cpreg\1cpreg2\2cpreg3/;
$sig =~ s/cpreg\b(.*)cpreg\b/cpreg\1cpreg2/;
# Total arg counts
my $sig_src = $sig;
$sig_src =~ s/^.*;//;
my @count = $sig_src =~ /\w+/g;
my $num_tot_srcs = scalar @count;
my $sig_dst = $sig;
$sig_dst =~ s/;.*$//;
@count = $sig_dst =~ /\w+/g;
my $num_tot_dsts = scalar @count;
# String for explicit args
my $esig = $sig;
# Look for writeback => name and implicit args
if ($sig =~ /\bmem/ &&
($sig =~ /RAw.*;.*RAw/ || $sig =~ /sp;.*sp/) &&
$num_tot_dsts > 0) {
# For us, writeback or post-indexed look the same. We have
# two variants: immed disp or index (possibly shifted) reg.
if ($sig =~ /;.*RD/) {
$name .= "_wbreg" unless (scalar(keys %sigs) == 1);
} elsif ($sig =~ /;.*\bi/) {
$name .= "_wbimm" unless (scalar(keys %sigs) == 1);
} else {
$name .= "_wb" unless (scalar(keys %sigs) == 1);
}
if ($sig =~ /sp;.*sp/) {
$sig =~ s/sp/opnd_create_reg(opnd_get_base(mem))/g;
$esig =~ s/\s*sp//g;
} else {
$sig =~ s/RAw/opnd_create_reg(opnd_get_base(mem))/g;
$esig =~ s/\s*RAw//g;
}
$sig =~ s/RDw/opnd_create_reg_ex(opnd_get_reg(Rm) 0 DR_OPND_SHIFTED)/;
} elsif ($sig =~ /\bshift R/) {
$name .= "_shreg";
$sig =~ s/RDw/opnd_create_reg_ex(opnd_get_reg(Rm) 0 DR_OPND_SHIFTED)/;
} elsif ($sig =~ /\bshift imm/) {
$name .= "_shimm";
$sig =~ s/RDw/opnd_create_reg_ex(opnd_get_reg(Rm) 0 DR_OPND_SHIFTED)/;
} elsif ($immed_name && $sig =~ /\bimm/) {
$name .= "_imm";
} elsif ($sig =~ /\bSPSR/) {
$name .= "_spsr";
$sig =~ s/SPSR/opnd_create_reg(DR_REG_SPSR)/g;
$esig =~ s/\s*SPSR//g;
} elsif ($sig =~ /\bSPSR/) {
$name .= "_spsr";
$sig =~ s/SPSR/opnd_create_reg(DR_REG_SPSR)/g;
$esig =~ s/\s*SPSR//g;
} elsif ($sig =~ /RAw RBw; RAw RBw/) {
# OP_smlal{b,t}{b,t}
$esig =~ s/; RAw RBw/;/g;
} elsif ($sig =~ /VC\d._/) {
# Things like OP_vmull, OP_vmls
$name .= "_scalar";
} elsif ($opc eq 'OP_msr' && $sig =~ /\bimm2$/) {
# Distinguished by immed
$name .= "_imm";
} elsif (($opc eq 'OP_msr_priv' || $opc eq 'OP_mrs_priv') &&
$sig =~ /\bstatreg\b/) {
# Distinguished by immed
$name .= "_spsr";
} elsif ($opc =~ /OP_cpsi/ && $sig =~ /\bimm.*\bimm/) {
$name .= "_noflags";
} elsif ($opc =~ /OP_vmov_32$/) {
if ($sig =~ /V.*;/) {
$name .= "_g2s";
} else {
$name .= "_s2g";
}
} elsif ($opc eq 'OP_vmov') {
# Using C++ function overloads would help here.
# XXX: could we create a vararg version? But how know #dsts?
# User passes it in and we add instr_create_Ndst_varsrc()?
if ($sig =~ /^V\w+;\s*R\w+$/) {
$name .= "_g2s";
} elsif ($sig =~ /^R\w+;\s*V\w+$/) {
$name .= "_s2g";
} elsif ($sig =~ /^V\w+;\s*R\w+\s*R/) {
$name .= "_gg2s";
} elsif ($sig =~ /^R\w+\s*R\w+;\s*V\w+$/) {
$name .= "_s2gg";
} elsif ($sig =~ /^V\w+\s*V\w+;\s*R\w+\s*R/) {
$name .= "_gg2ss";
} else {
$name .= "_ss2gg";
}
} elsif (($opc =~ /^OP_stc/ || $opc =~ /^OP_ldc/) &&
$sig eq 'cpreg; mem imm imm2') {
$name .= "_option";
} elsif ($esig =~ /RBw; .* RBw$/) {
# Implicit arg for OP_bfc and OP_bfi
$esig =~ s/RBw$//;
} elsif ($esig =~ /mem.*; mem/) {
# Implicit arg for OP_swp*
$esig =~ s/^mem//;
}
if ($sig =~ /\bshift\b/) {
$sig =~ s/\bshift/opnd_add_flags(shift DR_OPND_IS_SHIFT)/;
}
# Hardcoded implicit args:
# SPw, LSL, and ASR, and the k8, k16, k32 consts are explicit in the asm.
# LRw for OP_bl is not.
if ($sig =~ /\bLRw/) {
$sig =~ s/LRw/opnd_create_reg(DR_REG_LR)/;
$esig =~ s/\s*LRw//g;
}
if ($sig =~ /\bFPSCR/) {
$sig =~ s/FPSCR/opnd_create_reg(DR_REG_FPSCR)/;
$esig =~ s/\s*FPSCR//g;
}
# Special docs
if ($opc eq 'OP_msr' && $sig !~ /imm2/) {
$sig =~ s/imm/imm_msr/;
$esig =~ s/imm/imm_msr/;
}
$sig = rename_regs($sig);
$esig = rename_regs($esig);
# List of total (including implicit) args
my $call_str = $sig;
$call_str =~ s/;//g;
$call_str =~ s/\s*$//;
$call_str =~ s/^\s*//;
$call_str =~ s/\b(\w+)/, \1/g;
$call_str =~ s/\b([\w\(\)]+)\s+/\1/g;
$call_str =~ s/\b([A-Za-z0-9_]+)\b/(\1)/g;
$call_str =~ s/\((opnd_\w+)\)/\1/g;
$call_str =~ s/\((\d+)\)/\1/g;
$call_str =~ s/\((DR_[A-Z_]+)\)/\1/g;
$call_str =~ s/\(, /(/g;
$call_str =~ s/\(\((\w+)\)\)/(\1)/g;
# List of explicit args
$arg_str = $esig;
$arg_str =~ s/;//g;
$arg_str =~ s/\s*$//g;
$arg_str =~ s/^\s*//g;
$arg_str =~ s/\b(\w+)/, \1/g;
$arg_str =~ s/\b(\w+)\s+/\1/g;
my $opc_str = $opc;
my $func_sfx = '';
# List => vararg with list at end of course
if ($sig =~ /\bL/) {
# Figure out at which index in src/dst list the vararg should be inserted
my $idx = 0;
my @ops = split(' ', $sig);
for (my $i = 0; $i <= $#ops; $i++) {
last if ($ops[$i] =~ /^L/);
$idx++;
$idx = 0 if ($ops[$i] =~ /;$/);
}
$arg_str =~ s/, L\w+//;
$call_str =~ s/, \(L\w+\)//;
$arg_str .= ", list_len, ...";
$call_str .= ", __VA_ARGS__";
if ($sig =~ /;.*\bL/) {
$func_sfx = '_varsrc';
$opc_str .= ", $num_tot_dsts, ".($num_tot_srcs-1);
} else {
$func_sfx = '_vardst';
$opc_str .= ", ".($num_tot_dsts-1).", $num_tot_srcs";
}
$opc_str .= ", list_len, $idx";
$num_tot_dsts = 'N';
$num_tot_srcs = 'M';
}
print " Macro sigs: ($esig) | $sig\n" if ($verbose > 0);
die "XXXX Duplicate macro $name for |$sig|\n" if (defined($dupcheck{$name}));
$dupcheck{$name} = 1;
if ($sig =~ /_specialshift/ && $sig !~ /_imm_specialshift/) {
$sig =~ s/_specialshift//;
$arg_str =~ s/_specialshift//;
$call_str =~ s/_specialshift/, DR_SHIFT_NONE, 0/;
}
if ($sig =~ /_or_imm_specialshift/) {
$sig =~ s/_specialshift//;
$arg_str =~ s/_specialshift//;
$call_str =~ s/_specialshift//;
$arg_str =~ /\s+(\S+)$/;
$last_arg = $1;
my $mac = sprintf "#define INSTR_CREATE_%s(dc%s) \\\n".
" (opnd_is_reg(%s) ? \\\n".
" INSTR_CREATE_%s_shimm((dc)%s, \\\n".
" OPND_CREATE_INT8(DR_SHIFT_NONE), OPND_CREATE_INT8(0)) : \\\n".
" instr_create_%sdst_%ssrc%s((dc), %s%s))\n",
$name, $arg_str, $last_arg, $name, $call_str, $num_tot_dsts,
$num_tot_srcs, $func_sfx, $opc_str, $call_str;
push(@{$macro{$arg_str}}, ($mac));
} else {
my $mac = sprintf "#define INSTR_CREATE_%s(dc%s) \\\n".
" instr_create_%sdst_%ssrc%s((dc), %s%s)\n",
$name, $arg_str, $num_tot_dsts, $num_tot_srcs, $func_sfx,
$opc_str, $call_str;
push(@{$macro{$arg_str}}, ($mac));
}
}
}
foreach my $args (keys %macro) {
$order{$args} = order_sig($args);
}
my @order = sort({$order{$a} <=> $order{$b}} keys %macro);
my %mapping = ('reg' => 'register',
'Rd' => 'destination register',
'Rd2' => 'second destination register',
'Rn' => 'source register',
'Rn_or_imm' => 'source register, or integer constant,',
'Rt' => 'source register',
'Rt2' => 'second source register',
'Rm' => 'second source register',
'Rm_or_imm' => 'second source register, or integer constant,',
'Ra' => 'third source register',
'Rs' => 'third source register',
'Vd' => 'destination SIMD register',
'Vd2' => 'second destination register',
'Vn' => 'source SIMD register',
'Vt' => 'source SIMD register',
'Vt2' => 'second source SIMD register',
'Vm' => 'second source SIMD register',
'Vm2' => 'third source SIMD register',
'Vm_or_imm' => 'source SIMD register, or integer constant,',
'Vn_or_imm' => 'source SIMD register, or integer constant,',
'Vs' => 'third source SIMD register',
'imm' => 'integer constant',
'imm_msr' => 'integer constant (typically from OPND_CREATE_INT_MSR*)',
'imm2' => 'second integer constant',
'imm3' => 'third integer constant',
'pc' => 'program counter constant',
'mem' => 'memory',
'shift' => 'dr_shift_type_t integer constant',
'cpreg' => 'coprocessor register',
'cpreg2' => 'second coprocessor register',
'cpreg3' => 'third coprocessor register',
'statreg' => 'status register (usually DR_REG_CPSR)',
'sp' => 'stack pointer');
foreach my $args (@order) {
print '
/** @name Signature: ';
my $sigline = "($args)";
$sigline =~ s/\(, /(/;
print $sigline;
print ' */
/** @{ */ /* start doxygen group (via DISTRIBUTE_GROUP_DOC=YES). */
/**
* This INSTR_CREATE_xxx macro creates an instr_t with opcode OP_xxx and
* the given explicit operands, automatically supplying any implicit operands.
* The operands should be listed with destinations first, followed by sources.
* The ordering within these two groups should follow the conventional
* assembly ordering.
* \param dc The void * dcontext used to allocate memory for the instr_t.
';
$sigline =~ s/\(//;
$sigline =~ s/\)//;
$sigline =~ s/\s*//g;
my @params = split(',', $sigline);
my %count;
my $saw_n = 0;
foreach my $param (@params) {
$saw_n = 1 if ($param =~ /[VR]n/);
my $tomap = $param;
# Singleton src => no "second"
$tomap =~ s/m/n/ if ($param =~ /[VR]m/ && !$saw_n);
if ($tomap eq 'list_len') {
print " * \\param $param The number of registers in the register list.\n";
} elsif ($tomap eq '...') {
print " * \\param $param The register list as separate opnd_t arguments.\n";
print " * The registers in the list must be in increasing order.\n";
} else {
die "XXXX No mapping for $param\n" if (!defined($mapping{$tomap}));
my $full = $mapping{$tomap};
$count{$param}++;
die "XXXX Duplicate name $param\n" unless ($count{$param} == 1);
print " * \\param $param The $full opnd_t operand.\n";
}
}
print " */\n";
foreach my $mac (sort @{$macro{$args}}) {
print $mac;
}
print '/** @} */ /* end doxygen group */
';
}
sub collapse_types($, $)
{
my ($str, $infile) = @_;
$str =~ s/\s//g;
# Collapse opnds that will look identical to the macro and for which
# we don't need to distinguish later
$str =~ s/\bM\w+/M/g;
$str =~ s/\bn\w+/i/g; # Negative immed == positive
$str =~ s/\bi\w+/i/g; # Type of immed doesn't matter
$str =~ s/\bj\w+/j/g; # Type of jump immed doesn't matter
$str =~ s/\bro2\w*/i/; # Rotate-by
$str =~ s/\bk\w+/i/; # Hardcoded const => immed (all are explicit in asm)
$str =~ s/\b(R\w)N(\w)/\1\2/g; # User must negate registers
$str =~ s/\bLSL/shift/g;
$str =~ s/\bASR/shift/g;
$str =~ s/\bL[\dPL]+/L/;
# Thumb-only rules
if ($infile =~ /t32/) {
# This is only there when encoding
$str =~ s/RA_EQ_D\w+/xx/;
# We're not adding special macros for implicit regs or for
# destructive src==dst forms. We assume there's a general form
# (sometimes only w/ our special DR_SHIFT_NONE implicit) for each.
$str =~ s/,RV/,RA/; # destructive src => diff name (no special macro for it)
$str =~ s/^SPw/RBw/;
$str =~ s/\bSPw/RAw/ unless ($str =~ /\bM/); # don't do this for srs
$str =~ s/\bPC/RA/;
# We map Thumb names to the corresponding ARM names
$str =~ s/sh2_4/sh2/;
$str =~ s/sh1_21/sh1/;
$str =~ s/^RCw/RBw/; # T32.W often have RCw as dst
$str =~ s/\bRU/RD/g;
$str =~ s/\bRV/RB/g;
$str =~ s/\bRW/RA/g;
$str =~ s/\bRX/RD/g;
$str =~ s/\bRY/RA/g;
$str =~ s/^RZ/RB/g;
$str =~ s/\bRZ/RA/g;
}
return $str;
}
sub order_sig($)
{
my ($a) = @_;
my $ord = 0;
$ord += 100000000 if ($a =~ /V/);
$ord += 10000000 if ($a =~ /cpreg/);
$ord += 1000000 if ($a =~ /\.\.\./);
$ord += 100000 if ($a =~ /mem/);
$ord += 10000 if ($a =~ /shift/);
$ord += 1000 if ($a =~ /imm/);
@commas = $a =~ /,/g;
$ord += 100*@commas;
for (my $i = 0; $i < length($a); $i++) {
$ord += ord(substr($a, $i, 1))/(($i+1));
}
return $ord;
}
# Should be called after removing implicit regs (writeback regs)
sub rename_regs($)
{
my ($str) = @_;
if ($str !~ /_or_imm/) {
# Remove subreg modifiers (can't remove earlier)
$str =~ s/\b([RV]\w[^E2])\w+/\1/;
}
# Try to match assembly names
# XXX i#1563: currently loads use Rd -- using Rt would add complexity
# We also have several other breaks from asm.
if ($str =~ /\b[RV]A.*\b[RV]B.*;/) {
$str =~ s/\b([RV])A\w/\1d/g; # g is for things like OP_smlalbb
$str =~ s/\b([RV])B\w/\1d2/g;
$str =~ s/\b([RV])C\w/\1m/;
$str =~ s/\bRD\w/Rn/;
} elsif ($str =~ /\bVC.*\bVC2.*/) { # OP_vmov
# I'm breaking w/ asm to make things clearer and easier for the
# docs mapping for what is a dst and which is 1st vs 2nd src:
if ($str =~ /\bVC.*;/) {
$str =~ s/\bRA\w/Rt2/;
$str =~ s/\bRB\w/Rt/;
$str =~ s/\bVC[^0-9]/Vd/; # asm has Vm, Vm2
$str =~ s/\bVC2\w/Vd2/;
} else {
$str =~ s/\bRA\w/Rd2/; # asm has Rt, Rt2
$str =~ s/\bRB\w/Rd/;
$str =~ s/\bVC[^0-9]/Vt/; # asm has Vm, Vm2
$str =~ s/\bVC2\w/Vt2/;
}
} elsif ($str =~ /\bVC.*;.*\bRB.*\bRA/) { # OP_vmov
# I'm breaking w/ asm to make things clearer and easier for the
# docs mapping for what is a dst:
$str =~ s/\bRA\w/Rt2/;
$str =~ s/\bRB\w/Rt/;
$str =~ s/\bVC\w/Vd/;
} elsif ($str =~ /\b[RV]A.*;/) {
if ($str =~ /\bVA.*;/) {
$str =~ s/\b([RV])B\w/\1t/;
} else {
$str =~ s/\b([RV])B\w/\1a/;
}
$str =~ s/\b([RV])A\w/\1d/;
$str =~ s/\b([RV])C\w/\1m/;
$str =~ s/\bRD\w/Rn/;
} elsif ($str =~ /;.*\b[RV]DE.*\b[RV]D2.*/) { # OP_stlexd
$str =~ s/\b([RV])A\w/\1n/;
$str =~ s/\b([RV])B\w/\1d/;
$str =~ s/\bRC\w/Rs/;
$str =~ s/\b([RV])DE\w/\1t/;
$str =~ s/\b([RV])D2\w/\1t2/;
} elsif ($str =~ /\b[RV]BE.*\b[RV]B2.*;/) {
$str =~ s/\b([RV])A\w/\1n/;
$str =~ s/\b([RV])BE\w/\1d/;
$str =~ s/\b([RV])B2\w/\1d2/;
$str =~ s/\bRC\w/Rs/;
$str =~ s/\bVC\w/Vm/;
$str =~ s/\bRD\w/Rm/;
} elsif ($str =~ /\bRB\w; RD\w RC\w+$/) { # OP_lsl
$str =~ s/\bRB\w/Rd/;
$str =~ s/\bRC\w/Rm/;
$str =~ s/\bRD\w/Rn/;
} elsif ($str =~ /\b[RV]B.*;/) {
$str =~ s/\b([RV])A\w/\1n/;
$str =~ s/\b([RV])B\w/\1d/g;
$str =~ s/\bRC\w/Rs/;
$str =~ s/\bVC\w/Vm/;
$str =~ s/\bRD\w/Rm/;
} elsif ($str =~ /;.*\b[RV]BE.*\b[RV]B2.*/) {
$str =~ s/\b([RV])A\w/\1n/;
$str =~ s/\b([RV])BE\w/\1t/;
$str =~ s/\b([RV])B2\w/\1t2/;
$str =~ s/\bRC\w/Rs/;
$str =~ s/\bVC\w/Vm/;
$str =~ s/\bRD\w/Rm/;
} elsif ($str =~ /;.*\b[RV]B.*/) {
$str =~ s/\b([RV])A\w/\1n/;
$str =~ s/\b([RV])B\w/\1t/;
$str =~ s/\bRC\w/Rs/;
$str =~ s/\bVC\w/Vm/;
$str =~ s/\bRD\w/Rm/;
} elsif ($str !~ /\b[RV].*;/ && $str =~ /;.*\b[RV]\w/) {
# Single regs for post-indexing or small # opnds
$str =~ s/\b([RV])A\w/\1n/;
$str =~ s/\b([RV])B\w/\1d/;
$str =~ s/\bRC\w/Rs/;
$str =~ s/\bVC\w/Vm/;
$str =~ s/\bRD\w/Rm/;
} elsif ($str =~ /\bRC\w; RA\w RD\w$/) {
$str =~ s/\bRC\w/Rd/;
$str =~ s/\bRA\w/Rn/;
$str =~ s/\bRD\w/Rm/;
} elsif ($str =~ /\b[RV]\w/) {
die "XXXX No reg mapping for $str\n";
}
return $str;
}