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