1#!/usr/bin/env perl 2 3no strict 'refs'; 4use warnings; 5use Getopt::Long; 6Getopt::Long::Configure("auto_help") if $Getopt::Long::VERSION > 2.32; 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 # Enable neon assembly based on HAVE_NEON logic instead of adding new 276 # HAVE_NEON_ASM logic 277 if ($opt eq 'neon_asm') { $opt_uc = 'NEON' } 278 eval "\$have_${opt}=\"flags & HAS_${opt_uc}\""; 279 } 280 281 common_top; 282 print <<EOF; 283#include "vpx_config.h" 284 285#ifdef RTCD_C 286#include "vpx_ports/arm.h" 287static void setup_rtcd_internal(void) 288{ 289 int flags = arm_cpu_caps(); 290 291 (void)flags; 292 293EOF 294 295 set_function_pointers("c", @ALL_ARCHS); 296 297 print <<EOF; 298} 299#endif 300EOF 301 common_bottom; 302} 303 304sub mips() { 305 determine_indirection("c", @ALL_ARCHS); 306 common_top; 307 308 print <<EOF; 309#include "vpx_config.h" 310 311#ifdef RTCD_C 312static void setup_rtcd_internal(void) 313{ 314EOF 315 316 set_function_pointers("c", @ALL_ARCHS); 317 318 print <<EOF; 319#if HAVE_DSPR2 320#if CONFIG_VP8 321void dsputil_static_init(); 322dsputil_static_init(); 323#endif 324#if CONFIG_VP9 325void vp9_dsputil_static_init(); 326vp9_dsputil_static_init(); 327#endif 328#endif 329} 330#endif 331EOF 332 common_bottom; 333} 334 335sub unoptimized() { 336 determine_indirection "c"; 337 common_top; 338 print <<EOF; 339#include "vpx_config.h" 340 341#ifdef RTCD_C 342static void setup_rtcd_internal(void) 343{ 344EOF 345 346 set_function_pointers "c"; 347 348 print <<EOF; 349} 350#endif 351EOF 352 common_bottom; 353} 354 355# 356# Main Driver 357# 358 359&require("c"); 360if ($opts{arch} eq 'x86') { 361 @ALL_ARCHS = filter(qw/mmx sse sse2 sse3 ssse3 sse4_1 avx avx2/); 362 x86; 363} elsif ($opts{arch} eq 'x86_64') { 364 @ALL_ARCHS = filter(qw/mmx sse sse2 sse3 ssse3 sse4_1 avx avx2/); 365 @REQUIRES = filter(keys %required ? keys %required : qw/mmx sse sse2/); 366 &require(@REQUIRES); 367 x86; 368} elsif ($opts{arch} eq 'mips32') { 369 @ALL_ARCHS = filter(qw/mips32/); 370 open CONFIG_FILE, $opts{config} or 371 die "Error opening config file '$opts{config}': $!\n"; 372 while (<CONFIG_FILE>) { 373 if (/HAVE_DSPR2=yes/) { 374 @ALL_ARCHS = filter(qw/mips32 dspr2/); 375 last; 376 } 377 } 378 close CONFIG_FILE; 379 mips; 380} elsif ($opts{arch} eq 'armv5te') { 381 @ALL_ARCHS = filter(qw/edsp/); 382 arm; 383} elsif ($opts{arch} eq 'armv6') { 384 @ALL_ARCHS = filter(qw/edsp media/); 385 arm; 386} elsif ($opts{arch} eq 'armv7') { 387 @ALL_ARCHS = filter(qw/edsp media neon_asm neon/); 388 @REQUIRES = filter(keys %required ? keys %required : qw/media/); 389 &require(@REQUIRES); 390 arm; 391} elsif ($opts{arch} eq 'armv8') { 392 @ALL_ARCHS = filter(qw/neon/); 393 arm; 394} else { 395 unoptimized; 396} 397 398__END__ 399 400=head1 NAME 401 402rtcd - 403 404=head1 SYNOPSIS 405 406Usage: rtcd.pl [options] FILE 407 408See 'perldoc rtcd.pl' for more details. 409 410=head1 DESCRIPTION 411 412Reads the Run Time CPU Detections definitions from FILE and generates a 413C header file on stdout. 414 415=head1 OPTIONS 416 417Options: 418 --arch=ARCH Architecture to generate defs for (required) 419 --disable-EXT Disable support for EXT extensions 420 --require-EXT Require support for EXT extensions 421 --sym=SYMBOL Unique symbol to use for RTCD initialization function 422 --config=FILE File with CONFIG_FOO=yes lines to parse 423