Home | History | Annotate | Download | only in make
      1 #!/usr/bin/env perl
      2 
      3 no strict 'refs';
      4 use warnings;
      5 use Getopt::Long;
      6 Getopt::Long::Configure("auto_help");
      7 
      8 my %ALL_FUNCS = ();
      9 my @ALL_ARCHS;
     10 my @ALL_FORWARD_DECLS;
     11 my @REQUIRES;
     12 
     13 my %opts = ();
     14 my %disabled = ();
     15 my %required = ();
     16 
     17 my @argv;
     18 foreach (@ARGV) {
     19   $disabled{$1} = 1, next if /--disable-(.*)/;
     20   $required{$1} = 1, next if /--require-(.*)/;
     21   push @argv, $_;
     22 }
     23 
     24 # NB: use GetOptions() instead of GetOptionsFromArray() for compatibility.
     25 @ARGV = @argv;
     26 GetOptions(
     27   \%opts,
     28   'arch=s',
     29   'sym=s',
     30   'config=s',
     31 );
     32 
     33 foreach my $opt (qw/arch config/) {
     34   if (!defined($opts{$opt})) {
     35     warn "--$opt is required!\n";
     36     Getopt::Long::HelpMessage('-exit' => 1);
     37   }
     38 }
     39 
     40 foreach my $defs_file (@ARGV) {
     41   if (!-f $defs_file) {
     42     warn "$defs_file: $!\n";
     43     Getopt::Long::HelpMessage('-exit' => 1);
     44   }
     45 }
     46 
     47 open CONFIG_FILE, $opts{config} or
     48   die "Error opening config file '$opts{config}': $!\n";
     49 
     50 my %config = ();
     51 while (<CONFIG_FILE>) {
     52   next if !/^CONFIG_/;
     53   chomp;
     54   my @pair = split /=/;
     55   $config{$pair[0]} = $pair[1];
     56 }
     57 close CONFIG_FILE;
     58 
     59 #
     60 # Routines for the RTCD DSL to call
     61 #
     62 sub vpx_config($) {
     63   return (defined $config{$_[0]}) ? $config{$_[0]} : "";
     64 }
     65 
     66 sub specialize {
     67   my $fn=$_[0];
     68   shift;
     69   foreach my $opt (@_) {
     70     eval "\$${fn}_${opt}=${fn}_${opt}";
     71   }
     72 }
     73 
     74 sub add_proto {
     75   my $fn = splice(@_, -2, 1);
     76   $ALL_FUNCS{$fn} = \@_;
     77   specialize $fn, "c";
     78 }
     79 
     80 sub require {
     81   foreach my $fn (keys %ALL_FUNCS) {
     82     foreach my $opt (@_) {
     83       my $ofn = eval "\$${fn}_${opt}";
     84       next if !$ofn;
     85 
     86       # if we already have a default, then we can disable it, as we know
     87       # we can do better.
     88       my $best = eval "\$${fn}_default";
     89       if ($best) {
     90         my $best_ofn = eval "\$${best}";
     91         if ($best_ofn && "$best_ofn" ne "$ofn") {
     92           eval "\$${best}_link = 'false'";
     93         }
     94       }
     95       eval "\$${fn}_default=${fn}_${opt}";
     96       eval "\$${fn}_${opt}_link='true'";
     97     }
     98   }
     99 }
    100 
    101 sub forward_decls {
    102   push @ALL_FORWARD_DECLS, @_;
    103 }
    104 
    105 #
    106 # Include the user's directives
    107 #
    108 foreach my $f (@ARGV) {
    109   open FILE, "<", $f or die "cannot open $f: $!\n";
    110   my $contents = join('', <FILE>);
    111   close FILE;
    112   eval $contents or warn "eval failed: $@\n";
    113 }
    114 
    115 #
    116 # Process the directives according to the command line
    117 #
    118 sub process_forward_decls() {
    119   foreach (@ALL_FORWARD_DECLS) {
    120     $_->();
    121   }
    122 }
    123 
    124 sub determine_indirection {
    125   vpx_config("CONFIG_RUNTIME_CPU_DETECT") eq "yes" or &require(@ALL_ARCHS);
    126   foreach my $fn (keys %ALL_FUNCS) {
    127     my $n = "";
    128     my @val = @{$ALL_FUNCS{$fn}};
    129     my $args = pop @val;
    130     my $rtyp = "@val";
    131     my $dfn = eval "\$${fn}_default";
    132     $dfn = eval "\$${dfn}";
    133     foreach my $opt (@_) {
    134       my $ofn = eval "\$${fn}_${opt}";
    135       next if !$ofn;
    136       my $link = eval "\$${fn}_${opt}_link";
    137       next if $link && $link eq "false";
    138       $n .= "x";
    139     }
    140     if ($n eq "x") {
    141       eval "\$${fn}_indirect = 'false'";
    142     } else {
    143       eval "\$${fn}_indirect = 'true'";
    144     }
    145   }
    146 }
    147 
    148 sub declare_function_pointers {
    149   foreach my $fn (sort keys %ALL_FUNCS) {
    150     my @val = @{$ALL_FUNCS{$fn}};
    151     my $args = pop @val;
    152     my $rtyp = "@val";
    153     my $dfn = eval "\$${fn}_default";
    154     $dfn = eval "\$${dfn}";
    155     foreach my $opt (@_) {
    156       my $ofn = eval "\$${fn}_${opt}";
    157       next if !$ofn;
    158       print "$rtyp ${ofn}($args);\n";
    159     }
    160     if (eval "\$${fn}_indirect" eq "false") {
    161       print "#define ${fn} ${dfn}\n";
    162     } else {
    163       print "RTCD_EXTERN $rtyp (*${fn})($args);\n";
    164     }
    165     print "\n";
    166   }
    167 }
    168 
    169 sub set_function_pointers {
    170   foreach my $fn (sort keys %ALL_FUNCS) {
    171     my @val = @{$ALL_FUNCS{$fn}};
    172     my $args = pop @val;
    173     my $rtyp = "@val";
    174     my $dfn = eval "\$${fn}_default";
    175     $dfn = eval "\$${dfn}";
    176     if (eval "\$${fn}_indirect" eq "true") {
    177       print "    $fn = $dfn;\n";
    178       foreach my $opt (@_) {
    179         my $ofn = eval "\$${fn}_${opt}";
    180         next if !$ofn;
    181         next if "$ofn" eq "$dfn";
    182         my $link = eval "\$${fn}_${opt}_link";
    183         next if $link && $link eq "false";
    184         my $cond = eval "\$have_${opt}";
    185         print "    if (${cond}) $fn = $ofn;\n"
    186       }
    187     }
    188   }
    189 }
    190 
    191 sub filter {
    192   my @filtered;
    193   foreach (@_) { push @filtered, $_ unless $disabled{$_}; }
    194   return @filtered;
    195 }
    196 
    197 #
    198 # Helper functions for generating the arch specific RTCD files
    199 #
    200 sub common_top() {
    201   my $include_guard = uc($opts{sym})."_H_";
    202   print <<EOF;
    203 #ifndef ${include_guard}
    204 #define ${include_guard}
    205 
    206 #ifdef RTCD_C
    207 #define RTCD_EXTERN
    208 #else
    209 #define RTCD_EXTERN extern
    210 #endif
    211 
    212 #ifdef __cplusplus
    213 extern "C" {
    214 #endif
    215 
    216 EOF
    217 
    218 process_forward_decls();
    219 print "\n";
    220 declare_function_pointers("c", @ALL_ARCHS);
    221 
    222 print <<EOF;
    223 void $opts{sym}(void);
    224 
    225 EOF
    226 }
    227 
    228 sub common_bottom() {
    229   print <<EOF;
    230 
    231 #ifdef __cplusplus
    232 }  // extern "C"
    233 #endif
    234 
    235 #endif
    236 EOF
    237 }
    238 
    239 sub x86() {
    240   determine_indirection("c", @ALL_ARCHS);
    241 
    242   # Assign the helper variable for each enabled extension
    243   foreach my $opt (@ALL_ARCHS) {
    244     my $opt_uc = uc $opt;
    245     eval "\$have_${opt}=\"flags & HAS_${opt_uc}\"";
    246   }
    247 
    248   common_top;
    249   print <<EOF;
    250 #ifdef RTCD_C
    251 #include "vpx_ports/x86.h"
    252 static void setup_rtcd_internal(void)
    253 {
    254     int flags = x86_simd_caps();
    255 
    256     (void)flags;
    257 
    258 EOF
    259 
    260   set_function_pointers("c", @ALL_ARCHS);
    261 
    262   print <<EOF;
    263 }
    264 #endif
    265 EOF
    266   common_bottom;
    267 }
    268 
    269 sub arm() {
    270   determine_indirection("c", @ALL_ARCHS);
    271 
    272   # Assign the helper variable for each enabled extension
    273   foreach my $opt (@ALL_ARCHS) {
    274     my $opt_uc = uc $opt;
    275     eval "\$have_${opt}=\"flags & HAS_${opt_uc}\"";
    276   }
    277 
    278   common_top;
    279   print <<EOF;
    280 #include "vpx_config.h"
    281 
    282 #ifdef RTCD_C
    283 #include "vpx_ports/arm.h"
    284 static void setup_rtcd_internal(void)
    285 {
    286     int flags = arm_cpu_caps();
    287 
    288     (void)flags;
    289 
    290 EOF
    291 
    292   set_function_pointers("c", @ALL_ARCHS);
    293 
    294   print <<EOF;
    295 }
    296 #endif
    297 EOF
    298   common_bottom;
    299 }
    300 
    301 sub mips() {
    302   determine_indirection("c", @ALL_ARCHS);
    303   common_top;
    304 
    305   print <<EOF;
    306 #include "vpx_config.h"
    307 
    308 #ifdef RTCD_C
    309 static void setup_rtcd_internal(void)
    310 {
    311 EOF
    312 
    313   set_function_pointers("c", @ALL_ARCHS);
    314 
    315   print <<EOF;
    316 #if HAVE_DSPR2
    317 #if CONFIG_VP8
    318 void dsputil_static_init();
    319 dsputil_static_init();
    320 #endif
    321 #if CONFIG_VP9
    322 void vp9_dsputil_static_init();
    323 vp9_dsputil_static_init();
    324 #endif
    325 #endif
    326 }
    327 #endif
    328 EOF
    329   common_bottom;
    330 }
    331 
    332 sub unoptimized() {
    333   determine_indirection "c";
    334   common_top;
    335   print <<EOF;
    336 #include "vpx_config.h"
    337 
    338 #ifdef RTCD_C
    339 static void setup_rtcd_internal(void)
    340 {
    341 EOF
    342 
    343   set_function_pointers "c";
    344 
    345   print <<EOF;
    346 }
    347 #endif
    348 EOF
    349   common_bottom;
    350 }
    351 
    352 #
    353 # Main Driver
    354 #
    355 
    356 &require("c");
    357 if ($opts{arch} eq 'x86') {
    358   @ALL_ARCHS = filter(qw/mmx sse sse2 sse3 ssse3 sse4_1 avx avx2/);
    359   x86;
    360 } elsif ($opts{arch} eq 'x86_64') {
    361   @ALL_ARCHS = filter(qw/mmx sse sse2 sse3 ssse3 sse4_1 avx avx2/);
    362   @REQUIRES = filter(keys %required ? keys %required : qw/mmx sse sse2/);
    363   &require(@REQUIRES);
    364   x86;
    365 } elsif ($opts{arch} eq 'mips32') {
    366   @ALL_ARCHS = filter(qw/mips32/);
    367   open CONFIG_FILE, $opts{config} or
    368     die "Error opening config file '$opts{config}': $!\n";
    369   while (<CONFIG_FILE>) {
    370     if (/HAVE_DSPR2=yes/) {
    371       @ALL_ARCHS = filter(qw/mips32 dspr2/);
    372       last;
    373     }
    374   }
    375   close CONFIG_FILE;
    376   mips;
    377 } elsif ($opts{arch} eq 'armv5te') {
    378   @ALL_ARCHS = filter(qw/edsp/);
    379   arm;
    380 } elsif ($opts{arch} eq 'armv6') {
    381   @ALL_ARCHS = filter(qw/edsp media/);
    382   arm;
    383 } elsif ($opts{arch} eq 'armv7') {
    384   @ALL_ARCHS = filter(qw/edsp media neon/);
    385   arm;
    386 } else {
    387   unoptimized;
    388 }
    389 
    390 __END__
    391 
    392 =head1 NAME
    393 
    394 rtcd -
    395 
    396 =head1 SYNOPSIS
    397 
    398 Usage: rtcd.pl [options] FILE
    399 
    400 See 'perldoc rtcd.pl' for more details.
    401 
    402 =head1 DESCRIPTION
    403 
    404 Reads the Run Time CPU Detections definitions from FILE and generates a
    405 C header file on stdout.
    406 
    407 =head1 OPTIONS
    408 
    409 Options:
    410   --arch=ARCH       Architecture to generate defs for (required)
    411   --disable-EXT     Disable support for EXT extensions
    412   --require-EXT     Require support for EXT extensions
    413   --sym=SYMBOL      Unique symbol to use for RTCD initialization function
    414   --config=FILE     File with CONFIG_FOO=yes lines to parse
    415