ref: 3a38edea2cd114d53914cab017cab2e43a600031
dir: /build/make/rtcd.pl/
#!/usr/bin/env perl ## ## Copyright (c) 2017 The WebM project authors. All Rights Reserved. ## ## Use of this source code is governed by a BSD-style license ## that can be found in the LICENSE file in the root of the source ## tree. An additional intellectual property rights grant can be found ## in the file PATENTS. All contributing project authors may ## be found in the AUTHORS file in the root of the source tree. ## no strict 'refs'; use warnings; use Getopt::Long; Getopt::Long::Configure("auto_help") if $Getopt::Long::VERSION > 2.32; 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_|HAVE_)/; 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; // This file is generated. Do not edit. #ifndef ${include_guard} #define ${include_guard} #ifdef RTCD_C #define RTCD_EXTERN #else #define RTCD_EXTERN extern #endif EOF process_forward_decls(); print <<EOF; #ifdef __cplusplus extern "C" { #endif EOF 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; # Enable neon assembly based on HAVE_NEON logic instead of adding new # HAVE_NEON_ASM logic if ($opt eq 'neon_asm') { $opt_uc = 'NEON' } 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); # 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/mips.h" static void setup_rtcd_internal(void) { int flags = mips_cpu_caps(); (void)flags; EOF set_function_pointers("c", @ALL_ARCHS); print <<EOF; #if HAVE_DSPR2 void vpx_dsputil_static_init(); #if CONFIG_VP8 void dsputil_static_init(); #endif vpx_dsputil_static_init(); #if CONFIG_VP8 dsputil_static_init(); #endif #endif } #endif EOF common_bottom; } sub ppc() { 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/ppc.h" static void setup_rtcd_internal(void) { int flags = ppc_simd_caps(); (void)flags; EOF set_function_pointers("c", @ALL_ARCHS); print <<EOF; } #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"); &require(keys %required); if ($opts{arch} eq 'x86') { @ALL_ARCHS = filter(qw/mmx sse sse2 sse3 ssse3 sse4_1 avx avx2 avx512/); x86; } elsif ($opts{arch} eq 'x86_64') { @ALL_ARCHS = filter(qw/mmx sse sse2 sse3 ssse3 sse4_1 avx avx2 avx512/); @REQUIRES = filter(qw/mmx sse sse2/); &require(@REQUIRES); x86; } elsif ($opts{arch} eq 'mips32' || $opts{arch} eq 'mips64') { my $have_dspr2 = 0; my $have_msa = 0; my $have_mmi = 0; @ALL_ARCHS = filter("$opts{arch}"); open CONFIG_FILE, $opts{config} or die "Error opening config file '$opts{config}': $!\n"; while (<CONFIG_FILE>) { if (/HAVE_DSPR2=yes/) { $have_dspr2 = 1; } if (/HAVE_MSA=yes/) { $have_msa = 1; } if (/HAVE_MMI=yes/) { $have_mmi = 1; } } close CONFIG_FILE; if ($have_dspr2 == 1) { @ALL_ARCHS = filter("$opts{arch}", qw/dspr2/); } elsif ($have_msa == 1 && $have_mmi == 1) { @ALL_ARCHS = filter("$opts{arch}", qw/mmi msa/); } elsif ($have_msa == 1) { @ALL_ARCHS = filter("$opts{arch}", qw/msa/); } elsif ($have_mmi == 1) { @ALL_ARCHS = filter("$opts{arch}", qw/mmi/); } else { unoptimized; } mips; } elsif ($opts{arch} =~ /armv7\w?/) { @ALL_ARCHS = filter(qw/neon_asm neon/); arm; } elsif ($opts{arch} eq 'armv8' || $opts{arch} eq 'arm64' ) { @ALL_ARCHS = filter(qw/neon/); &require("neon"); arm; } elsif ($opts{arch} =~ /^ppc/ ) { @ALL_ARCHS = filter(qw/vsx/); ppc; } 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