summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorruki <[email protected]>2024-02-24 22:09:03 +0800
committerGitHub <[email protected]>2024-02-24 22:09:03 +0800
commitee93d3b328bf2e22896ce1864f63771b920b7bef (patch)
tree7f018b6485368eb77dd2c759de9087967b95ae27
parenta91f084c0227f3a0ac98964d4676de321ab10219 (diff)
parentfa62c88a428f8af413eaf71e0acf6f4df0cf3b24 (diff)
Merge pull request #4767 from SirLynix/patch-8
Update gas-preprocessor.pl
-rwxr-xr-xxmake/scripts/gas-preprocessor.pl356
1 files changed, 287 insertions, 69 deletions
diff --git a/xmake/scripts/gas-preprocessor.pl b/xmake/scripts/gas-preprocessor.pl
index 7e0857a66..ba756119d 100755
--- a/xmake/scripts/gas-preprocessor.pl
+++ b/xmake/scripts/gas-preprocessor.pl
@@ -16,6 +16,7 @@ my %canonical_arch = ("aarch64" => "aarch64", "arm64" => "aarch64",
my %comments = ("aarch64" => '//',
"arm" => '@',
+ "ppc" => '#',
"powerpc" => '#');
my @gcc_cmd;
@@ -27,6 +28,7 @@ my $as_type = "apple-gas";
my $fix_unreq = $^O eq "darwin";
my $force_thumb = 0;
+my $verbose = 0;
my $arm_cond_codes = "eq|ne|cs|cc|mi|pl|vs|vc|hi|ls|ge|lt|gt|le|al|hs|lo";
@@ -34,7 +36,7 @@ my $usage_str = "
$0\n
Gas-preprocessor.pl converts assembler files using modern GNU as syntax for
Apple's ancient gas version or clang's incompatible integrated assembler. The
-conversion is regularly tested for Libav, x264 and vlc. Other projects might
+conversion is regularly tested for FFmpeg, Libav, x264 and vlc. Other projects might
use different features which are not correctly handled.
Options for this program needs to be separated with ' -- ' from the assembler
@@ -48,6 +50,7 @@ command. Following options are currently supported:
-force-thumb - assemble as thumb regardless of the input source
(note, this is incomplete and only works for sources
it explicitly was tested with)
+ -verbose - print executed commands
";
sub usage() {
@@ -61,21 +64,23 @@ while (@ARGV) {
$fix_unreq = $1 ne "no-";
} elsif ($opt eq "-force-thumb") {
$force_thumb = 1;
+ } elsif ($opt eq "-verbose") {
+ $verbose = 1;
} elsif ($opt eq "-arch") {
$arch = shift;
- die "unknown arch: '$arch'\n" if not exists $comments{$arch};
+ die "unknown arch: '$arch'\n" if not exists $canonical_arch{$arch};
} elsif ($opt eq "-as-type") {
$as_type = shift;
- die "unknown as type: '$as_type'\n" if $as_type !~ /^((apple-)?(gas|clang)|armasm)$/;
+ die "unknown as type: '$as_type'\n" if $as_type !~ /^((apple-)?(gas|clang|llvm_gcc)|armasm)$/;
} elsif ($opt eq "-help") {
usage();
exit 0;
} elsif ($opt eq "--" ) {
- @gcc_cmd = @ARGV;
+ @gcc_cmd = @ARGV;
} elsif ($opt =~ /^-/) {
die "option '$opt' is not known. See '$0 -help' for usage information\n";
} else {
- push @gcc_cmd, $opt, @ARGV;
+ push @gcc_cmd, $opt, @ARGV;
}
last if (@gcc_cmd);
}
@@ -86,10 +91,11 @@ if (grep /\.c$/, @gcc_cmd) {
} elsif (grep /\.[sS]$/, @gcc_cmd) {
# asm file, just do C preprocessor
@preprocess_c_cmd = (@gcc_cmd, "-E");
-} elsif (grep /-(v|-help|-version|dumpversion)/, @gcc_cmd) {
+} elsif (grep /-(v|h|-version|dumpversion)/, @gcc_cmd) {
# pass -v/--version along, used during probing. Matching '-v' might have
# uninteded results but it doesn't matter much if gas-preprocessor or
# the compiler fails.
+ print STDERR join(" ", @gcc_cmd)."\n" if $verbose;
exec(@gcc_cmd);
} else {
die "Unrecognized input filetype";
@@ -98,7 +104,6 @@ if ($as_type eq "armasm") {
$preprocess_c_cmd[0] = "cpp";
- @preprocess_c_cmd = grep ! /^-nologo$/, @preprocess_c_cmd;
# Remove -ignore XX parameter pairs from preprocess_c_cmd
my $index = 1;
while ($index < $#preprocess_c_cmd) {
@@ -109,9 +114,23 @@ if ($as_type eq "armasm") {
$index++;
}
if (grep /^-MM$/, @preprocess_c_cmd) {
+ push(@preprocess_c_cmd, "-D_WIN32");
+ # Normally a preprocessor for windows would predefine _WIN32,
+ # but we're using any generic system-agnostic preprocessor "cpp"
+ # with -undef (to avoid getting predefined variables from the host
+ # system in cross compilation cases), so manually define it here.
+ # We only use this generic preprocessor for generating dependencies,
+ # if the build system runs preprocessing with -M/-MM without -MF.
+ push(@preprocess_c_cmd, "-undef");
+ @preprocess_c_cmd = grep ! /^-nologo$/, @preprocess_c_cmd;
+ print STDERR join(" ", @preprocess_c_cmd)."\n" if $verbose;
system(@preprocess_c_cmd) == 0 or die "Error running preprocessor";
exit 0;
}
+
+ # If not preprocessing for getting a dependency list, use cl.exe
+ # instead.
+ $preprocess_c_cmd[0] = "cl";
}
# if compiling, avoid creating an output file named '-.o'
@@ -126,13 +145,12 @@ if ((grep /^-c$/, @gcc_cmd) && !(grep /^-o/, @gcc_cmd)) {
}
}
}
-# replace only the '-o' argument with '-', avoids rewriting the make dependency
-# target specified with -MT to '-'
+# Remove the -o argument; if omitted, we by default preprocess to stdout.
my $index = 1;
while ($index < $#preprocess_c_cmd) {
if ($preprocess_c_cmd[$index] eq "-o") {
- $index++;
- $preprocess_c_cmd[$index] = "-";
+ splice(@preprocess_c_cmd, $index, 2);
+ last;
}
$index++;
}
@@ -140,6 +158,10 @@ while ($index < $#preprocess_c_cmd) {
my $tempfile;
if ($as_type ne "armasm") {
@gcc_cmd = map { /\.[csS]$/ ? qw(-x assembler -) : $_ } @gcc_cmd;
+
+ # Filter out options that can cause warnings due to unused arguments,
+ # Clang warns about unused -D parameters when invoked with "-x assembler".
+ @gcc_cmd = grep ! /^-D/, @gcc_cmd;
} else {
@preprocess_c_cmd = grep ! /^-c$/, @preprocess_c_cmd;
@preprocess_c_cmd = grep ! /^-m/, @preprocess_c_cmd;
@@ -150,6 +172,10 @@ if ($as_type ne "armasm") {
@preprocess_c_cmd = grep ! /^-fp/, @preprocess_c_cmd;
@preprocess_c_cmd = grep ! /^-EHsc$/, @preprocess_c_cmd;
@preprocess_c_cmd = grep ! /^-O/, @preprocess_c_cmd;
+ @preprocess_c_cmd = grep ! /^-oldit/, @preprocess_c_cmd;
+ @preprocess_c_cmd = grep ! /^-FS/, @preprocess_c_cmd;
+ @preprocess_c_cmd = grep ! /^-w/, @preprocess_c_cmd;
+ @preprocess_c_cmd = grep ! /^-M/, @preprocess_c_cmd;
@gcc_cmd = grep ! /^-G/, @gcc_cmd;
@gcc_cmd = grep ! /^-W/, @gcc_cmd;
@@ -157,6 +183,8 @@ if ($as_type ne "armasm") {
@gcc_cmd = grep ! /^-fp/, @gcc_cmd;
@gcc_cmd = grep ! /^-EHsc$/, @gcc_cmd;
@gcc_cmd = grep ! /^-O/, @gcc_cmd;
+ @gcc_cmd = grep ! /^-FS/, @gcc_cmd;
+ @gcc_cmd = grep ! /^-w/, @gcc_cmd;
my @outfiles = grep /\.(o|obj)$/, @gcc_cmd;
$tempfile = $outfiles[0].".asm";
@@ -200,12 +228,14 @@ $comm = ";" if $as_type =~ /armasm/;
my %ppc_spr = (ctr => 9,
vrsave => 256);
+print STDERR join(" ", @preprocess_c_cmd)."\n" if $verbose;
open(INPUT, "-|", @preprocess_c_cmd) || die "Error running preprocessor";
if ($ENV{GASPP_DEBUG}) {
open(ASMFILE, ">&STDOUT");
} else {
if ($as_type ne "armasm") {
+ print STDERR join(" ", @gcc_cmd)."\n" if $verbose;
open(ASMFILE, "|-", @gcc_cmd) or die "Error running assembler";
} else {
open(ASMFILE, ">", $tempfile);
@@ -243,7 +273,7 @@ my $thumb = 0;
my %thumb_labels;
my %call_targets;
-my %mov32_targets;
+my %import_symbols;
my %neon_alias_reg;
my %neon_alias_type;
@@ -264,16 +294,22 @@ if ($force_thumb) {
# note that the handling of arguments is probably overly permissive vs. gas
# but it should be the same for valid cases
while (<INPUT>) {
+ # remove lines starting with '#', preprocessing is done, '#' at start of
+ # the line indicates a comment for all supported archs (aarch64, arm, ppc
+ # and x86). Also strips line number comments but since they are off anyway
+ # it is no loss.
+ s/^\s*#.*$//;
# remove all comments (to avoid interfering with evaluating directives)
s/(?<!\\)$inputcomm.*//x;
# Strip out windows linefeeds
s/\r$//;
- # Strip out line number comments - armasm can handle them in a separate
- # syntax, but since the line numbers are off they are only misleading.
- s/^#\s+(\d+).*// if $as_type =~ /armasm/;
- parse_line($_);
+ foreach my $subline (split(";", $_)) {
+ chomp $subline;
+ parse_line_continued($subline);
+ }
}
+parse_line_continued("");
sub eval_expr {
my $expr = $_[0];
@@ -297,7 +333,7 @@ sub handle_if {
$expr =~ s/\s//g;
$result ^= $expr eq "";
} elsif ($type eq "c") {
- if ($expr =~ /(.*)\s*,\s*(.*)/) {
+ if ($expr =~ /(\S*)\s*,\s*(\S*)/) {
$result ^= $1 eq $2;
} else {
die "argument to .ifc not recognized";
@@ -356,18 +392,32 @@ sub parse_if_line {
return 0;
}
+my $last_line = "";
+sub parse_line_continued {
+ my $line = $_[0];
+ $last_line .= $line;
+ if ($last_line =~ /\\$/) {
+ $last_line =~ s/\\$//;
+ } else {
+ # Add newlines at the end of lines after concatenation.
+ $last_line .= "\n";
+ parse_line($last_line);
+ $last_line = "";
+ }
+}
+
sub parse_line {
my $line = $_[0];
return if (parse_if_line($line));
if (scalar(@rept_lines) == 0) {
- if (/\.macro/) {
+ if ($line =~ /\.macro/) {
$macro_level++;
if ($macro_level > 1 && !$current_macro) {
die "nested macros but we don't have master macro";
}
- } elsif (/\.endm/) {
+ } elsif ($line =~ /\.endm/) {
$macro_level--;
if ($macro_level < 0) {
die "unmatched .endm";
@@ -393,7 +443,7 @@ sub parse_line {
} elsif ($macro_level == 0) {
expand_macros($line);
} else {
- if ($line =~ /\.macro\s+([\d\w\.]+)\s*(.*)/) {
+ if ($line =~ /\.macro\s+([\d\w\.]+)\s*,?\s*(.*)/) {
$current_macro = $1;
# commas in the argument list are optional, so only use whitespace as the separator
@@ -420,7 +470,7 @@ sub parse_line {
sub handle_set {
my $line = $_[0];
- if ($line =~ /\.set\s+(.*),\s*(.*)/) {
+ if ($line =~ /\.(?:set|equ)\s+(\S*)\s*,\s*(.*)/) {
$symbols{$1} = eval_expr($2);
return 1;
}
@@ -617,18 +667,26 @@ sub is_arm_register {
return 0;
}
+sub is_aarch64_register {
+ my $name = $_[0];
+ if ($name =~ /^[xw]\d+$/) {
+ return 1;
+ }
+ return 0;
+}
+
sub handle_local_label {
my $line = $_[0];
my $num = $_[1];
my $dir = $_[2];
my $target = "$num$dir";
if ($dir eq "b") {
- $line =~ s/$target/$last_temp_labels{$num}/g;
+ $line =~ s/\b$target\b/$last_temp_labels{$num}/g;
} else {
my $name = "temp_label_$temp_label_next";
$temp_label_next++;
push(@{$next_temp_labels{$num}}, $name);
- $line =~ s/$target/$name/g;
+ $line =~ s/\b$target\b/$name/g;
}
return $line;
}
@@ -668,9 +726,9 @@ sub handle_serialized_line {
}
# handle GNU as pc-relative relocations for adrp/add
- if ($line =~ /(.*)\s*adrp([\w\s\d]+)\s*,\s*#?:pg_hi21:([^\s]+)/) {
+ if ($line =~ /(.*)\s*adrp([\w\s\d]+)\s*,\s*#?:pg_hi21:([^\s]+)/ and $as_type =~ /^apple-/) {
$line = "$1 adrp$2, ${3}\@PAGE\n";
- } elsif ($line =~ /(.*)\s*add([\w\s\d]+)\s*,([\w\s\d]+)\s*,\s*#?:lo12:([^\s]+)/) {
+ } elsif ($line =~ /(.*)\s*add([\w\s\d]+)\s*,([\w\s\d]+)\s*,\s*#?:lo12:([^\s]+)/ and $as_type =~ /^apple-/) {
$line = "$1 add$2, $3, ${4}\@PAGEOFF\n";
}
@@ -696,7 +754,7 @@ sub handle_serialized_line {
my $cond = $3;
my $label = $4;
# Don't interpret e.g. bic as b<cc> with ic as conditional code
- if ($cond =~ /|$arm_cond_codes/) {
+ if ($cond =~ /^(|$arm_cond_codes)$/) {
if (exists $thumb_labels{$label}) {
print ASMFILE ".thumb_func $label\n";
} else {
@@ -776,24 +834,25 @@ sub handle_serialized_line {
if ($arch eq "aarch64") {
# fix missing aarch64 instructions in Xcode 5.1 (beta3)
# mov with vector arguments is not supported, use alias orr instead
- if ($line =~ /^\s*mov\s+(v\d[\.{}\[\]\w]+),\s*(v\d[\.{}\[\]\w]+)\b\s*$/) {
- $line = " orr $1, $2, $2\n";
+ if ($line =~ /^(\d+:)?\s*mov\s+(v\d[\.{}\[\]\w]+),\s*(v\d[\.{}\[\]\w]+)\b\s*$/) {
+ $line = "$1 orr $2, $3, $3\n";
}
# movi 16, 32 bit shifted variant, shift is optional
- if ($line =~ /^\s*movi\s+(v[0-3]?\d\.(?:2|4|8)[hsHS])\s*,\s*(#\w+)\b\s*$/) {
- $line = " movi $1, $2, lsl #0\n";
+ if ($line =~ /^(\d+:)?\s*movi\s+(v[0-3]?\d\.(?:2|4|8)[hsHS])\s*,\s*(#\w+)\b\s*$/) {
+ $line = "$1 movi $2, $3, lsl #0\n";
}
# Xcode 5 misses the alias uxtl. Replace it with the more general ushll.
# Clang 3.4 misses the alias sxtl too. Replace it with the more general sshll.
- if ($line =~ /^\s*(s|u)xtl(2)?\s+(v[0-3]?\d\.[248][hsdHSD])\s*,\s*(v[0-3]?\d\.(?:2|4|8|16)[bhsBHS])\b\s*$/) {
- $line = " $1shll$2 $3, $4, #0\n";
+ # armasm64 also misses these instructions.
+ if ($line =~ /^(\d+:)?\s*(s|u)xtl(2)?\s+(v[0-3]?\d\.[248][hsdHSD])\s*,\s*(v[0-3]?\d\.(?:2|4|8|16)[bhsBHS])\b\s*$/) {
+ $line = "$1 $2shll$3 $4, $5, #0\n";
}
- # clang 3.4 does not automatically use shifted immediates in add/sub
- if ($as_type eq "clang" and
- $line =~ /^(\s*(?:add|sub)s?) ([^#l]+)#([\d\+\-\*\/ <>]+)\s*$/) {
- my $imm = eval $3;
+ # clang 3.4 and armasm64 do not automatically use shifted immediates in add/sub
+ if (($as_type eq "clang" or $as_type eq "armasm") and
+ $line =~ /^(\d+:)?(\s*(?:add|sub)s?) ([^#l]+)#([\d\+\-\*\/ <>]+)\s*$/) {
+ my $imm = eval $4;
if ($imm > 4095 and not ($imm & 4095)) {
- $line = "$1 $2#" . ($imm >> 12) . ", lsl #12\n";
+ $line = "$1 $2 $3#" . ($imm >> 12) . ", lsl #12\n";
}
}
if ($ENV{GASPP_FIX_XCODE5}) {
@@ -824,7 +883,7 @@ sub handle_serialized_line {
$labels_seen{$1} = 1;
}
- if ($line =~ s/^(\d+)://) {
+ if ($line =~ s/^\s*(\d+)://) {
# Convert local labels into unique labels. armasm (at least in
# RVCT) has something similar, but still different enough.
# By converting to unique labels we avoid any possible
@@ -844,7 +903,7 @@ sub handle_serialized_line {
$last_temp_labels{$num} = $name;
}
- if ($line =~ s/^(\w+):/$1/) {
+ if ($line =~ s/^\s*(\w+):/$1/) {
# Skip labels that have already been declared with a PROC,
# labels must not be declared multiple times.
return if (defined $labels_seen{$1});
@@ -857,21 +916,40 @@ sub handle_serialized_line {
# Check branch instructions
- if ($line =~ /(?:^|\n)\s*(\w+\s*:\s*)?(bl?x?(..)?(\.w)?)\s+(\w+)/) {
+ if ($line =~ /(?:^|\n)\s*(\w+\s*:\s*)?(bl?x?\.?([^\s]{2})?(\.w)?)\s+(\w+)/) {
my $instr = $2;
my $cond = $3;
my $width = $4;
my $target = $5;
# Don't interpret e.g. bic as b<cc> with ic as conditional code
- if ($cond !~ /|$arm_cond_codes/) {
+ if ($cond !~ /^(|$arm_cond_codes)$/) {
# Not actually a branch
- } elsif ($target =~ /(\d+)([bf])/) {
+ } elsif ($target =~ /^(\d+)([bf])$/) {
+ # The target is a local label
+ $line = handle_local_label($line, $1, $2);
+ $line =~ s/\b$instr\b/$&.w/ if $width eq "" and $arch eq "arm";
+ } elsif (($arch eq "arm" and !is_arm_register($target)) or
+ ($arch eq "aarch64" and !is_aarch64_register($target))) {
+ $call_targets{$target}++;
+ }
+ } elsif ($line =~ /(?:^|\n)\s*(\w+\s*:\s*)?(cbn?z|adr|tbn?z)\s+(\w+)\s*,(\s*#\d+\s*,)?\s*(\w+)/) {
+ my $instr = $2;
+ my $reg = $3;
+ my $bit = $4;
+ my $target = $5;
+ if ($target =~ /^(\d+)([bf])$/) {
# The target is a local label
$line = handle_local_label($line, $1, $2);
- $line =~ s/\b$instr\b/$&.w/ if $width eq "";
- } elsif (!is_arm_register($target)) {
+ } else {
$call_targets{$target}++;
}
+ # Convert tbz with a wX register into an xX register,
+ # due to armasm64 bugs/limitations.
+ if (($instr eq "tbz" or $instr eq "tbnz") and $reg =~ /w\d+/) {
+ my $xreg = $reg;
+ $xreg =~ s/w/x/;
+ $line =~ s/\b$reg\b/$xreg/;
+ }
} elsif ($line =~ /^\s*.h?word.*\b\d+[bf]\b/) {
while ($line =~ /\b(\d+)([bf])\b/g) {
$line = handle_local_label($line, $1, $2);
@@ -879,12 +957,12 @@ sub handle_serialized_line {
}
# ALIGN in armasm syntax is the actual number of bytes
- if ($line =~ /\.align\s+(\d+)/) {
+ if ($line =~ /\.(?:p2)?align\s+(\d+)/) {
my $align = 1 << $1;
- $line =~ s/\.align\s(\d+)/ALIGN $align/;
+ $line =~ s/\.(?:p2)?align\s+(\d+)/ALIGN $align/;
}
# Convert gas style [r0, :128] into armasm [r0@128] alignment specification
- $line =~ s/\[([^\[]+),\s*:(\d+)\]/[$1\@$2]/g;
+ $line =~ s/\[([^\[,]+),?\s*:(\d+)\]/[$1\@$2]/g;
# armasm treats logical values {TRUE} and {FALSE} separately from
# numeric values - logical operators and values can't be intermixed
@@ -909,19 +987,127 @@ sub handle_serialized_line {
$line =~ s/\(\s*(\d+)\s*([<>])\s*(\d+)\s*\)/$val/;
}
- # Change a movw... #:lower16: into a mov32 pseudoinstruction
- $line =~ s/^(\s*)movw(\s+\w+\s*,\s*)\#:lower16:(.*)$/$1mov32$2$3/;
- # and remove the following, matching movt completely
- $line =~ s/^\s*movt\s+\w+\s*,\s*\#:upper16:.*$//;
+ if ($arch eq "arm") {
+ # Change a movw... #:lower16: into a mov32 pseudoinstruction
+ $line =~ s/^(\s*)movw(\s+\w+\s*,\s*)\#:lower16:(.*)$/$1mov32$2$3/;
+ # and remove the following, matching movt completely
+ $line =~ s/^\s*movt\s+\w+\s*,\s*\#:upper16:.*$//;
- if ($line =~ /^\s*mov32\s+\w+,\s*([a-zA-Z]\w*)/) {
- $mov32_targets{$1}++;
- }
+ if ($line =~ /^\s*mov32\s+\w+,\s*([a-zA-Z]\w*)/) {
+ $import_symbols{$1}++;
+ }
+
+ # Misc bugs/deficiencies:
+ # armasm seems unable to parse e.g. "vmov s0, s1" without a type
+ # qualifier, thus add .f32.
+ $line =~ s/^(\s+(?:vmov|vadd))(\s+s\d+\s*,\s*s\d+)/$1.f32$2/;
+ } elsif ($arch eq "aarch64") {
+ # Convert ext into ext8; armasm64 seems to require it named as ext8.
+ $line =~ s/^(\s+)ext(\s+)/$1ext8$2/;
+
+ # Pick up targets from ldr x0, =sym+offset
+ if ($line =~ /^\s*ldr\s+(\w+)\s*,\s*=([a-zA-Z]\w*)(.*)$/) {
+ my $reg = $1;
+ my $sym = $2;
+ my $offset = eval_expr($3);
+ if ($offset < 0 and $ENV{GASPP_ARMASM64_SKIP_NEG_OFFSET}) {
+ # armasm64 in VS < 15.6 is buggy with ldr x0, =sym+offset where the
+ # offset is a negative value; it does write a negative
+ # offset into the literal pool as it should, but the
+ # negative offset only covers the lower 32 bit of the 64
+ # bit literal/relocation.
+ # Thus remove the offset and apply it manually with a sub
+ # afterwards.
+ $offset = -$offset;
+ $line = "\tldr $reg, =$sym\n\tsub $reg, $reg, #$offset\n";
+ }
+ $import_symbols{$sym}++;
+ }
+
+ # armasm64 (currently) doesn't support offsets on adrp targets,
+ # even though the COFF format relocations (and the linker)
+ # supports it. Therefore strip out the offsets from adrp and
+ # add :lo12: (in case future armasm64 would start handling it)
+ # and add an extra explicit add instruction for the offset.
+ if ($line =~ s/(adrp\s+\w+\s*,\s*(\w+))([\d\+\-\*\/\(\) <>]+)?/\1/) {
+ $import_symbols{$2}++;
+ }
+ if ($line =~ s/(add\s+(\w+)\s*,\s*\w+\s*,\s*):lo12:(\w+)([\d\+\-\*\/\(\) <>]+)?/\1\3/) {
+ my $reg = $2;
+ my $sym = $3;
+ my $offset = eval_expr($4);
+ $line .= "\tadd $reg, $reg, #$offset\n" if $offset > 0;
+ $import_symbols{$sym}++;
+ }
+
+ # Convert e.g. "add x0, x0, w0, uxtw" into "add x0, x0, w0, uxtw #0",
+ # or "ldr x0, [x0, w0, uxtw]" into "ldr x0, [x0, w0, uxtw #0]".
+ $line =~ s/(uxt[whb]|sxt[whb])(\s*\]?\s*)$/\1 #0\2/i;
+
+ # Convert "mov x0, v0.d[0]" into "umov x0, v0.d[0]"
+ $line =~ s/\bmov\s+[xw]\d+\s*,\s*v\d+\.[ds]/u$&/i;
+
+ # Convert "ccmp w0, #0, #0, ne" into "ccmpne w0, #0, #0",
+ # and "csel w0, w0, w0, ne" into "cselne w0, w0, w0".
+ $line =~ s/(ccmp|csel)\s+([xw]\w+)\s*,\s*([xw#]\w+)\s*,\s*([xw#]\w+)\s*,\s*($arm_cond_codes)/\1\5 \2, \3, \4/;
+
+ # Convert "cinc w0, w0, ne" into "cincne w0, w0".
+ $line =~ s/(cinc)\s+([xw]\w+)\s*,\s*([xw]\w+)\s*,\s*($arm_cond_codes)/\1\4 \2, \3/;
- # Misc bugs/deficiencies:
- # armasm seems unable to parse e.g. "vmov s0, s1" without a type
- # qualifier, thus add .f32.
- $line =~ s/^(\s+(?:vmov|vadd))(\s+s)/$1.f32$2/;
+ # Convert "cset w0, lo" into "csetlo w0"
+ $line =~ s/(cset)\s+([xw]\w+)\s*,\s*($arm_cond_codes)/\1\3 \2/;
+
+ if ($ENV{GASPP_ARMASM64_SKIP_PRFUM}) {
+ # Strip out prfum; armasm64 (VS < 15.5) fails to assemble any
+ # variant/combination of prfum tested so far, but since it is
+ # a prefetch instruction it can be skipped without changing
+ # results.
+ $line =~ s/prfum.*\]//;
+ }
+
+ # Convert "ldrb w0, [x0, #-1]" into "ldurb w0, [x0, #-1]".
+ # Don't do this for forms with writeback though.
+ if ($line =~ /(ld|st)(r[bh]?)\s+(\w+)\s*,\s*\[\s*(\w+)\s*,\s*#([^\]]+)\s*\][^!]/) {
+ my $instr = $1;
+ my $suffix = $2;
+ my $target = $3;
+ my $base = $4;
+ my $offset = eval_expr($5);
+ if ($offset < 0) {
+ $line =~ s/$instr$suffix/${instr}u$suffix/;
+ }
+ }
+
+ if ($ENV{GASPP_ARMASM64_INVERT_SCALE}) {
+ # Instructions like fcvtzs and scvtf store the scale value
+ # inverted in the opcode (stored as 64 - scale), but armasm64
+ # in VS < 15.5 stores it as-is. Thus convert from
+ # "fcvtzs w0, s0, #8" into "fcvtzs w0, s0, #56".
+ if ($line =~ /(?:fcvtzs|scvtf)\s+(\w+)\s*,\s*(\w+)\s*,\s*#(\d+)/) {
+ my $scale = $3;
+ my $inverted_scale = 64 - $3;
+ $line =~ s/#$scale/#$inverted_scale/;
+ }
+ }
+
+ # Convert "ld1 {v0.4h-v3.4h}" into "ld1 {v0.4h,v1.4h,v2.4h,v3.4h}"
+ if ($line =~ /(\{\s*v(\d+)\.(\d+[bhsdBHSD])\s*-\s*v(\d+)\.(\d+[bhsdBHSD])\s*\})/) {
+ my $regspec = $1;
+ my $reg1 = $2;
+ my $layout1 = $3;
+ my $reg2 = $4;
+ my $layout2 = $5;
+ if ($layout1 eq $layout2) {
+ my $new_regspec = "{";
+ foreach my $i ($reg1 .. $reg2) {
+ $new_regspec .= "," if ($i > $reg1);
+ $new_regspec .= "v$i.$layout1";
+ }
+ $new_regspec .= "}";
+ $line =~ s/$regspec/$new_regspec/;
+ }
+ }
+ }
# armasm is unable to parse &0x - add spacing
$line =~ s/&0x/& 0x/g;
}
@@ -930,16 +1116,31 @@ sub handle_serialized_line {
# Convert register post indexing to a separate add instruction.
# This converts e.g. "ldr r0, [r1], r2" into "ldr r0, [r1]",
# "add r1, r1, r2".
- $line =~ s/(ldr|str)\s+(\w+),\s*\[(\w+)\],\s*(\w+)/$1 $2, [$3]\n\tadd $3, $3, $4/g;
+ $line =~ s/((?:ldr|str)[bh]?)\s+(\w+),\s*\[(\w+)\],\s*(\w+)/$1 $2, [$3]\n\tadd $3, $3, $4/g;
# Convert "mov pc, lr" into "bx lr", since the former only works
# for switching from arm to thumb (and only in armv7), but not
# from thumb to arm.
- s/mov\s*pc\s*,\s*lr/bx lr/g;
+ $line =~ s/mov\s*pc\s*,\s*lr/bx lr/g;
+
+ # Convert stmdb/ldmia/stmfd/ldmfd/ldm with only one register into a plain str/ldr with post-increment/decrement.
+ # Wide thumb2 encoding requires at least two registers in register list while all other encodings support one register too.
+ $line =~ s/stm(?:db|fd)\s+sp!\s*,\s*\{([^,-]+)\}/str $1, [sp, #-4]!/g;
+ $line =~ s/ldm(?:ia|fd)?\s+sp!\s*,\s*\{([^,-]+)\}/ldr $1, [sp], #4/g;
- # Convert stmdb/ldmia with only one register into a plain str/ldr with post-increment/decrement
- $line =~ s/stmdb\s+sp!\s*,\s*\{([^,-]+)\}/str $1, [sp, #-4]!/g;
- $line =~ s/ldmia\s+sp!\s*,\s*\{([^,-]+)\}/ldr $1, [sp], #4/g;
+ # Convert muls into mul+cmp
+ $line =~ s/muls\s+(\w+),\s*(\w+)\,\s*(\w+)/mul $1, $2, $3\n\tcmp $1, #0/g;
+
+ # Convert "and r0, sp, #xx" into "mov r0, sp", "and r0, r0, #xx"
+ $line =~ s/and\s+(\w+),\s*(sp|r13)\,\s*#(\w+)/mov $1, $2\n\tand $1, $1, #$3/g;
+
+ # Convert "ldr r0, [r0, r1, lsl #6]" where the shift is >3 (which
+ # can't be handled in thumb) into "add r0, r0, r1, lsl #6",
+ # "ldr r0, [r0]", for the special case where the same address is
+ # used as base and target for the ldr.
+ if ($line =~ /(ldr[bh]?)\s+(\w+),\s*\[\2,\s*(\w+),\s*lsl\s*#(\w+)\]/ and $4 > 3) {
+ $line =~ s/(ldr[bh]?)\s+(\w+),\s*\[\2,\s*(\w+),\s*lsl\s*#(\w+)\]/add $2, $2, $3, lsl #$4\n\t$1 $2, [$2]/;
+ }
$line =~ s/\.arm/.thumb/x;
}
@@ -956,7 +1157,6 @@ sub handle_serialized_line {
$line =~ s/\.arch/$comm$&/x if $as_type =~ /^(apple-|clang|armasm)/;
$line =~ s/\.object_arch/$comm$&/x if $as_type =~ /^(apple-|armasm)/;
$line =~ s/.section\s+.note.GNU-stack.*/$comm$&/x if $as_type =~ /^(apple-|armasm)/;
- $line =~ s/\.hidden/$comm$&/x if $as_type =~ /^(apple-|clang|armasm)/;
$line =~ s/\.syntax/$comm$&/x if $as_type =~ /armasm/;
@@ -970,23 +1170,40 @@ sub handle_serialized_line {
$line =~ s/\.int/.long/x;
$line =~ s/\.float/.single/x;
}
+ if ($as_type eq "apple-gas") {
+ $line =~ s/vmrs\s+APSR_nzcv/fmrx r15/x;
+ }
if ($as_type eq "armasm") {
$line =~ s/\.global/EXPORT/x;
+ $line =~ s/\.extern/IMPORT/x;
$line =~ s/\.int/dcd/x;
$line =~ s/\.long/dcd/x;
$line =~ s/\.float/dcfs/x;
$line =~ s/\.word/dcd/x;
$line =~ s/\.short/dcw/x;
$line =~ s/\.byte/dcb/x;
+ $line =~ s/\.quad/dcq/x;
+ $line =~ s/\.ascii/dcb/x;
+ $line =~ s/\.asciz(.*)$/dcb\1,0/x;
$line =~ s/\.thumb/THUMB/x;
$line =~ s/\.arm/ARM/x;
# The alignment in AREA is the power of two, just as .align in gas
- $line =~ s/\.text/AREA |.text|, CODE, READONLY, ALIGN=2, CODEALIGN/;
- $line =~ s/(\s*)(.*)\.rodata/$1AREA |.rodata|, DATA, READONLY, ALIGN=5/;
-
+ $line =~ s/\.text/AREA |.text|, CODE, READONLY, ALIGN=4, CODEALIGN/;
+ $line =~ s/(\s*)(.*)\.ro?data(\s*,\s*"\w+")?/$1AREA |.rdata|, DATA, READONLY, ALIGN=5/;
+ $line =~ s/\.data/AREA |.data|, DATA, ALIGN=5/;
+ }
+ if ($as_type eq "armasm" and $arch eq "arm") {
$line =~ s/fmxr/vmsr/;
$line =~ s/fmrx/vmrs/;
- $line =~ s/fadds/vadd/;
+ $line =~ s/fadds/vadd.f32/;
+ # Armasm in VS 2019 16.3 errors out on "it" instructions. But
+ # armasm implicitly adds the necessary it instructions anyway, so we
+ # can just filter them out.
+ $line =~ s/^\s*it[te]*\s+/$comm$&/;
+ }
+ if ($as_type eq "armasm" and $arch eq "aarch64") {
+ # Convert "b.eq" into "beq"
+ $line =~ s/\bb\.($arm_cond_codes)\b/b\1/;
}
# catch unknown section names that aren't mach-o style (with a comma)
@@ -1008,7 +1225,7 @@ if ($as_type ne "armasm") {
grep exists $thumb_labels{$_}, keys %call_targets;
} else {
map print(ASMFILE "\tIMPORT $_\n"),
- grep ! exists $labels_seen{$_}, (keys %call_targets, keys %mov32_targets);
+ grep ! exists $labels_seen{$_}, (keys %call_targets, keys %import_symbols);
print ASMFILE "\tEND\n";
}
@@ -1016,6 +1233,7 @@ if ($as_type ne "armasm") {
close(INPUT) or exit 1;
close(ASMFILE) or exit 1;
if ($as_type eq "armasm" and ! defined $ENV{GASPP_DEBUG}) {
+ print STDERR join(" ", @gcc_cmd)."\n" if $verbose;
system(@gcc_cmd) == 0 or die "Error running assembler";
}