123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215 |
- 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 $verbose = 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)
- -verbose - print executed commands
- ";
- 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 "-verbose") {
- $verbose = 1;
- } elsif ($opt eq "-arch") {
- $arch = shift;
- 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)$/;
- } 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) {
-
-
-
- print STDERR join(" ", @gcc_cmd)."\n" if $verbose;
- exec(@gcc_cmd);
- } else {
- die "Unrecognized input filetype";
- }
- if ($as_type eq "armasm") {
- $preprocess_c_cmd[0] = "cpp";
- push(@preprocess_c_cmd, "-undef");
-
-
-
-
- push(@preprocess_c_cmd, "-D_WIN32");
- @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) {
- print STDERR join(" ", @preprocess_c_cmd)."\n" if $verbose;
- 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;
- @preprocess_c_cmd = grep ! /^-oldit/, @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);
- 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);
- }
- }
- 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 %import_symbols;
- 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*
-
- 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 ($line =~ /\.macro/) {
- $macro_level++;
- if ($macro_level > 1 && !$current_macro) {
- die "nested macros but we don't have master macro";
- }
- } elsif ($line =~ /\.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|equ)\s+(\S*)\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 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/\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/\b$target\b/$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]+)/ 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]+)/ and $as_type =~ /^apple-/) {
- $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 =~ /^(\d+:)?\s*mov\s+(v\d[\.{}\[\]\w]+),\s*(v\d[\.{}\[\]\w]+)\b\s*$/) {
- $line = "$1 orr $2, $3, $3\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";
- }
-
-
-
- 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";
- }
-
- 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 $3#" . ($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/^\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?\.?([^\s]{2})?(\.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 "" 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|tbz)\s+(\w+)\s*,(\s*#\d+\s*,)?\s*(\w+)/) {
- my $instr = $2;
- my $reg = $3;
- my $bit = $4;
- my $target = $5;
- if ($target =~ /^(\d+)([bf])$/) {
-
- $line = handle_local_label($line, $1, $2);
- } else {
- $call_targets{$target}++;
- }
-
-
- if ($instr eq "tbz" 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);
- }
- }
-
- if ($line =~ /\.(?:p2)?align\s+(\d+)/) {
- my $align = 1 << $1;
- $line =~ s/\.(?:p2)?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/;
- }
- if ($arch eq "arm") {
-
- $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*)/) {
- $import_symbols{$1}++;
- }
-
-
-
- $line =~ s/^(\s+(?:vmov|vadd))(\s+s\d+\s*,\s*s\d+)/$1.f32$2/;
- } elsif ($arch eq "aarch64") {
-
- $line =~ s/^(\s+)ext(\s+)/$1ext8$2/;
-
- 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}) {
-
-
-
-
-
-
-
- $offset = -$offset;
- $line = "\tldr $reg, =$sym\n\tsub $reg, $reg, #$offset\n";
- }
- $import_symbols{$sym}++;
- }
-
-
-
-
-
- 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}++;
- }
-
-
- $line =~ s/(uxt[whb]|sxt[whb])(\s*\]?\s*)$/\1 #0\2/i;
-
- $line =~ s/\bmov\s+[xw]\d+\s*,\s*v\d+\.[ds]/u$&/i;
-
-
- $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/;
-
- $line =~ s/(cinc)\s+([xw]\w+)\s*,\s*([xw]\w+)\s*,\s*($arm_cond_codes)/\1\4 \2, \3/;
-
- $line =~ s/(cset)\s+([xw]\w+)\s*,\s*($arm_cond_codes)/\1\3 \2/;
- if ($ENV{GASPP_ARMASM64_SKIP_PRFUM}) {
-
-
-
-
- $line =~ s/prfum.*\]//;
- }
-
-
- 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}) {
-
-
-
-
- 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/;
- }
- }
-
- if ($line =~ /(?:ld|st)\d\s+({\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/;
- }
- }
- }
-
- $line =~ s/&0x/& 0x/g;
- }
- if ($force_thumb) {
-
-
-
- $line =~ s/((?:ldr|str)[bh]?)\s+(\w+),\s*\[(\w+)\],\s*(\w+)/$1 $2, [$3]\n\tadd $3, $3, $4/g;
-
-
-
- $line =~ s/mov\s*pc\s*,\s*lr/bx lr/g;
-
-
- $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;
-
- $line =~ s/muls\s+(\w+),\s*(\w+)\,\s*(\w+)/mul $1, $2, $3\n\tcmp $1, #0/g;
-
- $line =~ s/and\s+(\w+),\s*(sp|r13)\,\s*#(\w+)/mov $1, $2\n\tand $1, $1, #$3/g;
-
-
-
-
- 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;
- }
-
- $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 "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;
-
- $line =~ s/\.text/AREA |.text|, CODE, READONLY, ALIGN=4, CODEALIGN/;
- $line =~ s/(\s*)(.*)\.ro?data/$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.f32/;
-
-
-
- $line =~ s/^\s*it[te]*\s+/$comm$&/;
- }
- if ($as_type eq "armasm" and $arch eq "aarch64") {
-
- $line =~ s/\bb\.($arm_cond_codes)\b/b\1/;
- }
-
- 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 %import_symbols);
- print ASMFILE "\tEND\n";
- }
- 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";
- }
- END {
- unlink($tempfile) if defined $tempfile;
- }
|