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