mirror of
https://github.com/DynamoRIO/dynamorio.git
synced 2026-10-12 02:02:04 +00:00
The group delimiters @{ and @} in our headers were inside regular C
comments rather than Doxygen comments. This worked fine with doxygen
through 1.8.13; but 1.8.17 and up ignore these group directives. Here
we fix this by adding the extra asterisk.
Fixes #4749
843 lines
33 KiB
Perl
Executable File
843 lines
33 KiB
Perl
Executable File
#!/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;
|
|
}
|