1#!/usr/bin/env perl 2 3no strict 'refs'; 4use warnings; 5use Getopt::Long; 6Getopt::Long::Configure("auto_help"); 7 8my %ALL_FUNCS = (); 9my @ALL_ARCHS; 10my @ALL_FORWARD_DECLS; 11my @REQUIRES; 12 13my %opts = (); 14my %disabled = (); 15my %required = (); 16 17my @argv; 18foreach (@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; 26GetOptions( 27 \%opts, 28 'arch=s', 29 'sym=s', 30 'config=s', 31); 32 33foreach 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 40foreach my $defs_file (@ARGV) { 41 if (!-f $defs_file) { 42 warn "$defs_file: $!\n"; 43 Getopt::Long::HelpMessage('-exit' => 1); 44 } 45} 46 47open CONFIG_FILE, $opts{config} or 48 die "Error opening config file '$opts{config}': $!\n"; 49 50my %config = (); 51while (<CONFIG_FILE>) { 52 next if !/^CONFIG_/; 53 chomp; 54 my @pair = split /=/; 55 $config{$pair[0]} = $pair[1]; 56} 57close CONFIG_FILE; 58 59# 60# Routines for the RTCD DSL to call 61# 62sub vpx_config($) { 63 return (defined $config{$_[0]}) ? $config{$_[0]} : ""; 64} 65 66sub specialize { 67 my $fn=$_[0]; 68 shift; 69 foreach my $opt (@_) { 70 eval "\$${fn}_${opt}=${fn}_${opt}"; 71 } 72} 73 74sub add_proto { 75 my $fn = splice(@_, -2, 1); 76 $ALL_FUNCS{$fn} = \@_; 77 specialize $fn, "c"; 78} 79 80sub 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 101sub forward_decls { 102 push @ALL_FORWARD_DECLS, @_; 103} 104 105# 106# Include the user's directives 107# 108foreach 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# 118sub process_forward_decls() { 119 foreach (@ALL_FORWARD_DECLS) { 120 $_->(); 121 } 122} 123 124sub 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 148sub 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 169sub 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 191sub 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# 200sub 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 213extern "C" { 214#endif 215 216EOF 217 218process_forward_decls(); 219print "\n"; 220declare_function_pointers("c", @ALL_ARCHS); 221 222print <<EOF; 223void $opts{sym}(void); 224 225EOF 226} 227 228sub common_bottom() { 229 print <<EOF; 230 231#ifdef __cplusplus 232} // extern "C" 233#endif 234 235#endif 236EOF 237} 238 239sub 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" 252static void setup_rtcd_internal(void) 253{ 254 int flags = x86_simd_caps(); 255 256 (void)flags; 257 258EOF 259 260 set_function_pointers("c", @ALL_ARCHS); 261 262 print <<EOF; 263} 264#endif 265EOF 266 common_bottom; 267} 268 269sub 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" 284static void setup_rtcd_internal(void) 285{ 286 int flags = arm_cpu_caps(); 287 288 (void)flags; 289 290EOF 291 292 set_function_pointers("c", @ALL_ARCHS); 293 294 print <<EOF; 295} 296#endif 297EOF 298 common_bottom; 299} 300 301sub mips() { 302 determine_indirection("c", @ALL_ARCHS); 303 common_top; 304 305 print <<EOF; 306#include "vpx_config.h" 307 308#ifdef RTCD_C 309static void setup_rtcd_internal(void) 310{ 311EOF 312 313 set_function_pointers("c", @ALL_ARCHS); 314 315 print <<EOF; 316#if HAVE_DSPR2 317#if CONFIG_VP8 318void dsputil_static_init(); 319dsputil_static_init(); 320#endif 321#if CONFIG_VP9 322void vp9_dsputil_static_init(); 323vp9_dsputil_static_init(); 324#endif 325#endif 326} 327#endif 328EOF 329 common_bottom; 330} 331 332sub unoptimized() { 333 determine_indirection "c"; 334 common_top; 335 print <<EOF; 336#include "vpx_config.h" 337 338#ifdef RTCD_C 339static void setup_rtcd_internal(void) 340{ 341EOF 342 343 set_function_pointers "c"; 344 345 print <<EOF; 346} 347#endif 348EOF 349 common_bottom; 350} 351 352# 353# Main Driver 354# 355 356&require("c"); 357if ($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 394rtcd - 395 396=head1 SYNOPSIS 397 398Usage: rtcd.pl [options] FILE 399 400See 'perldoc rtcd.pl' for more details. 401 402=head1 DESCRIPTION 403 404Reads the Run Time CPU Detections definitions from FILE and generates a 405C header file on stdout. 406 407=head1 OPTIONS 408 409Options: 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