Commit 8797e0f8 authored by Anton Mitrofanov's avatar Anton Mitrofanov
Browse files

Update to the latest version of gas-preprocessor.pl from...

Update to the latest version of gas-preprocessor.pl from http://git.libav.org/?p=gas-preprocessor.git

Contributions by Janne Grunau, Martin Storsjo, Mans Rullgard, David Conrad, Martin Aumuller and others
parent 59b9c252
......@@ -684,7 +684,7 @@ case $host_cpu in
arm*)
ARCH="ARM"
if [ "$SYS" = MACOSX ] ; then
AS="${AS-extras/gas-preprocessor.pl $CC}"
AS="${AS-${SRCPATH}/tools/gas-preprocessor.pl -arch arm -- ${CC}}"
ASFLAGS="$ASFLAGS -DPREFIX -DPIC" # apple's ld doesn't support movw/movt relocations at all
# build for armv7 by default
if ! echo $CFLAGS | grep -Eq '\-arch' ; then
......@@ -698,7 +698,7 @@ case $host_cpu in
aarch64)
ARCH="AARCH64"
if [ "$SYS" = MACOSX ] ; then
AS="${AS-extras/gas-preprocessor.pl $CC}"
AS="${AS-${SRCPATH}/tools/gas-preprocessor.pl -arch aarch64 -- ${CC}}"
ASFLAGS="$ASFLAGS -DPREFIX"
else
AS="${AS-${CC}}"
......
#!/usr/bin/env perl
# by David Conrad
# This code is licensed under GPLv2 or later; go to gnu.org to read it
# (not that it much matters for an asm preprocessor)
# usage: set your assembler to be something like "perl gas-preprocessor.pl gcc"
use strict;
# Apple's gas is ancient and doesn't support modern preprocessing features like
# .rept and has ugly macro syntax, among other things. Thus, this script
# implements the subset of the gas preprocessor used by x264 and ffmpeg
# that isn't supported by Apple's gas.
my @gcc_cmd = @ARGV;
my @preprocess_c_cmd;
if (grep /\.c$/, @gcc_cmd) {
# C file (inline asm?) - compile
@preprocess_c_cmd = (@gcc_cmd, "-S");
} elsif (grep /\.S$/, @gcc_cmd) {
# asm file, just do C preprocessor
@preprocess_c_cmd = (@gcc_cmd, "-E");
} else {
die "Unrecognized input filetype";
}
@gcc_cmd = map { /\.[cS]$/ ? qw(-x assembler -) : $_ } @gcc_cmd;
@preprocess_c_cmd = map { /\.o$/ ? "-" : $_ } @preprocess_c_cmd;
open(ASMFILE, "-|", @preprocess_c_cmd) || die "Error running preprocessor";
my $current_macro = '';
my %macro_lines;
my %macro_args;
my %macro_args_default;
my @pass1_lines;
# pass 1: parse .macro
# note that the handling of arguments is probably overly permissive vs. gas
# but it should be the same for valid cases
while (<ASMFILE>) {
# comment out unsupported directives
s/\.type/@.type/x;
s/\.func/@.func/x;
s/\.endfunc/@.endfunc/x;
s/\.ltorg/@.ltorg/x;
s/\.size/@.size/x;
s/\.fpu/@.fpu/x;
# the syntax for these is a little different
s/\.global/.globl/x;
# also catch .section .rodata since the equivalent to .const_data is .section __DATA,__const
s/(.*)\.rodata/.const_data/x;
s/\.int/.long/x;
s/\.float/.single/x;
# catch unknown section names that aren't mach-o style (with a comma)
if (/.section ([^,]*)$/) {
die ".section $1 unsupported; figure out the mach-o section name and add it";
}
# macros creating macros is not handled (is that valid?)
if (/\.macro\s+([\d\w\.]+)\s*(.*)/) {
$current_macro = $1;
# commas in the argument list are optional, so only use whitespace as the separator
my $arglist = $2;
$arglist =~ s/,/ /g;
my @args = split(/\s+/, $arglist);
foreach my $i (0 .. $#args) {
my @argpair = split(/=/, $args[$i]);
$macro_args{$current_macro}[$i] = $argpair[0];
$argpair[0] =~ s/:vararg$//;
$macro_args_default{$current_macro}{$argpair[0]} = $argpair[1];
}
# ensure %macro_lines has the macro name added as a key
$macro_lines{$current_macro} = [];
} elsif (/\.endm/) {
if (!$current_macro) {
die "ERROR: .endm without .macro";
}
$current_macro = '';
} elsif ($current_macro) {
push(@{$macro_lines{$current_macro}}, $_);
} else {
expand_macros($_);
}
}
sub expand_macros {
my $line = @_[0];
if ($line =~ /(\S+:|)\s*([\w\d\.]+)\s*(.*)/ && exists $macro_lines{$2}) {
push(@pass1_lines, $1);
my $macro = $2;
# commas are optional here too, but are syntactically important because
# parameters can be blank
my @arglist = split(/,/, $3);
my @args;
foreach (@arglist) {
my @whitespace_split = split(/\s+/, $_);
if (!@whitespace_split) {
push(@args, '');
} else {
foreach (@whitespace_split) {
if (length($_)) {
push(@args, $_);
}
}
}
}
my %replacements;
if ($macro_args_default{$macro}){
%replacements = %{$macro_args_default{$macro}};
}
# construct hashtable of text to replace
foreach my $i (0 .. $#args) {
my $argname = $macro_args{$macro}[$i];
if ($args[$i] =~ m/=/) {
# arg=val references the argument name
# XXX: I'm not sure what the expected behaviour if a lot of
# these are mixed with unnamed args
my @named_arg = split(/=/, $args[$i]);
$replacements{$named_arg[0]} = $named_arg[1];
} elsif ($i > $#{$macro_args{$macro}}) {
# more args given than the macro has named args
# XXX: is vararg allowed on arguments before the last?
$argname = $macro_args{$macro}[-1];
if ($argname =~ s/:vararg$//) {
$replacements{$argname} .= ", $args[$i]";
} else {
die "Too many arguments to macro $macro";
}
} else {
$argname =~ s/:vararg$//;
$replacements{$argname} = $args[$i];
}
}
# apply replacements as regex
foreach (@{$macro_lines{$macro}}) {
my $macro_line = $_;
# do replacements by longest first, this avoids wrong replacement
# when argument names are subsets of each other
foreach (reverse sort {length $a <=> length $b} keys %replacements) {
$macro_line =~ s/\\$_/$replacements{$_}/g;
}
$macro_line =~ s/\\\(\)//g; # remove \()
expand_macros($macro_line);
}
} else {
push(@pass1_lines, $line);
}
}
close(ASMFILE) or exit 1;
open(ASMFILE, "|-", @gcc_cmd) or die "Error running assembler";
my @sections;
my $num_repts;
my $rept_lines;
my %literal_labels; # for ldr <reg>, =<expr>
my $literal_num = 0;
# pass 2: parse .rept and .if variants
# NOTE: since we don't implement a proper parser, using .rept with a
# variable assigned from .set is not supported
foreach my $line (@pass1_lines) {
# textual comparison .if
# this assumes nothing else on the same line
if ($line =~ /\.ifnb\s+(.*)/) {
if ($1) {
$line = ".if 1\n";
} else {
$line = ".if 0\n";
}
} elsif ($line =~ /\.ifb\s+(.*)/) {
if ($1) {
$line = ".if 0\n";
} else {
$line = ".if 1\n";
}
} elsif ($line =~ /\.ifc\s+(.*)\s*,\s*(.*)/) {
if ($1 eq $2) {
$line = ".if 1\n";
} else {
$line = ".if 0\n";
}
}
# handle .previous (only with regard to .section not .subsection)
if ($line =~ /\.(section|text|const_data)/) {
push(@sections, $line);
} elsif ($line =~ /\.previous/) {
if (!$sections[-2]) {
die ".previous without a previous section";
}
$line = $sections[-2];
push(@sections, $line);
}
# handle ldr <reg>, =<expr>
if ($line =~ /(.*)\s*ldr([\w\s\d]+)\s*,\s*=(.*)/) {
my $label = $literal_labels{$3};
if (!$label) {
$label = ".Literal_$literal_num";
$literal_num++;
$literal_labels{$3} = $label;
}
$line = "$1 ldr$2, $label\n";
} elsif ($line =~ /\.ltorg/) {
foreach my $literal (keys %literal_labels) {
$line .= "$literal_labels{$literal}:\n .word $literal\n";
}
%literal_labels = ();
}
# @l -> lo16() @ha -> ha16()
$line =~ s/,\s+([^,]+)\@l(\s)/, lo16($1)$2/g;
$line =~ s/,\s+([^,]+)\@ha(\s)/, ha16($1)$2/g;
if ($line =~ /\.rept\s+(.*)/) {
$num_repts = $1;
$rept_lines = "\n";
# handle the possibility of repeating another directive on the same line
# .endr on the same line is not valid, I don't know if a non-directive is
if ($num_repts =~ s/(\.\w+.*)//) {
$rept_lines .= "$1\n";
}
$num_repts = eval($num_repts);
} elsif ($line =~ /\.endr/) {
for (1 .. $num_repts) {
print ASMFILE $rept_lines;
}
$rept_lines = '';
} elsif ($rept_lines) {
$rept_lines .= $line;
} else {
print ASMFILE $line;
}
}
print ASMFILE ".text\n";
foreach my $literal (keys %literal_labels) {
print ASMFILE "$literal_labels{$literal}:\n .word $literal\n";
}
close(ASMFILE) or exit 1;
#!/usr/bin/env perl
# by David Conrad
# This code is licensed under GPLv2 or later; go to gnu.org to read it
# (not that it much matters for an asm preprocessor)
# usage: set your assembler to be something like "perl gas-preprocessor.pl gcc"
use strict;
# Apple's gas is ancient and doesn't support modern preprocessing features like
# .rept and has ugly macro syntax, among other things. Thus, this script
# implements the subset of the gas preprocessor used by x264 and ffmpeg
# that isn't supported by Apple's gas.
my %canonical_arch = ("aarch64" => "aarch64", "arm64" => "aarch64",
"arm" => "arm",
"powerpc" => "powerpc", "ppc" => "powerpc");
my %comments = ("aarch64" => '//',
"arm" => '@',
"powerpc" => '#');
my @gcc_cmd;
my @preprocess_c_cmd;
my $comm;
my $arch;
my $as_type = "apple-gas";
my $fix_unreq = $^O eq "darwin";
my $force_thumb = 0;
my $arm_cond_codes = "eq|ne|cs|cc|mi|pl|vs|vc|hi|ls|ge|lt|gt|le|al|hs|lo";
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
use different features which are not correctly handled.
Options for this program needs to be separated with ' -- ' from the assembler
command. Following options are currently supported:
-help - this usage text
-arch - target architecture
-as-type - one value out of {{,apple-}{gas,clang},armasm}
-fix-unreq
-no-fix-unreq
-force-thumb - assemble as thumb regardless of the input source
(note, this is incomplete and only works for sources
it explicitly was tested with)
";
sub usage() {
print $usage_str;
}
while (@ARGV) {
my $opt = shift;
if ($opt =~ /^-(no-)?fix-unreq$/) {
$fix_unreq = $1 ne "no-";
} elsif ($opt eq "-force-thumb") {
$force_thumb = 1;
} elsif ($opt eq "-arch") {
$arch = shift;
die "unknown arch: '$arch'\n" if not exists $comments{$arch};
} elsif ($opt eq "-as-type") {
$as_type = shift;
die "unknown as type: '$as_type'\n" if $as_type !~ /^((apple-)?(gas|clang)|armasm)$/;
} elsif ($opt eq "-help") {
usage();
exit 0;
} elsif ($opt eq "--" ) {
@gcc_cmd = @ARGV;
} elsif ($opt =~ /^-/) {
die "option '$opt' is not known. See '$0 -help' for usage information\n";
} else {
push @gcc_cmd, $opt, @ARGV;
}
last if (@gcc_cmd);
}
if (grep /\.c$/, @gcc_cmd) {
# C file (inline asm?) - compile
@preprocess_c_cmd = (@gcc_cmd, "-S");
} elsif (grep /\.[sS]$/, @gcc_cmd) {
# asm file, just do C preprocessor
@preprocess_c_cmd = (@gcc_cmd, "-E");
} 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.
exec(@gcc_cmd);
} else {
die "Unrecognized input filetype";
}
if ($as_type eq "armasm") {
$preprocess_c_cmd[0] = "cpp";
push(@preprocess_c_cmd, "-U__ELF__");
push(@preprocess_c_cmd, "-U__MACH__");
@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) {
if ($preprocess_c_cmd[$index] eq "-ignore" and $index + 1 < $#preprocess_c_cmd) {
splice(@preprocess_c_cmd, $index, 2);
next;
}
$index++;
}
if (grep /^-MM$/, @preprocess_c_cmd) {
system(@preprocess_c_cmd) == 0 or die "Error running preprocessor";
exit 0;
}
}
# if compiling, avoid creating an output file named '-.o'
if ((grep /^-c$/, @gcc_cmd) && !(grep /^-o/, @gcc_cmd)) {
foreach my $i (@gcc_cmd) {
if ($i =~ /\.[csS]$/) {
my $outputfile = $i;
$outputfile =~ s/\.[csS]$/.o/;
push(@gcc_cmd, "-o");
push(@gcc_cmd, $outputfile);
last;
}
}
}
# replace only the '-o' argument with '-', avoids rewriting the make dependency
# target specified with -MT to '-'
my $index = 1;
while ($index < $#preprocess_c_cmd) {
if ($preprocess_c_cmd[$index] eq "-o") {
$index++;
$preprocess_c_cmd[$index] = "-";
}
$index++;
}
my $tempfile;
if ($as_type ne "armasm") {
@gcc_cmd = map { /\.[csS]$/ ? qw(-x assembler -) : $_ } @gcc_cmd;
} else {
@preprocess_c_cmd = grep ! /^-c$/, @preprocess_c_cmd;
@preprocess_c_cmd = grep ! /^-m/, @preprocess_c_cmd;
@preprocess_c_cmd = grep ! /^-G/, @preprocess_c_cmd;
@preprocess_c_cmd = grep ! /^-W/, @preprocess_c_cmd;
@preprocess_c_cmd = grep ! /^-Z/, @preprocess_c_cmd;
@preprocess_c_cmd = grep ! /^-fp/, @preprocess_c_cmd;
@preprocess_c_cmd = grep ! /^-EHsc$/, @preprocess_c_cmd;
@preprocess_c_cmd = grep ! /^-O/, @preprocess_c_cmd;
@gcc_cmd = grep ! /^-G/, @gcc_cmd;
@gcc_cmd = grep ! /^-W/, @gcc_cmd;
@gcc_cmd = grep ! /^-Z/, @gcc_cmd;
@gcc_cmd = grep ! /^-fp/, @gcc_cmd;
@gcc_cmd = grep ! /^-EHsc$/, @gcc_cmd;
@gcc_cmd = grep ! /^-O/, @gcc_cmd;
my @outfiles = grep /\.(o|obj)$/, @gcc_cmd;
$tempfile = $outfiles[0].".asm";
# Remove most parameters from gcc_cmd, which actually is the armasm command,
# which doesn't support any of the common compiler/preprocessor options.
@gcc_cmd = grep ! /^-D/, @gcc_cmd;
@gcc_cmd = grep ! /^-U/, @gcc_cmd;
@gcc_cmd = grep ! /^-m/, @gcc_cmd;
@gcc_cmd = grep ! /^-M/, @gcc_cmd;
@gcc_cmd = grep ! /^-c$/, @gcc_cmd;
@gcc_cmd = grep ! /^-I/, @gcc_cmd;
@gcc_cmd = map { /\.S$/ ? $tempfile : $_ } @gcc_cmd;
}
# detect architecture from gcc binary name
if (!$arch) {
if ($gcc_cmd[0] =~ /(arm64|aarch64|arm|powerpc|ppc)/) {
$arch = $1;
} else {
# look for -arch flag
foreach my $i (1 .. $#gcc_cmd-1) {
if ($gcc_cmd[$i] eq "-arch" and
$gcc_cmd[$i+1] =~ /(arm64|aarch64|arm|powerpc|ppc)/) {
$arch = $1;
}
}
}
}
# assume we're not cross-compiling if no -arch or the binary doesn't have the arch name
$arch = qx/arch/ if (!$arch);
die "Unknown target architecture '$arch'" if not exists $canonical_arch{$arch};
$arch = $canonical_arch{$arch};
$comm = $comments{$arch};
my $inputcomm = $comm;
$comm = ";" if $as_type =~ /armasm/;
my %ppc_spr = (ctr => 9,
vrsave => 256);
open(INPUT, "-|", @preprocess_c_cmd) || die "Error running preprocessor";
if ($ENV{GASPP_DEBUG}) {
open(ASMFILE, ">&STDOUT");
} else {
if ($as_type ne "armasm") {
open(ASMFILE, "|-", @gcc_cmd) or die "Error running assembler";
} else {
open(ASMFILE, ">", $tempfile);
}
}
my $current_macro = '';
my $macro_level = 0;
my $rept_level = 0;
my %macro_lines;
my %macro_args;
my %macro_args_default;
my $macro_count = 0;
my $altmacro = 0;
my $in_irp = 0;
my $num_repts;
my @rept_lines;
my @irp_args;
my $irp_param;
my @ifstack;
my %symbols;
my @sections;
my %literal_labels; # for ldr <reg>, =<expr>
my $literal_num = 0;
my $literal_expr = ".word";
$literal_expr = ".quad" if $arch eq "aarch64";
my $thumb = 0;
my %thumb_labels;
my %call_targets;
my %mov32_targets;
my %neon_alias_reg;
my %neon_alias_type;
my $temp_label_next = 0;
my %last_temp_labels;
my %next_temp_labels;
my %labels_seen;
my %aarch64_req_alias;
if ($force_thumb) {
parse_line(".thumb\n");
}
# pass 1: parse .macro
# 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/^#.*$//;
# remove all comments (to avoid interfering with evaluating directives)
s/(?<!\\)$inputcomm.*//x;
# Strip out windows linefeeds
s/\r$//;
foreach my $subline (split(";", $_)) {
# Add newlines at the end of lines that don't already have one
chomp $subline;
$subline .= "\n";
parse_line($subline);
}
}
sub eval_expr {
my $expr = $_[0];
while ($expr =~ /([A-Za-z._][A-Za-z0-9._]*)/g) {
my $sym = $1;
$expr =~ s/$sym/($symbols{$sym})/ if defined $symbols{$sym};
}
eval $expr;
}
sub handle_if {
my $line = $_[0];
# handle .if directives; apple's assembler doesn't support important non-basic ones
# evaluating them is also needed to handle recursive macros
if ($line =~ /\.if(n?)([a-z]*)\s+(.*)/) {
my $result = $1 eq "n";
my $type = $2;
my $expr = $3;
if ($type eq "b") {
$expr =~ s/\s//g;
$result ^= $expr eq "";
} elsif ($type eq "c") {
if ($expr =~ /(.*)\s*,\s*(.*)/) {
$result ^= $1 eq $2;
} else {
die "argument to .ifc not recognized";
}
} elsif ($type eq "") {
$result ^= eval_expr($expr) != 0;
} elsif ($type eq "eq") {
$result = eval_expr($expr) == 0;
} elsif ($type eq "lt") {
$result = eval_expr($expr) < 0;
} else {
chomp($line);
die "unhandled .if varient. \"$line\"";
}
push (@ifstack, $result);
return 1;
} else {
return 0;
}
}
sub parse_if_line {
my $line = $_[0];
# evaluate .if blocks
if (scalar(@ifstack)) {
# Don't evaluate any new if statements if we're within
# a repetition or macro - they will be evaluated once
# the repetition is unrolled or the macro is expanded.
if (scalar(@rept_lines) == 0 and $macro_level == 0) {
if ($line =~ /\.endif/) {
pop(@ifstack);
return 1;
} elsif ($line =~ /\.elseif\s+(.*)/) {
if ($ifstack[-1] == 0) {
$ifstack[-1] = !!eval_expr($1);