Created
June 29, 2017 20:44
-
-
Save robin-raymond/0e24055a38393ec633c0e835b659f8f4 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/.gitignore b/.gitignore | |
new file mode 100644 | |
index 0000000..956d759 | |
--- /dev/null | |
+++ b/.gitignore | |
@@ -0,0 +1,6 @@ | |
+/dl/openmax_dl*_win10_*.xml | |
+/dl/openmax_dl*.vcxproj.filters | |
+/dl/openmax_dl*.vcxproj | |
+/dl/openmax_dl*_win10_*.targets | |
+/dl/openmax_dl*_win10_*.props | |
+/dl/dl*.sln | |
\ No newline at end of file | |
diff --git a/dl/build/CompileArmAsm.bat b/dl/build/CompileArmAsm.bat | |
new file mode 100644 | |
index 0000000..9f4dcb9 | |
--- /dev/null | |
+++ b/dl/build/CompileArmAsm.bat | |
@@ -0,0 +1,19 @@ | |
+@echo off | |
+rem Compile file using armasm | |
+ | |
+IF "%1"=="" ( | |
+ echo Compile file using armasm | |
+ echo usage: | |
+ echo CompileArmAsm filename | |
+ goto END | |
+) | |
+ | |
+armasm -oldit %1 -o %~p1/%~n1.obj | |
+rem for /f %%i in ('armasm %1 -o %~p1/%~n1.obj') do set VAR=%%i | |
+rem echo %VAR | |
+ | |
+:END | |
+ | |
+ | |
+ | |
+ | |
diff --git a/dl/build/CompileArmAsmFolder.bat b/dl/build/CompileArmAsmFolder.bat | |
new file mode 100644 | |
index 0000000..8274a27 | |
--- /dev/null | |
+++ b/dl/build/CompileArmAsmFolder.bat | |
@@ -0,0 +1,20 @@ | |
+@echo off | |
+rem compiles all .asm files in specified folder | |
+ | |
+IF "%1"=="" ( | |
+ echo compiles all asm .asm files in specified folder from GAS to MS ARM assembler | |
+ echo usage: | |
+ echo CompileArmAsmFolder folder | |
+ goto END | |
+) | |
+ | |
+for %%f in (%1\*.asm) do ( | |
+ echo Processing: %%f ... | |
+ call CompileArmAsm %%f | |
+) | |
+ | |
+:END | |
+ | |
+ | |
+ | |
+ | |
diff --git a/dl/build/GasToArmAsm.bat b/dl/build/GasToArmAsm.bat | |
new file mode 100644 | |
index 0000000..96e9a2a | |
--- /dev/null | |
+++ b/dl/build/GasToArmAsm.bat | |
@@ -0,0 +1,22 @@ | |
+@echo off | |
+rem Converts specified file from GAS to MS ARM assembler | |
+rem Requirements: perl and GNU Cpp compiler in path (cpp.exe) | |
+ | |
+IF "%1"=="" ( | |
+ echo converts specified file form GAS to MS ARM assembler | |
+ echo usage: | |
+ echo GasToArmAsm filename | |
+ goto END | |
+) | |
+ | |
+if NOT EXIST %~p1\generated MKDIR %~p1\generated | |
+ | |
+SET GASPP_DEBUG=1 | |
+perl gas-preprocessor.pl -as-type armasm -force-thumb -- armasm -I../../ -c %1 -o %~p1/generated/%~n1.obj > %~p1/generated/%~n1.asm | |
+SET GASPP_DEBUG= | |
+ | |
+:END | |
+ | |
+ | |
+ | |
+ | |
diff --git a/dl/build/GasToArmAsmFolder.bat b/dl/build/GasToArmAsmFolder.bat | |
new file mode 100644 | |
index 0000000..766d8cb | |
--- /dev/null | |
+++ b/dl/build/GasToArmAsmFolder.bat | |
@@ -0,0 +1,20 @@ | |
+@echo off | |
+rem Converts all .s files in specified folder from GAS to MS ARM assembler | |
+ | |
+IF "%1"=="" ( | |
+ echo Converts all .s files in specified folder from GAS to MS ARM assembler | |
+ echo usage: | |
+ echo GasToArmAsmFolder folder | |
+ goto END | |
+) | |
+ | |
+for %%f in (%1\*.s) do ( | |
+ echo Processing: %%f ... | |
+ call GasToArmAsm %%f | |
+) | |
+ | |
+:END | |
+ | |
+ | |
+ | |
+ | |
diff --git a/dl/build/gas-preprocessor.pl b/dl/build/gas-preprocessor.pl | |
new file mode 100644 | |
index 0000000..3a542f0 | |
--- /dev/null | |
+++ b/dl/build/gas-preprocessor.pl | |
@@ -0,0 +1,1122 @@ | |
+#!/usr/bin/env perl | |
+# This sript originates from https://git.libav.org/?p=gas-preprocessor.git and was | |
+# specificaly modified for openmax_dl project for conversion from gas to MS Armasm. | |
+ | |
+# 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 $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) { | |
+ # 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; | |
+ } elsif ($preprocess_c_cmd[$index] eq "-oldit") { | |
+ splice(@preprocess_c_cmd, $index, 1); | |
+ 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"; | |
+my $cmdTest = join(' ', @preprocess_c_cmd); | |
+open(INPUT, "$cmdTest |") || die "Error running preprocessor"; | |
+my @alllines = <INPUT>; | |
+ | |
+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; | |
+ | |
+# Handler some multiline pseudo instruction fisrt (it easier to do it here) | |
+my $testxxx = scalar @alllines; | |
+if ($as_type eq "armasm") { | |
+ my $lineNumber = 0; | |
+ foreach (@alllines) { | |
+ if ($_ =~ /\.fnstart/) { | |
+ $_ = ""; | |
+ $alllines[$lineNumber - 1] =~ s/\s*[^:^\s]*:/$2 PROC/; | |
+ } | |
+ $lineNumber++; | |
+ } | |
+} | |
+ | |
+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 | |
+foreach (@alllines) { | |
+ # 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") or ($type eq "eqs")) { | |
+ #remove double quotes. | |
+ $expr =~ s/""/"/g; | |
+ 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); | |
+ } 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; | |
+ } | |
+ } | |
+ | |
+ # discard lines in false .if blocks | |
+ 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; | |
+ | |
+ # 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 ($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]; | |
+ | |
+ # handle .if directives; apple's assembler doesn't support important non-basic ones | |
+ # evaluating them is also needed to handle recursive macros | |
+ 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; | |
+ | |
+ # Strip out the .set lines from the armasm output | |
+ return if (handle_set($line) and $as_type eq "armasm"); | |
+ | |
+ 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+.*)//) { | |
+ 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; | |
+ | |
+ # only use whitespace as the separator | |
+ 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; # remove \() | |
+ 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; | |
+ | |
+ # commas are optional here too, but are syntactically important because | |
+ # parameters can be blank | |
+ my @arglist = split(/,/, $3); | |
+ my @args; | |
+ my @args_seperator; | |
+ | |
+ my $comma_sep_required = 0; | |
+ foreach (@arglist) { | |
+ # allow arithmetic/shift operators in macro arguments | |
+ $_ =~ s/\s*(\+|-|\*|\/|<<|>>|<|>)\s*/$1/g; | |
+ | |
+ my @whitespace_split = split(/\s+/, $_); | |
+ if (!@whitespace_split) { | |
+ push(@args, ''); | |
+ push(@args_seperator, ''); | |
+ } else { | |
+ foreach (@whitespace_split) { | |
+ #print ("arglist = \"$_\"\n"); | |
+ if (length($_)) { | |
+ push(@args, $_); | |
+ my $sep = $comma_sep_required ? "," : " "; | |
+ push(@args_seperator, $sep); | |
+ #print ("sep = \"$sep\", arg = \"$_\"\n"); | |
+ $comma_sep_required = 0; | |
+ } | |
+ } | |
+ } | |
+ | |
+ $comma_sep_required = 1; | |
+ } | |
+ | |
+ 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]; | |
+ my @macro_args = @{ $macro_args{$macro} }; | |
+ 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$//) { | |
+ #print "macro = $macro, args[$i] = $args[$i], args_seperator=@args_seperator, argname = $argname, arglist[$i] = $arglist[$i], arglist = @arglist, args=@args, macro_args=@macro_args\n"; | |
+ #$replacements{$argname} .= ", $args[$i]"; | |
+ $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++; | |
+ | |
+ # 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; | |
+ } | |
+ 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; # remove \() | |
+ 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]; | |
+ | |
+ # 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); | |
+ } | |
+ | |
+ $thumb = 1 if $line =~ /\.code\s+16|\.thumb/; | |
+ $thumb = 0 if $line =~ /\.code\s+32|\.arm/; | |
+ | |
+ # handle ldr <reg>, =<expr> | |
+ 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 = (); | |
+ } | |
+ | |
+ # handle GNU as pc-relative relocations for adrp/add | |
+ 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"; | |
+ } | |
+ | |
+ # thumb add with large immediate needs explicit add.w | |
+ if ($thumb and $line =~ /add\s+.*#([^@]+)/) { | |
+ $line =~ s/add/add.w/ if eval_expr($1) > 255; | |
+ } | |
+ | |
+ # mach-o local symbol names start with L (no dot) | |
+ $line =~ s/(?<!\w)\.(L\w+)/$1/g; | |
+ | |
+ # recycle the '.func' directive for '.thumb_func' | |
+ 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; | |
+ # Don't interpret e.g. bic as b<cc> with ic as conditional code | |
+ if ($cond =~ /|$arm_cond_codes/) { | |
+ if (exists $thumb_labels{$label}) { | |
+ print ASMFILE ".thumb_func $label\n"; | |
+ } else { | |
+ $call_targets{$label}++; | |
+ } | |
+ } | |
+ } | |
+ | |
+ # @l -> lo16() @ha -> ha16() | |
+ $line =~ s/,\s+([^,]+)\@l\b/, lo16($1)/g; | |
+ $line =~ s/,\s+([^,]+)\@ha\b/, ha16($1)/g; | |
+ | |
+ # move to/from SPR | |
+ 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; | |
+ } | |
+ } | |
+ | |
+ # old gas versions store upper and lower case names on .req, | |
+ # but they remove only one on .unreq | |
+ 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+/) { | |
+ # This line seems to possibly have a neon instruction | |
+ foreach (keys %neon_alias_reg) { | |
+ my $alias = $_; | |
+ # Require the register alias to match as an invididual word, not as a substring | |
+ # of a larger word-token. | |
+ if ($line =~ /\b$alias\b/) { | |
+ $line =~ s/\b$alias\b/$neon_alias_reg{$alias}/g; | |
+ # Add the type suffix. If multiple aliases match on the same line, | |
+ # only do this replacement the first time (a vfoo.bar string won't match v\w+). | |
+ $line =~ s/^(\s+)(v\w+)(\s+)/$1$2.$neon_alias_type{$alias}$3/; | |
+ } | |
+ } | |
+ } | |
+ | |
+ if ($arch eq "aarch64" or $as_type eq "armasm") { | |
+ # clang's integrated aarch64 assembler in Xcode 5 does not support .req/.unreq | |
+ if ($line =~ /\b(\w+)\s+\.req\s+(\w+)\b/) { | |
+ $aarch64_req_alias{$1} = $2; | |
+ return; | |
+ } | |
+ foreach (keys %aarch64_req_alias) { | |
+ my $alias = $_; | |
+ # recursively resolve aliases | |
+ 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") { | |
+ # 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"; | |
+ } | |
+ # 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"; | |
+ } | |
+ # 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"; | |
+ } | |
+ # 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; | |
+ 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") { | |
+ # Also replace variables set by .set | |
+ foreach (keys %symbols) { | |
+ my $sym = $_; | |
+ $line =~ s/\b$sym\b/$symbols{$sym}/g; | |
+ } | |
+ | |
+ # .hidden is not supported on armasm | |
+ if ($line =~ /\.hidden/) { | |
+ return; | |
+ } | |
+ | |
+ # Handle function declarations and keep track of the declared labels | |
+ if ($line =~ s/^\s*\.func\s+(\w+)/$1 PROC/) { | |
+ $labels_seen{$1} = 1; | |
+ } | |
+ | |
+ 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 | |
+ # incompatibilities. | |
+ | |
+ 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++; | |
+ # The matching regexp above removes the label from the start of | |
+ # the line (which might contain an instruction as well), readd | |
+ # it on a separate line above it. | |
+ $line = "$name:\n" . $line; | |
+ $last_temp_labels{$num} = $name; | |
+ } | |
+ | |
+ if ($line =~ 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}); | |
+ $labels_seen{$1} = 1; | |
+ } elsif ($line !~ /(\w+) PROC/) { | |
+ # If not a label, make sure the line starts with whitespace, | |
+ # otherwise ms armasm interprets it incorrectly. | |
+ $line =~ s/^[\.\w]/\t$&/; | |
+ } | |
+ | |
+ # Check branch instructions | |
+ if ($line =~ /(?:^|\n)\s*(\w+\s*:\s*)?(bl?x?(..)?(\.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/) { | |
+ # Not actually a branch | |
+ } 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 ""; | |
+ } 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); | |
+ } | |
+ } | |
+ | |
+ # ALIGN in armasm syntax is the actual number of bytes | |
+ if ($line =~ /\.align\s+(\d+)/) { | |
+ my $align = 1 << $1; | |
+ $line =~ s/\.align\s(\d+)/ALIGN $align/; | |
+ } | |
+ # Convert gas style [r0, :128] into armasm [r0@128] alignment specification | |
+ $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 | |
+ # with numerical values. Evaluate !<number> and (a <> b) into numbers, | |
+ # let the assembler evaluate the rest of the expressions. This current | |
+ # only works for cases when ! and <> are used with actual constant numbers, | |
+ # we don't evaluate subexpressions here. | |
+ | |
+ # Evaluate !<number> | |
+ while ($line =~ /!\s*(\d+)/g) { | |
+ my $val = ($1 != 0) ? 0 : 1; | |
+ $line =~ s/!(\d+)/$val/; | |
+ } | |
+ # Evaluate (a > b) | |
+ 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/; | |
+ } | |
+ | |
+ # 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}++; | |
+ } | |
+ | |
+ # 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/; | |
+ # armasm is unable to parse &0x - add spacing | |
+ $line =~ s/&0x/& 0x/g; | |
+ } | |
+ | |
+ if ($force_thumb) { | |
+ # 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; | |
+ | |
+ # 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; | |
+ | |
+ # 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; | |
+ | |
+ $line =~ s/\.arm/.thumb/x; | |
+ } | |
+ | |
+ # comment out unsupported directives | |
+ $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-/) { | |
+ # the syntax for these is a little different | |
+ $line =~ s/\.global/.globl/x; | |
+ # also catch .section .rodata since the equivalent to .const_data is .section __DATA,__const | |
+ $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; | |
+ # The alignment in AREA is the power of two, just as .align in gas | |
+ #$line =~ s/\.text/AREA |.text|, CODE, READONLY, ALIGN=4, CODEALIGN/; | |
+ # note this is not 100% accurate replacement, basically all parameters behing section name is ignored | |
+ $line =~ s/\.section\s*([^,]*),.*/\tAREA |$1|, CODE, READONLY, ALIGN=2, CODEALIGN/; | |
+ $line =~ s/(\s*)(.*)\.rodata/$1AREA |.rodata|, DATA, READONLY, ALIGN=5/; | |
+ $line =~ s/(\s*)\.equ\s*(.*),/$2 EQU/x; | |
+ $line =~ s/\.fnend/ENDP/x; | |
+ $line =~ s/\s*\.exitm//x; | |
+ $line =~ s/\s*\.warning.*//x; | |
+ # fix labels alignment | |
+ $line =~ s/(\s*)([^:]*):/$2/x; | |
+ $line =~ s/\.extern/EXTERN/x; | |
+ | |
+ # gas uses different syntax for some instruction. Basically gas allows to specify type after register instead of after instruction like ArmAsm | |
+ # !! IMPORTANT: conditions are not handled (VMOV{cond}.F32 Sd, #imm) | |
+ # fix VLDx, VSTx | |
+ $line =~ s/(VLD1|VST1)\s*([D|Q][0-9]*)\.[F|S](32|16)([^,]*),/$1\.$3 {$2$4},/xi; # VLD1 D0.F32.... | |
+ $line =~ s/(VLD2|VLD1|VST2|VST1)\s*{\s*([D|Q][0-9]*)\.(F|S)(32|16)([^,]*),\s*([D|Q][0-9]*)\.(F|S)(32|16)([^,]*)\s*}/$1\.$4 {$2$5, $6$9}/xi; | |
+ $line =~ s/(VLD1|VST1)\s*{\s*([D|Q][0-9]*)\.((F|S)(32|16))/$1\.$5 {$2/xi; # VLD1 {D0.F32}.... | |
+ $line =~ s/(VLD3|VST3)\s*{\s*(D[0-9]*)\.(F|S)(32|16)([^,]*),\s*(D[0-9]*)\.(F|S)(32|16)([^,]*),\s*(D[0-9]*)\.(F|S)(32|16)([^,]*)\s*}/$1\.$4 {$2$5, $6$9, $10$13}/xi; | |
+ $line =~ s/(VLD4|VST4)\s*{\s*(D[0-9]*)\.(F|S)(32|16)([^,]*),\s*(D[0-9]*)\.(F|S)(32|16)([^,]*),\s*(D[0-9]*)\.(F|S)(32|16)([^,]*),\s*(D[0-9]*)\.(F|S)(32|16)([^,]*)\s*}/$1\.$4 {$2$5, $6$9, $10$13, $14$17}/xi; | |
+ # fix VADD, VSUB, VMUL, VMLS, VMLA, VSUB | |
+ $line =~ s/(VADD|VSUB|VMUL|VMLS|VMLA|VSUB)\s*([D|Q][0-9]*)\.F32([^,]*),\s*([D|Q][0-9]*)\.F32([^,]*),\s*([D|Q][0-9]*)\.F32/$1\.F32 $2$3, $3$4, $5$6/xi; # float | |
+ $line =~ s/(VADD|VSUB|VMUL|VMLS|VMLA|VSUB)\s*([D|Q][0-9]*)\.S(32|16)([^,]*),\s*([D|Q][0-9]*)\.S(32|16)([^,]*),\s*([D|Q][0-9]*)\.S(32|16)/$1\.i$3 $2$4, $5$7, $8$10/xi; # integer | |
+ # fix VMULL, VMLSL, VMLAL, VHADD, VHSUB | |
+ $line =~ s/(VHSUB|VHADD|VMULL|VMLSL|VMLAL|VRSHRN)\s*([D|Q][0-9]*)\.S(64|32|16)([^,]*),\s*([D|Q][0-9]*)\.S(32|16)([^,]*),\s*([D|Q][0-9]*)\.S(32|16)/$1\.S$6 $2$4, $5$7, $8$10/xi; # integer | |
+ # fix VRSHRN | |
+ $line =~ s/(VRSHRN)\s*([D|Q][0-9]*)\.S(32|16)([^,]*),\s*([D|Q][0-9]*)\.S(64|32|16)([^,]*)/$1\.i$6 $2$4, $5$7/xi; # integer | |
+ # fix VMOV register. Accordiong to documentation type is optional, so it's removed completely | |
+ $line =~ s/(VMOV)\s*([S|D|Q][0-9]*)\.(F|S)(32|16)([^,]*),(\s*([R|D|Q][0-9]*)(\.(F|S)(32|16)){0,1})/$1.$4\ $2$5,$7/xi; | |
+ # fix VMOV immediate | |
+ $line =~ s/(VMOV)\s*([S|D|Q][0-9]*)\.F32([^,]*),\s*(\#){0,1}(.*)([^\s]*)/$1\.F32 $2$3,#$5/xi; | |
+ $line =~ s/VMOV.(F){0,1}(32)(\s*s.*,\s*r.*)/VMOV $3/xi; #remove type form certain VMOV pattern, because it produces compilation error | |
+ # fix VZIP, VUZP, VTRN, VDUP | |
+ $line =~ s/(VDUP|VZIP|VUZP|VTRN)\s*([D|Q][0-9]*)\.(F|S)(32|16)([^,]*),(\s*([D|Q][0-9]*)\.(F|S)(32|16)){0,1}/$1\.$4 $2$5,$7/xi; | |
+ # fix VQRDMULH, VQDMULH, VRHADD | |
+ $line =~ s/(VQRDMULH|VQDMULH|VRHADD)\s*(D[0-9]*)\.S(16|32)([^,]*),\s*(D[0-9]*)\.S(16|32)([^,]*),\s*(D[0-9]*)\.S(16|32)([^\s]*)/$1\.S$3 $2$4, $5$7, $8$10/xi; | |
+ # fix VREV64 | |
+ $line =~ s/(VREV64|VREV32)\s*([D|Q][0-9]*)\.[F|S](16|32|64)([^,]*),\s*([D|Q][0-9]*)\.[F|S](16|32|64)([^\s]*)/$1\.$3 $2$4, $5$7/xi; | |
+ # fix VNEG | |
+ $line =~ s/(VNEG)\s*(D[0-9]*)\.(F|S)(32|16)([^,]*),\s*(D[0-9]*)\.(F|S)(32|16)([^\s]*)/$1\.$3$4 $2$5, $6$9/xi; | |
+ # fix VRSHL, VSHL | |
+ $line =~ s/(VRSHL|VSHL|VSHR)\s*([D|Q][0-9]*)\.S(16|32)([^,]*),\s*([D|Q][0-9]*)\.S(16|32)([^\s]*)/$1\.S$3 $2$4, $5$7/xi; | |
+ # fix VMOVL, VQMOVN | |
+ $line =~ s/(VMOVL|VQMOVN)\s*([D|Q][0-9]*)\.S(16|32)([^,]*),\s*([D|Q][0-9]*)\.S(16|32)([^\s]*)/$1\.S$6 $2$4, $5$7/xi; | |
+ # fix VCVT | |
+ $line =~ s/(VCVT)\s*(S[0-9]*)\.(S32|F32)([^,]*),\s*(S[0-9]*)\.(S32|F32)([^\s]*)/$1\.$3.$6 $2$4, $5$7/xi; | |
+ # fix VDIV | |
+ $line =~ s/(VDIV)\s*(S[0-9]*)\.F32([^,]*),\s*(S[0-9]*)\.F32([^,]*),\s*(S[0-9]*)\.F32([^\s]*)/$1\.F32 $2$3, $4$5, $6$7/xi; | |
+ | |
+ #testing | |
+ $line =~ s/ARM//x; | |
+ | |
+ | |
+ # just testing | |
+ $line =~ s/\s*\.end/END/x; | |
+ | |
+ $line =~ s/fmxr/vmsr/; | |
+ $line =~ s/fmrx/vmrs/; | |
+ $line =~ s/fadds/vadd.f32/; | |
+ } | |
+ | |
+ # catch unknown section names that aren't mach-o style (with a comma) | |
+ if ($as_type =~ /apple-/ and $line =~ /.section ([^,]*)$/) { | |
+ die ".section $1 unsupported; figure out the mach-o section name and add it"; | |
+ } | |
+ | |
+ if ($line =~ /[^\s-]/) | |
+ { | |
+ 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 { | |
+ # TODO: Following lines sometimes causes adding imports for local fuctions. | |
+ # Eventualy it should be fixed, but right now just commented out | |
+ #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; | |
+} | |
+#exit 1 | |
diff --git a/dl/dl.gyp b/dl/dl.gyp | |
index 2b0826f..2deca5b 100644 | |
--- a/dl/dl.gyp | |
+++ b/dl/dl.gyp | |
@@ -17,7 +17,7 @@ | |
'../', | |
], | |
'conditions' : [ | |
- ['target_arch=="arm"', { | |
+ ['target_arch=="arm" or winuwp_platform=="win_phone" or winuwp_platform=="win10_arm"', { | |
'conditions' : [ | |
['clang==1', { | |
# TODO(hans) Enable integrated-as (crbug.com/124610). | |
@@ -75,7 +75,17 @@ | |
'BIG_FFT_TABLE', | |
], | |
}], | |
- ['target_arch=="arm" or target_arch=="arm64"', { | |
+ ['OS_RUNTIME=="winuwp"', { | |
+ 'defines': [ | |
+ 'WINUWP', | |
+ ], | |
+ }], | |
+ ['winuwp_platform=="win_phone" or winuwp_platform=="win10_arm"', { | |
+ 'defines': [ | |
+ 'USE_MSVS_ARM_INTRINCICS', | |
+ ], | |
+ }], | |
+ ['target_arch=="arm" or target_arch=="arm64" or winuwp_platform=="win_phone" or winuwp_platform=="win10_arm"', { | |
'sources':[ | |
# Common files that are used by both arm and arm64 code. | |
'api/arm/armOMX.h', | |
@@ -90,10 +100,10 @@ | |
'sp/src/arm/omxSP_FFTInit_R_F32.c', | |
], | |
}], | |
- ['target_arch=="arm"', { | |
+ ['target_arch=="arm" or winuwp_platform=="win_phone" or winuwp_platform=="win10_arm"', { | |
'sources': [ | |
# Common files that are used by both the NEON and non-NEON code. | |
- 'api/armCOMM_s.h', | |
+ 'api/arm/armCOMM_s.h', | |
'sp/src/arm/omxSP_FFTGetBufSize_C_SC16.c', | |
'sp/src/arm/omxSP_FFTGetBufSize_R_S16.c', | |
'sp/src/arm/omxSP_FFTGetBufSize_R_S16S32.c', | |
@@ -129,7 +139,7 @@ | |
'sp/src/arm/arm64/omxSP_FFTInv_CCSToR_F32.c', | |
], | |
}], | |
- ['target_arch=="ia32" or target_arch=="x64"', { | |
+ ['(target_arch=="ia32" or target_arch=="x64") and winuwp_platform!="win_phone" and winuwp_platform!="win10_arm"', { | |
'conditions': [ | |
['os_posix==1', { | |
'cflags': [ '-msse2', ], | |
@@ -194,7 +204,7 @@ | |
}, | |
], | |
'conditions': [ | |
- ['target_arch=="arm"', { | |
+ ['target_arch=="arm" or winuwp_platform=="win_phone" or winuwp_platform=="win10_arm"', { | |
'targets': [ | |
{ | |
# GN version: //third_party/opendmax_dl/openmax_dl_armv7 | |
@@ -217,7 +227,24 @@ | |
'sp/src/arm/armv7/omxSP_FFTInv_CCSToR_F32_Sfs_s.S', | |
], | |
'conditions': [ | |
- ['arm_neon_optional==1', { | |
+ ['winuwp_platform=="win_phone" or winuwp_platform=="win10_arm"', { | |
+ 'rules': [ | |
+ { | |
+ 'rule_name': 'gas_preprocessor', | |
+ 'extension': 'S', | |
+ 'inputs': [ | |
+ ], | |
+ 'outputs': [ | |
+ '<(INTERMEDIATE_DIR)/<(RULE_INPUT_ROOT).obj', | |
+ ], | |
+ 'action': [ | |
+ 'perl build\gas-preprocessor.pl -as-type armasm -force-thumb -- armasm -oldit -I../ -c <(RULE_INPUT_PATH) -o <(INTERMEDIATE_DIR)/<(RULE_INPUT_ROOT).obj' | |
+ ], | |
+ 'process_outputs_as_sources': 0, | |
+ 'message': 'Compiling <(RULE_INPUT_PATH)', | |
+ }], | |
+ }], | |
+ ['arm_neon_optional==1', { | |
# Run-time NEON detection. | |
'dependencies': [ | |
'../../../build/android/ndk.gyp:cpu_features', | |
@@ -297,6 +324,23 @@ | |
'sp/src/arm/neon/omxSP_FFTInv_CCSToR_F32_Sfs_s.S', | |
], | |
'conditions': [ | |
+ ['winuwp_platform=="win_phone" or winuwp_platform=="win10_arm"', { | |
+ 'rules': [ | |
+ { | |
+ 'rule_name': 'gas_preprocessor', | |
+ 'extension': 'S', | |
+ 'inputs': [ | |
+ ], | |
+ 'outputs': [ | |
+ '<(INTERMEDIATE_DIR)/<(RULE_INPUT_ROOT).obj', | |
+ ], | |
+ 'action': [ | |
+ 'perl build\gas-preprocessor.pl -as-type armasm -force-thumb -- armasm -oldit -I../ -c <(RULE_INPUT_PATH) -o <(INTERMEDIATE_DIR)/<(RULE_INPUT_ROOT).obj' | |
+ ], | |
+ 'process_outputs_as_sources': 0, | |
+ 'message': 'Compiling <(RULE_INPUT_PATH)', | |
+ }], | |
+ }], | |
# Disable GCC LTO due to NEON issues | |
# crbug.com/408997 | |
['clang==0 and use_lto==1', { | |
diff --git a/dl/sp/api/armSP.h b/dl/sp/api/armSP.h | |
index 8f804ac..bf6bc1c 100644 | |
--- a/dl/sp/api/armSP.h | |
+++ b/dl/sp/api/armSP.h | |
@@ -30,6 +30,16 @@ | |
#define _armSP_H_ | |
#include <stdint.h> | |
+#ifdef USE_MSVS_ARM_INTRINCICS | |
+ #include <intrin.h> | |
+#endif | |
+ | |
+#ifdef _MSC_VER | |
+ // Compilation in MSVS fails in case "inline" is used together with static keyword. | |
+ #define INLINE_KEYWORD __inline | |
+#else | |
+ #define INLINE_KEYWORD inline | |
+#endif | |
#include "dl/api/omxtypes.h" | |
@@ -93,14 +103,20 @@ typedef struct ARMsFFTSpec_FC32_Tag | |
/* | |
* Compute log2(x), where x must be a power of 2. | |
*/ | |
-static inline long fastlog2(long x) { | |
+static INLINE_KEYWORD long fastlog2(long x) { | |
long out; | |
+#ifdef USE_MSVS_ARM_INTRINCICS | |
+ out = _arm_clz(x); | |
+ out -= 63; | |
+ out = ~out; | |
+#else | |
asm("clz %0,%1\n\t" | |
"sub %0, %0, #63\n\t" | |
"neg %0, %0\n\t" | |
: "=r"(out) | |
: "r"(x) | |
:); | |
+#endif | |
return out; | |
} | |
@@ -110,17 +126,17 @@ static inline long fastlog2(long x) { | |
* FFT spec must have non-NULL pointers; and the FFT size must be | |
* within range. | |
*/ | |
-static inline int validateParametersFC32(const void* pSrc, | |
- const void* pDst, | |
- const ARMsFFTSpec_FC32* pFFTSpec) { | |
+static INLINE_KEYWORD int validateParametersFC32(const void* pSrc, | |
+ const void* pDst, | |
+ const ARMsFFTSpec_FC32* pFFTSpec) { | |
return pSrc && pDst && pFFTSpec && !(((uintptr_t)pSrc) & 31) && | |
!(((uintptr_t)pDst) & 31) && pFFTSpec->pTwiddle && pFFTSpec->pBuf && | |
(pFFTSpec->N >= 2) && (pFFTSpec->N <= (1 << TWIDDLE_TABLE_ORDER)); | |
} | |
-static inline int validateParametersF32(const void* pSrc, | |
- const void* pDst, | |
- const ARMsFFTSpec_R_FC32* pFFTSpec) { | |
+static INLINE_KEYWORD int validateParametersF32(const void* pSrc, | |
+ const void* pDst, | |
+ const ARMsFFTSpec_R_FC32* pFFTSpec) { | |
return pSrc && pDst && pFFTSpec && !(((uintptr_t)pSrc) & 31) && | |
!(((uintptr_t)pDst) & 31) && pFFTSpec->pTwiddle && pFFTSpec->pBuf && | |
(pFFTSpec->N >= 2) && (pFFTSpec->N <= (1 << TWIDDLE_TABLE_ORDER)); | |
diff --git a/dl/sp/api/omxSP.h b/dl/sp/api/omxSP.h | |
index 5a9ebe3..fc41dbf 100644 | |
--- a/dl/sp/api/omxSP.h | |
+++ b/dl/sp/api/omxSP.h | |
@@ -2533,7 +2533,7 @@ OMXResult omxSP_FFTInv_CCSToR_F32_Sfs( | |
* This block sets things up appropriately for run-time or build-time selection | |
* of NEON implementations. | |
*/ | |
-#if defined(__arm__) || defined(__aarch64__) | |
+#if defined(__arm__) || defined(__aarch64__) || defined(_M_ARM) | |
/* | |
* Generic versions. Just like their *_Sfs counterparts, but automatically | |
* detect whether NEON is available or not and choose the appropriate routine. | |
@@ -2589,7 +2589,7 @@ OMXResult omxSP_FFTInv_CToC_FC32_Sfs_vfp ( | |
/* Build-time non-ARM selection. */ | |
#define omxSP_FFTFwd_RToCCS_F32 omxSP_FFTFwd_RToCCS_F32_Sfs | |
#define omxSP_FFTInv_CCSToR_F32 omxSP_FFTInv_CCSToR_F32_Sfs | |
-#endif /* defined(__arm__) || defined(__aarch64__) */ | |
+#endif /* defined(__arm__) || defined(__aarch64__) || defined(_M_ARM)*/ | |
#ifdef __cplusplus | |
} | |
diff --git a/dl/sp/src/arm/neon/omxSP_FFTFwd_CToC_FC32_Sfs_s.S b/dl/sp/src/arm/neon/omxSP_FFTFwd_CToC_FC32_Sfs_s.S | |
index aa76112..f76d19e 100644 | |
--- a/dl/sp/src/arm/neon/omxSP_FFTFwd_CToC_FC32_Sfs_s.S | |
+++ b/dl/sp/src/arm/neon/omxSP_FFTFwd_CToC_FC32_Sfs_s.S | |
@@ -47,6 +47,7 @@ | |
@// (For example tables) | |
.extern armSP_FFTFwd_CToC_FC32_Radix2_ls_OutOfPlace_unsafe | |
.extern armSP_FFTFwd_CToC_FC32_Sfs_Radix2_ls_OutOfPlace_unsafe | |
+ .extern armSP_FFTFwd_CToC_FC32_Radix4_ls_OutOfPlace_unsafe | |
@//Input Registers | |
diff --git a/dl/sp/src/arm/neon/omxSP_FFTInv_CCSToR_S16_Sfs_s.S b/dl/sp/src/arm/neon/omxSP_FFTInv_CCSToR_S16_Sfs_s.S | |
index ae0ae0d..488e7e2 100644 | |
--- a/dl/sp/src/arm/neon/omxSP_FFTInv_CCSToR_S16_Sfs_s.S | |
+++ b/dl/sp/src/arm/neon/omxSP_FFTInv_CCSToR_S16_Sfs_s.S | |
@@ -45,6 +45,8 @@ | |
.extern armSP_FFTInv_CToC_SC16_Radix2_ls_OutOfPlace_unsafe | |
.extern armSP_FFTInv_CToC_SC16_Radix2_ps_OutOfPlace_unsafe | |
.extern armSP_FFTInv_CToC_SC16_Sfs_Radix2_ps_OutOfPlace_unsafe | |
+.extern armSP_FFTInv_CCSToR_S16_preTwiddleRadix2_unsafe | |
+.extern armSP_FFTInv_CCSToR_S16_Sfs_preTwiddleRadix2_unsafe | |
@Input Registers | |
#define pSrc r0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment