#!/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 () { 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('', ); 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 <) { 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/); @REQUIRES = filter(qw/neon/); &require(@REQUIRES); arm; } elsif ($opts{arch} =~ /^ppc/ ) { @ALL_ARCHS = filter(qw/vsx/); ppc; } elsif ($opts{arch} =~ /loongarch/ ) { @ALL_ARCHS = filter(qw/lsx lasx/); loongarch; } 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