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_|HAVE_)/; 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 212EOF 213 214process_forward_decls(); 215print <<EOF; 216 217#ifdef __cplusplus 218extern "C" { 219#endif 220 221EOF 222declare_function_pointers("c", @ALL_ARCHS); 223 224print <<EOF; 225void $opts{sym}(void); 226 227EOF 228} 229 230sub common_bottom() { 231 print <<EOF; 232 233#ifdef __cplusplus 234} // extern "C" 235#endif 236 237#endif 238EOF 239} 240 241sub x86() { 242 determine_indirection("c", @ALL_ARCHS); 243 244 # Assign the helper variable for each enabled extension 245 foreach my $opt (@ALL_ARCHS) { 246 my $opt_uc = uc $opt; 247 eval "\$have_${opt}=\"flags & HAS_${opt_uc}\""; 248 } 249 250 common_top; 251 print <<EOF; 252#ifdef RTCD_C 253#include "vpx_ports/x86.h" 254static void setup_rtcd_internal(void) 255{ 256 int flags = x86_simd_caps(); 257 258 (void)flags; 259 260EOF 261 262 set_function_pointers("c", @ALL_ARCHS); 263 264 print <<EOF; 265} 266#endif 267EOF 268 common_bottom; 269} 270 271sub arm() { 272 determine_indirection("c", @ALL_ARCHS); 273 274 # Assign the helper variable for each enabled extension 275 foreach my $opt (@ALL_ARCHS) { 276 my $opt_uc = uc $opt; 277 # Enable neon assembly based on HAVE_NEON logic instead of adding new 278 # HAVE_NEON_ASM logic 279 if ($opt eq 'neon_asm') { $opt_uc = 'NEON' } 280 eval "\$have_${opt}=\"flags & HAS_${opt_uc}\""; 281 } 282 283 common_top; 284 print <<EOF; 285#include "vpx_config.h" 286 287#ifdef RTCD_C 288#include "vpx_ports/arm.h" 289static void setup_rtcd_internal(void) 290{ 291 int flags = arm_cpu_caps(); 292 293 (void)flags; 294 295EOF 296 297 set_function_pointers("c", @ALL_ARCHS); 298 299 print <<EOF; 300} 301#endif 302EOF 303 common_bottom; 304} 305 306sub mips() { 307 determine_indirection("c", @ALL_ARCHS); 308 common_top; 309 310 print <<EOF; 311#include "vpx_config.h" 312 313#ifdef RTCD_C 314static void setup_rtcd_internal(void) 315{ 316EOF 317 318 set_function_pointers("c", @ALL_ARCHS); 319 320 print <<EOF; 321#if HAVE_DSPR2 322void vpx_dsputil_static_init(); 323#if CONFIG_VP8 324void dsputil_static_init(); 325#endif 326 327vpx_dsputil_static_init(); 328#if CONFIG_VP8 329dsputil_static_init(); 330#endif 331#endif 332} 333#endif 334EOF 335 common_bottom; 336} 337 338sub ppc() { 339 determine_indirection("c", @ALL_ARCHS); 340 341 # Assign the helper variable for each enabled extension 342 foreach my $opt (@ALL_ARCHS) { 343 my $opt_uc = uc $opt; 344 eval "\$have_${opt}=\"flags & HAS_${opt_uc}\""; 345 } 346 347 common_top; 348 print <<EOF; 349#include "vpx_config.h" 350 351#ifdef RTCD_C 352#include "vpx_ports/ppc.h" 353static void setup_rtcd_internal(void) 354{ 355 int flags = ppc_simd_caps(); 356 (void)flags; 357EOF 358 359 set_function_pointers("c", @ALL_ARCHS); 360 361 print <<EOF; 362} 363#endif 364EOF 365 common_bottom; 366} 367 368sub unoptimized() { 369 determine_indirection "c"; 370 common_top; 371 print <<EOF; 372#include "vpx_config.h" 373 374#ifdef RTCD_C 375static void setup_rtcd_internal(void) 376{ 377EOF 378 379 set_function_pointers "c"; 380 381 print <<EOF; 382} 383#endif 384EOF 385 common_bottom; 386} 387 388# 389# Main Driver 390# 391 392&require("c"); 393if ($opts{arch} eq 'x86') { 394 @ALL_ARCHS = filter(qw/mmx sse sse2 sse3 ssse3 sse4_1 avx avx2/); 395 x86; 396} elsif ($opts{arch} eq 'x86_64') { 397 @ALL_ARCHS = filter(qw/mmx sse sse2 sse3 ssse3 sse4_1 avx avx2/); 398 @REQUIRES = filter(keys %required ? keys %required : qw/mmx sse sse2/); 399 &require(@REQUIRES); 400 x86; 401} elsif ($opts{arch} eq 'mips32' || $opts{arch} eq 'mips64') { 402 @ALL_ARCHS = filter("$opts{arch}"); 403 open CONFIG_FILE, $opts{config} or 404 die "Error opening config file '$opts{config}': $!\n"; 405 while (<CONFIG_FILE>) { 406 if (/HAVE_DSPR2=yes/) { 407 @ALL_ARCHS = filter("$opts{arch}", qw/dspr2/); 408 last; 409 } 410 if (/HAVE_MSA=yes/) { 411 @ALL_ARCHS = filter("$opts{arch}", qw/msa/); 412 last; 413 } 414 } 415 close CONFIG_FILE; 416 mips; 417} elsif ($opts{arch} =~ /armv7\w?/) { 418 @ALL_ARCHS = filter(qw/neon_asm neon/); 419 arm; 420} elsif ($opts{arch} eq 'armv8' || $opts{arch} eq 'arm64' ) { 421 @ALL_ARCHS = filter(qw/neon/); 422 arm; 423} elsif ($opts{arch} =~ /^ppc/ ) { 424 @ALL_ARCHS = filter(qw/vsx/); 425 ppc; 426} else { 427 unoptimized; 428} 429 430__END__ 431 432=head1 NAME 433 434rtcd - 435 436=head1 SYNOPSIS 437 438Usage: rtcd.pl [options] FILE 439 440See 'perldoc rtcd.pl' for more details. 441 442=head1 DESCRIPTION 443 444Reads the Run Time CPU Detections definitions from FILE and generates a 445C header file on stdout. 446 447=head1 OPTIONS 448 449Options: 450 --arch=ARCH Architecture to generate defs for (required) 451 --disable-EXT Disable support for EXT extensions 452 --require-EXT Require support for EXT extensions 453 --sym=SYMBOL Unique symbol to use for RTCD initialization function 454 --config=FILE File with CONFIG_FOO=yes lines to parse 455