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 common_top; 319 320 print <<EOF; 321#include "vpx_config.h" 322 323#ifdef RTCD_C 324static void setup_rtcd_internal(void) 325{ 326EOF 327 328 set_function_pointers("c", @ALL_ARCHS); 329 330 print <<EOF; 331#if HAVE_DSPR2 332void vpx_dsputil_static_init(); 333#if CONFIG_VP8 334void dsputil_static_init(); 335#endif 336 337vpx_dsputil_static_init(); 338#if CONFIG_VP8 339dsputil_static_init(); 340#endif 341#endif 342} 343#endif 344EOF 345 common_bottom; 346} 347 348sub ppc() { 349 determine_indirection("c", @ALL_ARCHS); 350 351 # Assign the helper variable for each enabled extension 352 foreach my $opt (@ALL_ARCHS) { 353 my $opt_uc = uc $opt; 354 eval "\$have_${opt}=\"flags & HAS_${opt_uc}\""; 355 } 356 357 common_top; 358 print <<EOF; 359#include "vpx_config.h" 360 361#ifdef RTCD_C 362#include "vpx_ports/ppc.h" 363static void setup_rtcd_internal(void) 364{ 365 int flags = ppc_simd_caps(); 366 (void)flags; 367EOF 368 369 set_function_pointers("c", @ALL_ARCHS); 370 371 print <<EOF; 372} 373#endif 374EOF 375 common_bottom; 376} 377 378sub unoptimized() { 379 determine_indirection "c"; 380 common_top; 381 print <<EOF; 382#include "vpx_config.h" 383 384#ifdef RTCD_C 385static void setup_rtcd_internal(void) 386{ 387EOF 388 389 set_function_pointers "c"; 390 391 print <<EOF; 392} 393#endif 394EOF 395 common_bottom; 396} 397 398# 399# Main Driver 400# 401 402&require("c"); 403&require(keys %required); 404if ($opts{arch} eq 'x86') { 405 @ALL_ARCHS = filter(qw/mmx sse sse2 sse3 ssse3 sse4_1 avx avx2 avx512/); 406 x86; 407} elsif ($opts{arch} eq 'x86_64') { 408 @ALL_ARCHS = filter(qw/mmx sse sse2 sse3 ssse3 sse4_1 avx avx2 avx512/); 409 @REQUIRES = filter(qw/mmx sse sse2/); 410 &require(@REQUIRES); 411 x86; 412} elsif ($opts{arch} eq 'mips32' || $opts{arch} eq 'mips64') { 413 @ALL_ARCHS = filter("$opts{arch}"); 414 open CONFIG_FILE, $opts{config} or 415 die "Error opening config file '$opts{config}': $!\n"; 416 while (<CONFIG_FILE>) { 417 if (/HAVE_DSPR2=yes/) { 418 @ALL_ARCHS = filter("$opts{arch}", qw/dspr2/); 419 last; 420 } 421 if (/HAVE_MSA=yes/) { 422 @ALL_ARCHS = filter("$opts{arch}", qw/msa/); 423 last; 424 } 425 if (/HAVE_MMI=yes/) { 426 @ALL_ARCHS = filter("$opts{arch}", qw/mmi/); 427 last; 428 } 429 } 430 close CONFIG_FILE; 431 mips; 432} elsif ($opts{arch} =~ /armv7\w?/) { 433 @ALL_ARCHS = filter(qw/neon_asm neon/); 434 arm; 435} elsif ($opts{arch} eq 'armv8' || $opts{arch} eq 'arm64' ) { 436 @ALL_ARCHS = filter(qw/neon/); 437 &require("neon"); 438 arm; 439} elsif ($opts{arch} =~ /^ppc/ ) { 440 @ALL_ARCHS = filter(qw/vsx/); 441 ppc; 442} else { 443 unoptimized; 444} 445 446__END__ 447 448=head1 NAME 449 450rtcd - 451 452=head1 SYNOPSIS 453 454Usage: rtcd.pl [options] FILE 455 456See 'perldoc rtcd.pl' for more details. 457 458=head1 DESCRIPTION 459 460Reads the Run Time CPU Detections definitions from FILE and generates a 461C header file on stdout. 462 463=head1 OPTIONS 464 465Options: 466 --arch=ARCH Architecture to generate defs for (required) 467 --disable-EXT Disable support for EXT extensions 468 --require-EXT Require support for EXT extensions 469 --sym=SYMBOL Unique symbol to use for RTCD initialization function 470 --config=FILE File with CONFIG_FOO=yes lines to parse 471