1#!/usr/bin/env perl 2## 3## Copyright (c) 2017 The WebM project authors. All Rights Reserved. 4## 5## Use of this source code is governed by a BSD-style license 6## that can be found in the LICENSE file in the root of the source 7## tree. An additional intellectual property rights grant can be found 8## in the file PATENTS. All contributing project authors may 9## be found in the AUTHORS file in the root of the source tree. 10## 11 12no strict 'refs'; 13use warnings; 14use Getopt::Long; 15Getopt::Long::Configure("auto_help") if $Getopt::Long::VERSION > 2.32; 16 17my %ALL_FUNCS = (); 18my @ALL_ARCHS; 19my @ALL_FORWARD_DECLS; 20my @REQUIRES; 21 22my %opts = (); 23my %disabled = (); 24my %required = (); 25 26my @argv; 27foreach (@ARGV) { 28 $disabled{$1} = 1, next if /--disable-(.*)/; 29 $required{$1} = 1, next if /--require-(.*)/; 30 push @argv, $_; 31} 32 33# NB: use GetOptions() instead of GetOptionsFromArray() for compatibility. 34@ARGV = @argv; 35GetOptions( 36 \%opts, 37 'arch=s', 38 'sym=s', 39 'config=s', 40); 41 42foreach my $opt (qw/arch config/) { 43 if (!defined($opts{$opt})) { 44 warn "--$opt is required!\n"; 45 Getopt::Long::HelpMessage('-exit' => 1); 46 } 47} 48 49foreach my $defs_file (@ARGV) { 50 if (!-f $defs_file) { 51 warn "$defs_file: $!\n"; 52 Getopt::Long::HelpMessage('-exit' => 1); 53 } 54} 55 56open CONFIG_FILE, $opts{config} or 57 die "Error opening config file '$opts{config}': $!\n"; 58 59my %config = (); 60while (<CONFIG_FILE>) { 61 next if !/^(?:CONFIG_|HAVE_)/; 62 chomp; 63 my @pair = split /=/; 64 $config{$pair[0]} = $pair[1]; 65} 66close CONFIG_FILE; 67 68# 69# Routines for the RTCD DSL to call 70# 71sub vpx_config($) { 72 return (defined $config{$_[0]}) ? $config{$_[0]} : ""; 73} 74 75sub specialize { 76 my $fn=$_[0]; 77 shift; 78 foreach my $opt (@_) { 79 eval "\$${fn}_${opt}=${fn}_${opt}"; 80 } 81} 82 83sub add_proto { 84 my $fn = splice(@_, -2, 1); 85 $ALL_FUNCS{$fn} = \@_; 86 specialize $fn, "c"; 87} 88 89sub require { 90 foreach my $fn (keys %ALL_FUNCS) { 91 foreach my $opt (@_) { 92 my $ofn = eval "\$${fn}_${opt}"; 93 next if !$ofn; 94 95 # if we already have a default, then we can disable it, as we know 96 # we can do better. 97 my $best = eval "\$${fn}_default"; 98 if ($best) { 99 my $best_ofn = eval "\$${best}"; 100 if ($best_ofn && "$best_ofn" ne "$ofn") { 101 eval "\$${best}_link = 'false'"; 102 } 103 } 104 eval "\$${fn}_default=${fn}_${opt}"; 105 eval "\$${fn}_${opt}_link='true'"; 106 } 107 } 108} 109 110sub forward_decls { 111 push @ALL_FORWARD_DECLS, @_; 112} 113 114# 115# Include the user's directives 116# 117foreach my $f (@ARGV) { 118 open FILE, "<", $f or die "cannot open $f: $!\n"; 119 my $contents = join('', <FILE>); 120 close FILE; 121 eval $contents or warn "eval failed: $@\n"; 122} 123 124# 125# Process the directives according to the command line 126# 127sub process_forward_decls() { 128 foreach (@ALL_FORWARD_DECLS) { 129 $_->(); 130 } 131} 132 133sub determine_indirection { 134 vpx_config("CONFIG_RUNTIME_CPU_DETECT") eq "yes" or &require(@ALL_ARCHS); 135 foreach my $fn (keys %ALL_FUNCS) { 136 my $n = ""; 137 my @val = @{$ALL_FUNCS{$fn}}; 138 my $args = pop @val; 139 my $rtyp = "@val"; 140 my $dfn = eval "\$${fn}_default"; 141 $dfn = eval "\$${dfn}"; 142 foreach my $opt (@_) { 143 my $ofn = eval "\$${fn}_${opt}"; 144 next if !$ofn; 145 my $link = eval "\$${fn}_${opt}_link"; 146 next if $link && $link eq "false"; 147 $n .= "x"; 148 } 149 if ($n eq "x") { 150 eval "\$${fn}_indirect = 'false'"; 151 } else { 152 eval "\$${fn}_indirect = 'true'"; 153 } 154 } 155} 156 157sub declare_function_pointers { 158 foreach my $fn (sort keys %ALL_FUNCS) { 159 my @val = @{$ALL_FUNCS{$fn}}; 160 my $args = pop @val; 161 my $rtyp = "@val"; 162 my $dfn = eval "\$${fn}_default"; 163 $dfn = eval "\$${dfn}"; 164 foreach my $opt (@_) { 165 my $ofn = eval "\$${fn}_${opt}"; 166 next if !$ofn; 167 print "$rtyp ${ofn}($args);\n"; 168 } 169 if (eval "\$${fn}_indirect" eq "false") { 170 print "#define ${fn} ${dfn}\n"; 171 } else { 172 print "RTCD_EXTERN $rtyp (*${fn})($args);\n"; 173 } 174 print "\n"; 175 } 176} 177 178sub set_function_pointers { 179 foreach my $fn (sort keys %ALL_FUNCS) { 180 my @val = @{$ALL_FUNCS{$fn}}; 181 my $args = pop @val; 182 my $rtyp = "@val"; 183 my $dfn = eval "\$${fn}_default"; 184 $dfn = eval "\$${dfn}"; 185 if (eval "\$${fn}_indirect" eq "true") { 186 print " $fn = $dfn;\n"; 187 foreach my $opt (@_) { 188 my $ofn = eval "\$${fn}_${opt}"; 189 next if !$ofn; 190 next if "$ofn" eq "$dfn"; 191 my $link = eval "\$${fn}_${opt}_link"; 192 next if $link && $link eq "false"; 193 my $cond = eval "\$have_${opt}"; 194 print " if (${cond}) $fn = $ofn;\n" 195 } 196 } 197 } 198} 199 200sub filter { 201 my @filtered; 202 foreach (@_) { push @filtered, $_ unless $disabled{$_}; } 203 return @filtered; 204} 205 206# 207# Helper functions for generating the arch specific RTCD files 208# 209sub common_top() { 210 my $include_guard = uc($opts{sym})."_H_"; 211 print <<EOF; 212// This file is generated. Do not edit. 213#ifndef ${include_guard} 214#define ${include_guard} 215 216#ifdef RTCD_C 217#define RTCD_EXTERN 218#else 219#define RTCD_EXTERN extern 220#endif 221 222EOF 223 224process_forward_decls(); 225print <<EOF; 226 227#ifdef __cplusplus 228extern "C" { 229#endif 230 231EOF 232declare_function_pointers("c", @ALL_ARCHS); 233 234print <<EOF; 235void $opts{sym}(void); 236 237EOF 238} 239 240sub common_bottom() { 241 print <<EOF; 242 243#ifdef __cplusplus 244} // extern "C" 245#endif 246 247#endif 248EOF 249} 250 251sub x86() { 252 determine_indirection("c", @ALL_ARCHS); 253 254 # Assign the helper variable for each enabled extension 255 foreach my $opt (@ALL_ARCHS) { 256 my $opt_uc = uc $opt; 257 eval "\$have_${opt}=\"flags & HAS_${opt_uc}\""; 258 } 259 260 common_top; 261 print <<EOF; 262#ifdef RTCD_C 263#include "vpx_ports/x86.h" 264static void setup_rtcd_internal(void) 265{ 266 int flags = x86_simd_caps(); 267 268 (void)flags; 269 270EOF 271 272 set_function_pointers("c", @ALL_ARCHS); 273 274 print <<EOF; 275} 276#endif 277EOF 278 common_bottom; 279} 280 281sub arm() { 282 determine_indirection("c", @ALL_ARCHS); 283 284 # Assign the helper variable for each enabled extension 285 foreach my $opt (@ALL_ARCHS) { 286 my $opt_uc = uc $opt; 287 # Enable neon assembly based on HAVE_NEON logic instead of adding new 288 # HAVE_NEON_ASM logic 289 if ($opt eq 'neon_asm') { $opt_uc = 'NEON' } 290 eval "\$have_${opt}=\"flags & HAS_${opt_uc}\""; 291 } 292 293 common_top; 294 print <<EOF; 295#include "vpx_config.h" 296 297#ifdef RTCD_C 298#include "vpx_ports/arm.h" 299static void setup_rtcd_internal(void) 300{ 301 int flags = arm_cpu_caps(); 302 303 (void)flags; 304 305EOF 306 307 set_function_pointers("c", @ALL_ARCHS); 308 309 print <<EOF; 310} 311#endif 312EOF 313 common_bottom; 314} 315 316sub mips() { 317 determine_indirection("c", @ALL_ARCHS); 318 319 # Assign the helper variable for each enabled extension 320 foreach my $opt (@ALL_ARCHS) { 321 my $opt_uc = uc $opt; 322 eval "\$have_${opt}=\"flags & HAS_${opt_uc}\""; 323 } 324 325 common_top; 326 327 print <<EOF; 328#include "vpx_config.h" 329 330#ifdef RTCD_C 331#include "vpx_ports/mips.h" 332static void setup_rtcd_internal(void) 333{ 334 int flags = mips_cpu_caps(); 335 336 (void)flags; 337 338EOF 339 340 set_function_pointers("c", @ALL_ARCHS); 341 342 print <<EOF; 343#if HAVE_DSPR2 344void vpx_dsputil_static_init(); 345#if CONFIG_VP8 346void dsputil_static_init(); 347#endif 348 349vpx_dsputil_static_init(); 350#if CONFIG_VP8 351dsputil_static_init(); 352#endif 353#endif 354} 355#endif 356EOF 357 common_bottom; 358} 359 360sub ppc() { 361 determine_indirection("c", @ALL_ARCHS); 362 363 # Assign the helper variable for each enabled extension 364 foreach my $opt (@ALL_ARCHS) { 365 my $opt_uc = uc $opt; 366 eval "\$have_${opt}=\"flags & HAS_${opt_uc}\""; 367 } 368 369 common_top; 370 print <<EOF; 371#include "vpx_config.h" 372 373#ifdef RTCD_C 374#include "vpx_ports/ppc.h" 375static void setup_rtcd_internal(void) 376{ 377 int flags = ppc_simd_caps(); 378 (void)flags; 379EOF 380 381 set_function_pointers("c", @ALL_ARCHS); 382 383 print <<EOF; 384} 385#endif 386EOF 387 common_bottom; 388} 389 390sub unoptimized() { 391 determine_indirection "c"; 392 common_top; 393 print <<EOF; 394#include "vpx_config.h" 395 396#ifdef RTCD_C 397static void setup_rtcd_internal(void) 398{ 399EOF 400 401 set_function_pointers "c"; 402 403 print <<EOF; 404} 405#endif 406EOF 407 common_bottom; 408} 409 410# 411# Main Driver 412# 413 414&require("c"); 415&require(keys %required); 416if ($opts{arch} eq 'x86') { 417 @ALL_ARCHS = filter(qw/mmx sse sse2 sse3 ssse3 sse4_1 avx avx2 avx512/); 418 x86; 419} elsif ($opts{arch} eq 'x86_64') { 420 @ALL_ARCHS = filter(qw/mmx sse sse2 sse3 ssse3 sse4_1 avx avx2 avx512/); 421 @REQUIRES = filter(qw/mmx sse sse2/); 422 &require(@REQUIRES); 423 x86; 424} elsif ($opts{arch} eq 'mips32' || $opts{arch} eq 'mips64') { 425 my $have_dspr2 = 0; 426 my $have_msa = 0; 427 my $have_mmi = 0; 428 @ALL_ARCHS = filter("$opts{arch}"); 429 open CONFIG_FILE, $opts{config} or 430 die "Error opening config file '$opts{config}': $!\n"; 431 while (<CONFIG_FILE>) { 432 if (/HAVE_DSPR2=yes/) { 433 $have_dspr2 = 1; 434 } 435 if (/HAVE_MSA=yes/) { 436 $have_msa = 1; 437 } 438 if (/HAVE_MMI=yes/) { 439 $have_mmi = 1; 440 } 441 } 442 close CONFIG_FILE; 443 if ($have_dspr2 == 1) { 444 @ALL_ARCHS = filter("$opts{arch}", qw/dspr2/); 445 } elsif ($have_msa == 1 && $have_mmi == 1) { 446 @ALL_ARCHS = filter("$opts{arch}", qw/mmi msa/); 447 } elsif ($have_msa == 1) { 448 @ALL_ARCHS = filter("$opts{arch}", qw/msa/); 449 } elsif ($have_mmi == 1) { 450 @ALL_ARCHS = filter("$opts{arch}", qw/mmi/); 451 } else { 452 unoptimized; 453 } 454 mips; 455} elsif ($opts{arch} =~ /armv7\w?/) { 456 @ALL_ARCHS = filter(qw/neon_asm neon/); 457 arm; 458} elsif ($opts{arch} eq 'armv8' || $opts{arch} eq 'arm64' ) { 459 @ALL_ARCHS = filter(qw/neon/); 460 &require("neon"); 461 arm; 462} elsif ($opts{arch} =~ /^ppc/ ) { 463 @ALL_ARCHS = filter(qw/vsx/); 464 ppc; 465} else { 466 unoptimized; 467} 468 469__END__ 470 471=head1 NAME 472 473rtcd - 474 475=head1 SYNOPSIS 476 477Usage: rtcd.pl [options] FILE 478 479See 'perldoc rtcd.pl' for more details. 480 481=head1 DESCRIPTION 482 483Reads the Run Time CPU Detections definitions from FILE and generates a 484C header file on stdout. 485 486=head1 OPTIONS 487 488Options: 489 --arch=ARCH Architecture to generate defs for (required) 490 --disable-EXT Disable support for EXT extensions 491 --require-EXT Require support for EXT extensions 492 --sym=SYMBOL Unique symbol to use for RTCD initialization function 493 --config=FILE File with CONFIG_FOO=yes lines to parse 494