vpx/build/make/rtcd.pl
James Zern 805078a1bf build: convert rtcd.sh to perl
significantly speeds up file generation.

the goal of this change is to convert rtcd.sh to perl as directly as
possible to allow for simple comparison. future changes can make it more
perl-like.

---
Linux
    [CREATE] vpx_scale_rtcd.h
real    0m0.485s ->    0m0.022s
    [CREATE] vp8_rtcd.h
real    0m4.619s ->    0m0.060s
    [CREATE] vp9_rtcd.h
real    0m10.102s ->    0m0.087s

Windows
    [CREATE] vpx_scale_rtcd.h
real    0m8.360s ->    0m0.080s
    [CREATE] vp8_rtcd.h
real    1m8.083s ->    0m0.160s
    [CREATE] vp9_rtcd.h
real    2m6.489s ->    0m0.233s

Change-Id: Idfb71188206c91237d6a3c3a81dfe00d103f11ee
2014-03-03 14:47:11 -08:00

415 lines
7.8 KiB
Perl
Executable File

#!/usr/bin/env perl
no strict 'refs';
use warnings;
use Getopt::Long;
Getopt::Long::Configure("auto_help");
my %ALL_FUNCS = ();
my @ALL_ARCHS;
my @ALL_FORWARD_DECLS;
my @REQUIRES;
my %opts = ();
my %disabled = ();
my %required = ();
my @argv;
foreach (@ARGV) {
$disabled{$1} = 1, next if /--disable-(.*)/;
$required{$1} = 1, next if /--require-(.*)/;
push @argv, $_;
}
# NB: use GetOptions() instead of GetOptionsFromArray() for compatibility.
@ARGV = @argv;
GetOptions(
\%opts,
'arch=s',
'sym=s',
'config=s',
);
foreach my $opt (qw/arch config/) {
if (!defined($opts{$opt})) {
warn "--$opt is required!\n";
Getopt::Long::HelpMessage('-exit' => 1);
}
}
foreach my $defs_file (@ARGV) {
if (!-f $defs_file) {
warn "$defs_file: $!\n";
Getopt::Long::HelpMessage('-exit' => 1);
}
}
open CONFIG_FILE, $opts{config} or
die "Error opening config file '$opts{config}': $!\n";
my %config = ();
while (<CONFIG_FILE>) {
next if !/^CONFIG_/;
chomp;
my @pair = split /=/;
$config{$pair[0]} = $pair[1];
}
close CONFIG_FILE;
#
# Routines for the RTCD DSL to call
#
sub vpx_config($) {
return (defined $config{$_[0]}) ? $config{$_[0]} : "";
}
sub specialize {
my $fn=$_[0];
shift;
foreach my $opt (@_) {
eval "\$${fn}_${opt}=${fn}_${opt}";
}
}
sub add_proto {
my $fn = splice(@_, -2, 1);
$ALL_FUNCS{$fn} = \@_;
specialize $fn, "c";
}
sub require {
foreach my $fn (keys %ALL_FUNCS) {
foreach my $opt (@_) {
my $ofn = eval "\$${fn}_${opt}";
next if !$ofn;
# if we already have a default, then we can disable it, as we know
# we can do better.
my $best = eval "\$${fn}_default";
if ($best) {
my $best_ofn = eval "\$${best}";
if ($best_ofn && "$best_ofn" ne "$ofn") {
eval "\$${best}_link = 'false'";
}
}
eval "\$${fn}_default=${fn}_${opt}";
eval "\$${fn}_${opt}_link='true'";
}
}
}
sub forward_decls {
push @ALL_FORWARD_DECLS, @_;
}
#
# Include the user's directives
#
foreach my $f (@ARGV) {
open FILE, "<", $f or die "cannot open $f: $!\n";
my $contents = join('', <FILE>);
close FILE;
eval $contents or warn "eval failed: $@\n";
}
#
# Process the directives according to the command line
#
sub process_forward_decls() {
foreach (@ALL_FORWARD_DECLS) {
$_->();
}
}
sub determine_indirection {
vpx_config("CONFIG_RUNTIME_CPU_DETECT") eq "yes" or &require(@ALL_ARCHS);
foreach my $fn (keys %ALL_FUNCS) {
my $n = "";
my @val = @{$ALL_FUNCS{$fn}};
my $args = pop @val;
my $rtyp = "@val";
my $dfn = eval "\$${fn}_default";
$dfn = eval "\$${dfn}";
foreach my $opt (@_) {
my $ofn = eval "\$${fn}_${opt}";
next if !$ofn;
my $link = eval "\$${fn}_${opt}_link";
next if $link && $link eq "false";
$n .= "x";
}
if ($n eq "x") {
eval "\$${fn}_indirect = 'false'";
} else {
eval "\$${fn}_indirect = 'true'";
}
}
}
sub declare_function_pointers {
foreach my $fn (sort keys %ALL_FUNCS) {
my @val = @{$ALL_FUNCS{$fn}};
my $args = pop @val;
my $rtyp = "@val";
my $dfn = eval "\$${fn}_default";
$dfn = eval "\$${dfn}";
foreach my $opt (@_) {
my $ofn = eval "\$${fn}_${opt}";
next if !$ofn;
print "$rtyp ${ofn}($args);\n";
}
if (eval "\$${fn}_indirect" eq "false") {
print "#define ${fn} ${dfn}\n";
} else {
print "RTCD_EXTERN $rtyp (*${fn})($args);\n";
}
print "\n";
}
}
sub set_function_pointers {
foreach my $fn (sort keys %ALL_FUNCS) {
my @val = @{$ALL_FUNCS{$fn}};
my $args = pop @val;
my $rtyp = "@val";
my $dfn = eval "\$${fn}_default";
$dfn = eval "\$${dfn}";
if (eval "\$${fn}_indirect" eq "true") {
print " $fn = $dfn;\n";
foreach my $opt (@_) {
my $ofn = eval "\$${fn}_${opt}";
next if !$ofn;
next if "$ofn" eq "$dfn";
my $link = eval "\$${fn}_${opt}_link";
next if $link && $link eq "false";
my $cond = eval "\$have_${opt}";
print " if (${cond}) $fn = $ofn;\n"
}
}
}
}
sub filter {
my @filtered;
foreach (@_) { push @filtered, $_ unless $disabled{$_}; }
return @filtered;
}
#
# Helper functions for generating the arch specific RTCD files
#
sub common_top() {
my $include_guard = uc($opts{sym})."_H_";
print <<EOF;
#ifndef ${include_guard}
#define ${include_guard}
#ifdef RTCD_C
#define RTCD_EXTERN
#else
#define RTCD_EXTERN extern
#endif
#ifdef __cplusplus
extern "C" {
#endif
EOF
process_forward_decls();
print "\n";
declare_function_pointers("c", @ALL_ARCHS);
print <<EOF;
void $opts{sym}(void);
EOF
}
sub common_bottom() {
print <<EOF;
#ifdef __cplusplus
} // extern "C"
#endif
#endif
EOF
}
sub x86() {
determine_indirection("c", @ALL_ARCHS);
# Assign the helper variable for each enabled extension
foreach my $opt (@ALL_ARCHS) {
my $opt_uc = uc $opt;
eval "\$have_${opt}=\"flags & HAS_${opt_uc}\"";
}
common_top;
print <<EOF;
#ifdef RTCD_C
#include "vpx_ports/x86.h"
static void setup_rtcd_internal(void)
{
int flags = x86_simd_caps();
(void)flags;
EOF
set_function_pointers("c", @ALL_ARCHS);
print <<EOF;
}
#endif
EOF
common_bottom;
}
sub arm() {
determine_indirection("c", @ALL_ARCHS);
# Assign the helper variable for each enabled extension
foreach my $opt (@ALL_ARCHS) {
my $opt_uc = uc $opt;
eval "\$have_${opt}=\"flags & HAS_${opt_uc}\"";
}
common_top;
print <<EOF;
#include "vpx_config.h"
#ifdef RTCD_C
#include "vpx_ports/arm.h"
static void setup_rtcd_internal(void)
{
int flags = arm_cpu_caps();
(void)flags;
EOF
set_function_pointers("c", @ALL_ARCHS);
print <<EOF;
}
#endif
EOF
common_bottom;
}
sub mips() {
determine_indirection("c", @ALL_ARCHS);
common_top;
print <<EOF;
#include "vpx_config.h"
#ifdef RTCD_C
static void setup_rtcd_internal(void)
{
EOF
set_function_pointers("c", @ALL_ARCHS);
print <<EOF;
#if HAVE_DSPR2
#if CONFIG_VP8
void dsputil_static_init();
dsputil_static_init();
#endif
#if CONFIG_VP9
void vp9_dsputil_static_init();
vp9_dsputil_static_init();
#endif
#endif
}
#endif
EOF
common_bottom;
}
sub unoptimized() {
determine_indirection "c";
common_top;
print <<EOF;
#include "vpx_config.h"
#ifdef RTCD_C
static void setup_rtcd_internal(void)
{
EOF
set_function_pointers "c";
print <<EOF;
}
#endif
EOF
common_bottom;
}
#
# Main Driver
#
&require("c");
if ($opts{arch} eq 'x86') {
@ALL_ARCHS = filter(qw/mmx sse sse2 sse3 ssse3 sse4_1 avx avx2/);
x86;
} elsif ($opts{arch} eq 'x86_64') {
@ALL_ARCHS = filter(qw/mmx sse sse2 sse3 ssse3 sse4_1 avx avx2/);
@REQUIRES = filter(keys %required ? keys %required : qw/mmx sse sse2/);
&require(@REQUIRES);
x86;
} elsif ($opts{arch} eq 'mips32') {
@ALL_ARCHS = filter(qw/mips32/);
open CONFIG_FILE, $opts{config} or
die "Error opening config file '$opts{config}': $!\n";
while (<CONFIG_FILE>) {
if (/HAVE_DSPR2=yes/) {
@ALL_ARCHS = filter(qw/mips32 dspr2/);
last;
}
}
close CONFIG_FILE;
mips;
} elsif ($opts{arch} eq 'armv5te') {
@ALL_ARCHS = filter(qw/edsp/);
arm;
} elsif ($opts{arch} eq 'armv6') {
@ALL_ARCHS = filter(qw/edsp media/);
arm;
} elsif ($opts{arch} eq 'armv7') {
@ALL_ARCHS = filter(qw/edsp media neon/);
arm;
} else {
unoptimized;
}
__END__
=head1 NAME
rtcd -
=head1 SYNOPSIS
Usage: rtcd.pl [options] FILE
See 'perldoc rtcd.pl' for more details.
=head1 DESCRIPTION
Reads the Run Time CPU Detections definitions from FILE and generates a
C header file on stdout.
=head1 OPTIONS
Options:
--arch=ARCH Architecture to generate defs for (required)
--disable-EXT Disable support for EXT extensions
--require-EXT Require support for EXT extensions
--sym=SYMBOL Unique symbol to use for RTCD initialization function
--config=FILE File with CONFIG_FOO=yes lines to parse