use strict;
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) {
@preprocess_c_cmd = (@gcc_cmd, "-S");
} elsif (grep /\.[sS]$/, @gcc_cmd) {
@preprocess_c_cmd = (@gcc_cmd, "-E");
} elsif (grep /-(v|h|-version|dumpversion)/, @gcc_cmd) {
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;
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 ((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;
}
}
}
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";
@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;
}
if (!$arch) {
if ($gcc_cmd[0] =~ /(arm64|aarch64|arm|powerpc|ppc)/) {
$arch = $1;
} else {
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;
}
}
}
}
$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;
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");
}
while (<INPUT>) {
s/^#.*$//;
s/(?<!\\)$inputcomm.*//x;
s/\r$//;
foreach my $subline (split(";", $_)) {
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];
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];
if (scalar(@ifstack)) {
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);
} elsif ($ifstack[-1] > 0) {
$ifstack[-1] = -$ifstack[-1];
}
return 1;
} elsif ($line =~ /\.else/) {
$ifstack[-1] = !$ifstack[-1];
return 1;
} elsif (handle_if($line)) {
return 1;
}
}
foreach my $i (0 .. $#ifstack) {
if ($ifstack[$i] <= 0) {
return 1;
}
}
}
return 0;
}
sub parse_line {
my $line = $_[0];
return if (parse_if_line($line));
if (scalar(@rept_lines) == 0) {
if (/\.macro/) {
$macro_level++;
if ($macro_level > 1 && !$current_macro) {
die "nested macros but we don't have master macro";
}
} elsif (/\.endm/) {
$macro_level--;
if ($macro_level < 0) {
die "unmatched .endm";
} elsif ($macro_level == 0) {
$current_macro = '';
return;
}
}
}
if ($macro_level == 0) {
if ($line =~ /\.(rept|irp)/) {
$rept_level++;
} elsif ($line =~ /.endr/) {
$rept_level--;
}
}
if ($macro_level > 1) {
push(@{$macro_lines{$current_macro}}, $line);
} elsif (scalar(@rept_lines) and $rept_level >= 1) {
push(@rept_lines, $line);
} elsif ($macro_level == 0) {
expand_macros($line);
} else {
if ($line =~ /\.macro\s+([\d\w\.]+)\s*,?\s*(.*)/) {
$current_macro = $1;
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];
}
$macro_lines{$current_macro} = [];
} elsif ($current_macro) {
push(@{$macro_lines{$current_macro}}, $line);
} else {
die "macro level without a macro name";
}
}
}
sub handle_set {
my $line = $_[0];
if ($line =~ /\.set\s+(.*),\s*(.*)/) {
$symbols{$1} = eval_expr($2);
return 1;
}
return 0;
}
sub expand_macros {
my $line = $_[0];
if (handle_if($line)) {
return;
}
if (/\.purgem\s+([\d\w\.]+)/) {
delete $macro_lines{$1};
delete $macro_args{$1};
delete $macro_args_default{$1};
return;
}
if ($line =~ /\.altmacro/) {
$altmacro = 1;
return;
}
if ($line =~ /\.noaltmacro/) {
$altmacro = 0;
return;
}
$line =~ s/\%([^,]*)/eval_expr($1)/eg if $altmacro;
return if (handle_set($line) and $as_type eq "armasm");
if ($line =~ /\.rept\s+(.*)/) {
$num_repts = $1;
@rept_lines = ("\n");
if ($num_repts =~ s/(\.\w+.*)//) {
push(@rept_lines, "$1\n");
}
$num_repts = eval_expr($num_repts);
} elsif ($line =~ /\.irp\s+([\d\w\.]+)\s*(.*)/) {
$in_irp = 1;
$num_repts = 1;
@rept_lines = ("\n");
$irp_param = $1;
my $irp_arglist = $2;
$irp_arglist =~ s/,/ /g;
$irp_arglist =~ s/^\s+//;
@irp_args = split(/\s+/, $irp_arglist);
} elsif ($line =~ /\.irpc\s+([\d\w\.]+)\s*(.*)/) {
$in_irp = 1;
$num_repts = 1;
@rept_lines = ("\n");
$irp_param = $1;
my $irp_arglist = $2;
$irp_arglist =~ s/,/ /g;
$irp_arglist =~ s/^\s+//;
@irp_args = split(//, $irp_arglist);
} elsif ($line =~ /\.endr/) {
my @prev_rept_lines = @rept_lines;
my $prev_in_irp = $in_irp;
my @prev_irp_args = @irp_args;
my $prev_irp_param = $irp_param;
my $prev_num_repts = $num_repts;
@rept_lines = ();
$in_irp = 0;
@irp_args = '';
if ($prev_in_irp != 0) {
foreach my $i (@prev_irp_args) {
foreach my $origline (@prev_rept_lines) {
my $line = $origline;
$line =~ s/\\$prev_irp_param/$i/g;
$line =~ s/\\\(\)//g;
parse_line($line);
}
}
} else {
for (1 .. $prev_num_repts) {
foreach my $origline (@prev_rept_lines) {
my $line = $origline;
parse_line($line);
}
}
}
} elsif ($line =~ /(\S+:|)\s*([\w\d\.]+)\s*(.*)/ && exists $macro_lines{$2}) {
handle_serialized_line($1);
my $macro = $2;
my @arglist = split(/,/, $3);
my @args;
my @args_seperator;
my $comma_sep_required = 0;
foreach (@arglist) {
$_ =~ s/\s*(\+|-|\*|\/|<<|>>|<|>)\s*/$1/g;
my @whitespace_split = split(/\s+/, $_);
if (!@whitespace_split) {
push(@args, '');
push(@args_seperator, '');
} else {
foreach (@whitespace_split) {
if (length($_)) {
push(@args, $_);
my $sep = $comma_sep_required ? "," : " ";
push(@args_seperator, $sep);
$comma_sep_required = 0;
}
}
}
$comma_sep_required = 1;
}
my %replacements;
if ($macro_args_default{$macro}){
%replacements = %{$macro_args_default{$macro}};
}
foreach my $i (0 .. $#args) {
my $argname = $macro_args{$macro}[$i];
my @macro_args = @{ $macro_args{$macro} };
if ($args[$i] =~ m/=/) {
my @named_arg = split(/=/, $args[$i]);
$replacements{$named_arg[0]} = $named_arg[1];
} elsif ($i > $#{$macro_args{$macro}}) {
$argname = $macro_args{$macro}[-1];
if ($argname =~ s/:vararg$//) {
$replacements{$argname} .= "$args_seperator[$i] $args[$i]";
} else {
die "Too many arguments to macro $macro";
}
} else {
$argname =~ s/:vararg$//;
$replacements{$argname} = $args[$i];
}
}
my $count = $macro_count++;
foreach (@{$macro_lines{$macro}}) {
my $macro_line = $_;
foreach (reverse sort {length $a <=> length $b} keys %replacements) {
$macro_line =~ s/\\$_/$replacements{$_}/g;
}
if ($altmacro) {
foreach (reverse sort {length $a <=> length $b} keys %replacements) {
$macro_line =~ s/\b$_\b/$replacements{$_}/g;
}
}
$macro_line =~ s/\\\@/$count/g;
$macro_line =~ s/\\\(\)//g;
parse_line($macro_line);
}
} else {
handle_serialized_line($line);
}
}
sub is_arm_register {
my $name = $_[0];
if ($name eq "lr" or
$name eq "ip" or
$name =~ /^[rav]\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;
} else {
my $name = "temp_label_$temp_label_next";
$temp_label_next++;
push(@{$next_temp_labels{$num}}, $name);
$line =~ s/$target/$name/g;
}
return $line;
}
sub handle_serialized_line {
my $line = $_[0];
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);
}
$thumb = 1 if $line =~ /\.code\s+16|\.thumb/;
$thumb = 0 if $line =~ /\.code\s+32|\.arm/;
if ($line =~ /(.*)\s*ldr([\w\s\d]+)\s*,\s*=(.*)/ and $as_type ne "armasm") {
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/ and $as_type ne "armasm") {
$line .= ".align 2\n";
foreach my $literal (keys %literal_labels) {
$line .= "$literal_labels{$literal}:\n $literal_expr $literal\n";
}
%literal_labels = ();
}
if ($line =~ /(.*)\s*adrp([\w\s\d]+)\s*,\s*#?:pg_hi21:([^\s]+)/) {
$line = "$1 adrp$2, ${3}\@PAGE\n";
} elsif ($line =~ /(.*)\s*add([\w\s\d]+)\s*,([\w\s\d]+)\s*,\s*#?:lo12:([^\s]+)/) {
$line = "$1 add$2, $3, ${4}\@PAGEOFF\n";
}
if ($thumb and $line =~ /add\s+.*#([^@]+)/) {
$line =~ s/add/add.w/ if eval_expr($1) > 255;
}
$line =~ s/(?<!\w)\.(L\w+)/$1/g;
if ($thumb and $as_type =~ /^apple-/) {
$line =~ s/\.func/.thumb_func/x;
}
if ($thumb and $line =~ /^\s*(\w+)\s*:/) {
$thumb_labels{$1}++;
}
if ($as_type =~ /^apple-/ and
$line =~ /^\s*((\w+\s*:\s*)?bl?x?(..)?(?:\.w)?|\.global)\s+(\w+)/) {
my $cond = $3;
my $label = $4;
if ($cond =~ /|$arm_cond_codes/) {
if (exists $thumb_labels{$label}) {
print ASMFILE ".thumb_func $label\n";
} else {
$call_targets{$label}++;
}
}
}
$line =~ s/,\s+([^,]+)\@l\b/, lo16($1)/g;
$line =~ s/,\s+([^,]+)\@ha\b/, ha16($1)/g;
if ($line =~ /(\s+)(m[ft])([a-z]+)\s+(\w+)/ and exists $ppc_spr{$3}) {
if ($2 eq 'mt') {
$line = "$1${2}spr $ppc_spr{$3}, $4\n";
} else {
$line = "$1${2}spr $4, $ppc_spr{$3}\n";
}
}
if ($line =~ /\.unreq\s+(.*)/) {
if (defined $neon_alias_reg{$1}) {
delete $neon_alias_reg{$1};
delete $neon_alias_type{$1};
return;
} elsif (defined $aarch64_req_alias{$1}) {
delete $aarch64_req_alias{$1};
return;
}
}
if ($fix_unreq) {
if ($line =~ /\.unreq\s+(.*)/) {
$line = ".unreq " . lc($1) . "\n";
$line .= ".unreq " . uc($1) . "\n";
}
}
if ($line =~ /(\w+)\s+\.(dn|qn)\s+(\w+)(?:\.(\w+))?(\[\d+\])?/) {
$neon_alias_reg{$1} = "$3$5";
$neon_alias_type{$1} = $4;
return;
}
if (scalar keys %neon_alias_reg > 0 && $line =~ /^\s+v\w+/) {
foreach (keys %neon_alias_reg) {
my $alias = $_;
if ($line =~ /\b$alias\b/) {
$line =~ s/\b$alias\b/$neon_alias_reg{$alias}/g;
$line =~ s/^(\s+)(v\w+)(\s+)/$1$2.$neon_alias_type{$alias}$3/;
}
}
}
if ($arch eq "aarch64" or $as_type eq "armasm") {
if ($line =~ /\b(\w+)\s+\.req\s+(\w+)\b/) {
$aarch64_req_alias{$1} = $2;
return;
}
foreach (keys %aarch64_req_alias) {
my $alias = $_;
my $resolved = $aarch64_req_alias{$alias};
while (defined $aarch64_req_alias{$resolved}) {
$resolved = $aarch64_req_alias{$resolved};
}
$line =~ s/\b$alias\b/$resolved/g;
}
}
if ($arch eq "aarch64") {
if ($line =~ /^\s*mov\s+(v\d[\.{}\[\]\w]+),\s*(v\d[\.{}\[\]\w]+)\b\s*$/) {
$line = " orr $1, $2, $2\n";
}
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 =~ /^\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";
}
if ($as_type eq "clang" and
$line =~ /^(\s*(?:add|sub)s?) ([^#l]+)#([\d\+\-\*\/ <>]+)\s*$/) {
my $imm = eval $3;
if ($imm > 4095 and not ($imm & 4095)) {
$line = "$1 $2#" . ($imm >> 12) . ", lsl #12\n";
}
}
if ($ENV{GASPP_FIX_XCODE5}) {
if ($line =~ /^\s*bsl\b/) {
$line =~ s/\b(bsl)(\s+v[0-3]?\d\.(\w+))\b/$1.$3$2/;
$line =~ s/\b(v[0-3]?\d)\.$3\b/$1/g;
}
if ($line =~ /^\s*saddl2?\b/) {
$line =~ s/\b(saddl2?)(\s+v[0-3]?\d\.(\w+))\b/$1.$3$2/;
$line =~ s/\b(v[0-3]?\d)\.\w+\b/$1/g;
}
if ($line =~ /^\s*dup\b.*\]$/) {
$line =~ s/\bdup(\s+v[0-3]?\d)\.(\w+)\b/dup.$2$1/g;
$line =~ s/\b(v[0-3]?\d)\.[bhsdBHSD](\[\d\])$/$1$2/g;
}
}
}
if ($as_type eq "armasm") {
foreach (keys %symbols) {
my $sym = $_;
$line =~ s/\b$sym\b/$symbols{$sym}/g;
}
if ($line =~ s/^\s*\.func\s+(\w+)/$1 PROC/) {
$labels_seen{$1} = 1;
}
if ($line =~ s/^\s*(\d+)://) {
my $num = $1;
foreach (@{$next_temp_labels{$num}}) {
$line = "$_\n" . $line;
}
@next_temp_labels{$num} = ();
my $name = "temp_label_$temp_label_next";
$temp_label_next++;
$line = "$name:\n" . $line;
$last_temp_labels{$num} = $name;
}
if ($line =~ s/^(\w+):/$1/) {
return if (defined $labels_seen{$1});
$labels_seen{$1} = 1;
} elsif ($line !~ /(\w+) PROC/) {
$line =~ s/^[\.\w]/\t$&/;
}
if ($line =~ /(?:^|\n)\s*(\w+\s*:\s*)?(bl?x?(..)?(\.w)?)\s+(\w+)/) {
my $instr = $2;
my $cond = $3;
my $width = $4;
my $target = $5;
if ($cond !~ /|$arm_cond_codes/) {
} elsif ($target =~ /(\d+)([bf])/) {
$line = handle_local_label($line, $1, $2);
$line =~ s/\b$instr\b/$&.w/ if $width eq "";
} elsif (!is_arm_register($target)) {
$call_targets{$target}++;
}
} elsif ($line =~ /^\s*.h?word.*\b\d+[bf]\b/) {
while ($line =~ /\b(\d+)([bf])\b/g) {
$line = handle_local_label($line, $1, $2);
}
}
if ($line =~ /\.align\s+(\d+)/) {
my $align = 1 << $1;
$line =~ s/\.align\s(\d+)/ALIGN $align/;
}
$line =~ s/\[([^\[]+),\s*:(\d+)\]/[$1\@$2]/g;
while ($line =~ /!\s*(\d+)/g) {
my $val = ($1 != 0) ? 0 : 1;
$line =~ s/!(\d+)/$val/;
}
while ($line =~ /\(\s*(\d+)\s*([<>])\s*(\d+)\s*\)/) {
my $val;
if ($2 eq "<") {
$val = ($1 < $3) ? 1 : 0;
} else {
$val = ($1 > $3) ? 1 : 0;
}
$line =~ s/\(\s*(\d+)\s*([<>])\s*(\d+)\s*\)/$val/;
}
$line =~ s/^(\s*)movw(\s+\w+\s*,\s*)\#:lower16:(.*)$/$1mov32$2$3/;
$line =~ s/^\s*movt\s+\w+\s*,\s*\#:upper16:.*$//;
if ($line =~ /^\s*mov32\s+\w+,\s*([a-zA-Z]\w*)/) {
$mov32_targets{$1}++;
}
$line =~ s/^(\s+(?:vmov|vadd))(\s+s)/$1.f32$2/;
$line =~ s/&0x/& 0x/g;
}
if ($force_thumb) {
$line =~ s/(ldr|str)\s+(\w+),\s*\[(\w+)\],\s*(\w+)/$1 $2, [$3]\n\tadd $3, $3, $4/g;
s/mov\s*pc\s*,\s*lr/bx lr/g;
$line =~ s/stmdb\s+sp!\s*,\s*\{([^,-]+)\}/str $1, [sp, #-4]!/g;
$line =~ s/ldmia\s+sp!\s*,\s*\{([^,-]+)\}/ldr $1, [sp], #4/g;
$line =~ s/\.arm/.thumb/x;
}
$line =~ s/\.type/$comm$&/x if $as_type =~ /^(apple-|armasm)/;
$line =~ s/\.func/$comm$&/x if $as_type =~ /^(apple-|clang)/;
$line =~ s/\.endfunc/$comm$&/x if $as_type =~ /^(apple-|clang)/;
$line =~ s/\.endfunc/ENDP/x if $as_type =~ /armasm/;
$line =~ s/\.ltorg/$comm$&/x if $as_type =~ /^(apple-|clang)/;
$line =~ s/\.ltorg/LTORG/x if $as_type eq "armasm";
$line =~ s/\.size/$comm$&/x if $as_type =~ /^(apple-|armasm)/;
$line =~ s/\.fpu/$comm$&/x if $as_type =~ /^(apple-|armasm)/;
$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/\.syntax/$comm$&/x if $as_type =~ /armasm/;
$line =~ s/\.hword/.short/x;
if ($as_type =~ /^apple-/) {
$line =~ s/\.global/.globl/x;
$line =~ s/(.*)\.rodata/.const_data/x;
$line =~ s/\.int/.long/x;
$line =~ s/\.float/.single/x;
}
if ($as_type eq "armasm") {
$line =~ s/\.global/EXPORT/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/\.thumb/THUMB/x;
$line =~ s/\.arm/ARM/x;
$line =~ s/\.text/AREA |.text|, CODE, READONLY, ALIGN=2, CODEALIGN/;
$line =~ s/(\s*)(.*)\.rodata/$1AREA |.rodata|, DATA, READONLY, ALIGN=5/;
$line =~ s/fmxr/vmsr/;
$line =~ s/fmrx/vmrs/;
$line =~ s/fadds/vadd.f32/;
}
if ($as_type =~ /apple-/ and $line =~ /.section ([^,]*)$/) {
die ".section $1 unsupported; figure out the mach-o section name and add it";
}
print ASMFILE $line;
}
if ($as_type ne "armasm") {
print ASMFILE ".text\n";
print ASMFILE ".align 2\n";
foreach my $literal (keys %literal_labels) {
print ASMFILE "$literal_labels{$literal}:\n $literal_expr $literal\n";
}
map print(ASMFILE ".thumb_func $_\n"),
grep exists $thumb_labels{$_}, keys %call_targets;
} else {
map print(ASMFILE "\tIMPORT $_\n"),
grep ! exists $labels_seen{$_}, (keys %call_targets, keys %mov32_targets);
print ASMFILE "\tEND\n";
}
close(INPUT) or exit 1;
close(ASMFILE) or exit 1;
if ($as_type eq "armasm" and ! defined $ENV{GASPP_DEBUG}) {
system(@gcc_cmd) == 0 or die "Error running assembler";
}
END {
unlink($tempfile) if defined $tempfile;
}