iPhone compilation support
authorDavid Conrad <lessen42@gmail.com>
Sun, 4 Oct 2009 11:24:42 +0000 (07:24 -0400)
committerFiona Glaser <fiona@x264.com>
Mon, 15 Feb 2010 09:00:07 +0000 (01:00 -0800)
Also add --sysroot to configure options

To build for iPhone 3gs / iPod touch 3g:
CC=/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/gcc ./configure --host=arm-apple-darwin --sysroot=/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS3.0.sdk

For older devices, add
--extra-cflags='-arch armv6 -mcpu=arm1176jzf-s' --extra-ldflags='-arch armv6' --disable-asm

common/arm/asm.S
common/arm/pixel-a.S
configure
extras/gas-preprocessor.pl [new file with mode: 0755]

index d163165..395267f 100644 (file)
 
 #include "config.h"
 
+#ifdef PREFIX
+#   define EXTERN_ASM _
+#else
+#   define EXTERN_ASM
+#endif
+
 #ifdef __ELF__
 #   define ELF
 #else
@@ -35,7 +41,8 @@ ELF     .eabi_attribute 25, \val
         .endm
 
         .macro function name
-        .global \name
+        .global EXTERN_ASM\name
+EXTERN_ASM\name:
 ELF     .hidden \name
 ELF     .type   \name, %function
         .func   \name
index 4dd65ed..d8533e5 100644 (file)
@@ -110,16 +110,17 @@ SAD4_ARMV6 8
 
 .macro SAD_FUNC w, h, name, align:vararg
 function x264_pixel_sad\name\()_\w\()x\h\()_neon
+    SAD_START_\w \align
+
 .if \w == 16
-    .set r, \h / 2 - 1
+.rept \h / 2 - 1
+    SAD_\w \align
+.endr
 .else
-    .set r, \h - 1
-.endif
-
-    SAD_START_\w \align
-.rept r
+.rept \h - 1
     SAD_\w \align
 .endr
+.endif
 
 .if \w > 8
     vabal.u8    q8,  d4,  d6
index b254383..5288351 100755 (executable)
--- a/configure
+++ b/configure
@@ -23,6 +23,7 @@ echo "  --extra-cflags=ECFLAGS   add ECFLAGS to CFLAGS"
 echo "  --extra-ldflags=ELDFLAGS add ELDFLAGS to LDFLAGS"
 echo "  --host=HOST              build programs to run on HOST"
 echo "  --cross-prefix=PREFIX    use PREFIX for compilation tools"
+echo "  --sysroot=SYSROOT        root of cross-build tree"
 echo ""
 exit 1
 fi
@@ -223,6 +224,10 @@ for opt do
         --cross-prefix=*)
             cross_prefix="${opt#--cross-prefix=}"
             ;;
+        --sysroot=*)
+            CFLAGS="$CFLAGS --sysroot=${opt#--sysroot=}"
+            LDFLAGS="$LDFLAGS --sysroot=${opt#--sysroot=}"
+            ;;
         *)
             echo "Unknown option $opt, ignored"
             ;;
@@ -367,7 +372,17 @@ case $host_cpu in
     ;;
   arm*)
     ARCH="ARM"
-    AS="${AS-${cross_prefix}gcc}"
+    if [ "$SYS" = MACOSX ] ; then
+      AS="${AS-extras/gas-preprocessor.pl $CC}"
+      ASFLAGS="$ASFLAGS -DPREFIX -DPIC"  # apple's ld doesn't support movw/movt relocations at all
+      # build for armv7 by default
+      if ! echo $CFLAGS | grep -Eq '\-arch' ; then
+        CFLAGS="$CFLAGS -arch armv7"
+        LDFLAGS="$LDFLAGS -arch armv7"
+      fi
+    else
+      AS="${AS-${cross_prefix}gcc}"
+    fi
     ;;
   s390|s390x)
     ARCH="S390"
diff --git a/extras/gas-preprocessor.pl b/extras/gas-preprocessor.pl
new file mode 100755 (executable)
index 0000000..d60893c
--- /dev/null
@@ -0,0 +1,256 @@
+#!/usr/bin/env perl
+# by David Conrad
+# This code is licensed under GPLv2 or later; go to gnu.org to read it
+#  (not that it much matters for an asm preprocessor)
+# usage: set your assembler to be something like "perl gas-preprocessor.pl gcc"
+use strict;
+
+# Apple's gas is ancient and doesn't support modern preprocessing features like
+# .rept and has ugly macro syntax, among other things. Thus, this script
+# implements the subset of the gas preprocessor used by x264 and ffmpeg
+# that isn't supported by Apple's gas.
+
+# FIXME: doesn't work if the path has spaces, but oh well...
+my $gcc_cmd = join(' ', @ARGV);
+my $preprocess_c_cmd;
+
+if ($gcc_cmd =~ /\S+\.c/) {
+    # C file (inline asm?) - compile
+    $preprocess_c_cmd = "$gcc_cmd -S";
+    $gcc_cmd =~ s/\S+\.c/-x assembler -/g;
+} elsif ($gcc_cmd =~ /\S+\.S/) {
+    # asm file, just do C preprocessor
+    $preprocess_c_cmd = "$gcc_cmd -E";
+    $gcc_cmd =~ s/\S+\.S/-x assembler -/g;
+} else {
+    die "Unrecognized input filetype";
+}
+
+$preprocess_c_cmd =~ s/\S+\.o/-/g;
+
+open(ASMFILE, "-|", $preprocess_c_cmd) || die "Error running preprocessor";
+
+my $current_macro = '';
+my %macro_lines;
+my %macro_args;
+my %macro_args_default;
+
+my @pass1_lines;
+
+# pass 1: parse .macro
+# note that the handling of arguments is probably overly permissive vs. gas
+# but it should be the same for valid cases
+while (<ASMFILE>) {
+    # comment out unsupported directives
+    s/\.type/@.type/x;
+    s/\.func/@.func/x;
+    s/\.endfunc/@.endfunc/x;
+    s/\.ltorg/@.ltorg/x;
+    s/\.size/@.size/x;
+    s/\.fpu/@.fpu/x;
+
+    # the syntax for these is a little different
+    s/\.global/.globl/x;
+    # also catch .section .rodata since the equivalent to .const_data is .section __DATA,__const
+    s/(.*)\.rodata/.const_data/x;
+    s/\.int/.long/x;
+    s/\.float/.single/x;
+
+    # catch unknown section names that aren't mach-o style (with a comma)
+    if (/.section ([^,]*)$/) {
+        die ".section $1 unsupported; figure out the mach-o section name and add it";
+    }
+
+    # macros creating macros is not handled (is that valid?)
+    if (/\.macro\s+([\d\w\.]+)\s*(.*)/) {
+        $current_macro = $1;
+
+        # commas in the argument list are optional, so only use whitespace as the separator
+        my $arglist = $2;
+        $arglist =~ s/,/ /g;
+
+        my @args = split(/\s+/, $arglist);
+        foreach my $i (0 .. $#args) {
+            my @argpair = split(/=/, $args[$i]);
+            $macro_args{$current_macro}[$i] = $argpair[0];
+            $argpair[0] =~ s/:vararg$//;
+            $macro_args_default{$current_macro}{$argpair[0]} = $argpair[1];
+        }
+        # ensure %macro_lines has the macro name added as a key
+        $macro_lines{$current_macro} = [];
+    } elsif (/\.endm/) {
+        if (!$current_macro) {
+            die "ERROR: .endm without .macro";
+        }
+        $current_macro = '';
+    } elsif ($current_macro) {
+        push(@{$macro_lines{$current_macro}}, $_);
+    } else {
+        expand_macros($_);
+    }
+}
+
+sub expand_macros {
+    my $line = @_[0];
+    if ($line =~ /(\S+:|)\s*([\w\d\.]+)\s*(.*)/ && exists $macro_lines{$2}) {
+        push(@pass1_lines, $1);
+        my $macro = $2;
+
+        # commas are optional here too, but are syntactically important because
+        # parameters can be blank
+        my @arglist = split(/,/, $3);
+        my @args;
+        foreach (@arglist) {
+            my @whitespace_split = split(/\s+/, $_);
+            if (!@whitespace_split) {
+                push(@args, '');
+            } else {
+                foreach (@whitespace_split) {
+                    if (length($_)) {
+                        push(@args, $_);
+                    }
+                }
+            }
+        }
+
+        my %replacements;
+        if ($macro_args_default{$macro}){
+            %replacements = %{$macro_args_default{$macro}};
+        }
+
+        # construct hashtable of text to replace
+        foreach my $i (0 .. $#args) {
+            my $argname = $macro_args{$macro}[$i];
+
+            if ($args[$i] =~ m/=/) {
+                # arg=val references the argument name
+                # XXX: I'm not sure what the expected behaviour if a lot of
+                # these are mixed with unnamed args
+                my @named_arg = split(/=/, $args[$i]);
+                $replacements{$named_arg[0]} = $named_arg[1];
+            } elsif ($i > $#{$macro_args{$macro}}) {
+                # more args given than the macro has named args
+                # XXX: is vararg allowed on arguments before the last?
+                $argname = $macro_args{$macro}[-1];
+                if ($argname =~ s/:vararg$//) {
+                    $replacements{$argname} .= ", $args[$i]";
+                } else {
+                    die "Too many arguments to macro $macro";
+                }
+            } else {
+                $argname =~ s/:vararg$//;
+                $replacements{$argname} = $args[$i];
+            }
+        }
+
+        # apply replacements as regex
+        foreach (@{$macro_lines{$macro}}) {
+            my $macro_line = $_;
+            # do replacements by longest first, this avoids wrong replacement
+            # when argument names are subsets of each other
+            foreach (reverse sort {length $a <=> length $b} keys %replacements) {
+                $macro_line =~ s/\\$_/$replacements{$_}/g;
+            }
+            $macro_line =~ s/\\\(\)//g;     # remove \()
+            expand_macros($macro_line);
+        }
+    } else {
+        push(@pass1_lines, $line);
+    }
+}
+
+close(ASMFILE) or exit 1;
+open(ASMFILE, "|-", $gcc_cmd) or die "Error running assembler";
+
+my @sections;
+my $num_repts;
+my $rept_lines;
+
+my %literal_labels;     # for ldr <reg>, =<expr>
+my $literal_num = 0;
+
+# pass 2: parse .rept and .if variants
+# NOTE: since we don't implement a proper parser, using .rept with a
+# variable assigned from .set is not supported
+foreach my $line (@pass1_lines) {
+    # textual comparison .if
+    # this assumes nothing else on the same line
+    if ($line =~ /\.ifnb\s+(.*)/) {
+        if ($1) {
+            $line = ".if 1\n";
+        } else {
+            $line = ".if 0\n";
+        }
+    } elsif ($line =~ /\.ifb\s+(.*)/) {
+        if ($1) {
+            $line = ".if 0\n";
+        } else {
+            $line = ".if 1\n";
+        }
+    } elsif ($line =~ /\.ifc\s+(.*)\s*,\s*(.*)/) {
+        if ($1 eq $2) {
+            $line = ".if 1\n";
+        } else {
+            $line = ".if 0\n";
+        }
+    }
+
+    # handle .previous (only with regard to .section not .subsection)
+    if ($line =~ /\.(section|text|const_data)/) {
+        push(@sections, $line);
+    } elsif ($line =~ /\.previous/) {
+        if (!$sections[-2]) {
+            die ".previous without a previous section";
+        }
+        $line = $sections[-2];
+        push(@sections, $line);
+    }
+
+    # handle ldr <reg>, =<expr>
+    if ($line =~ /(.*)\s*ldr([\w\s\d]+)\s*,\s*=(.*)/) {
+        my $label = $literal_labels{$3};
+        if (!$label) {
+            $label = ".Literal_$literal_num";
+            $literal_num++;
+            $literal_labels{$3} = $label;
+        }
+        $line = "$1 ldr$2, $label\n";
+    } elsif ($line =~ /\.ltorg/) {
+        foreach my $literal (keys %literal_labels) {
+            $line .= "$literal_labels{$literal}:\n .word $literal\n";
+        }
+        %literal_labels = ();
+    }
+
+    # @l -> lo16()  @ha -> ha16()
+    $line =~ s/,\s+([^,]+)\@l(\s)/, lo16($1)$2/g;
+    $line =~ s/,\s+([^,]+)\@ha(\s)/, ha16($1)$2/g;
+
+    if ($line =~ /\.rept\s+(.*)/) {
+        $num_repts = $1;
+        $rept_lines = "\n";
+
+        # handle the possibility of repeating another directive on the same line
+        # .endr on the same line is not valid, I don't know if a non-directive is
+        if ($num_repts =~ s/(\.\w+.*)//) {
+            $rept_lines .= "$1\n";
+        }
+        $num_repts = eval($num_repts);
+    } elsif ($line =~ /\.endr/) {
+        for (1 .. $num_repts) {
+            print ASMFILE $rept_lines;
+        }
+        $rept_lines = '';
+    } elsif ($rept_lines) {
+        $rept_lines .= $line;
+    } else {
+        print ASMFILE $line;
+    }
+}
+
+print ASMFILE ".text\n";
+foreach my $literal (keys %literal_labels) {
+    print ASMFILE "$literal_labels{$literal}:\n .word $literal\n";
+}
+
+close(ASMFILE) or exit 1;