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## 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 !/^#define\s+(?:CONFIG_|HAVE_)/; 62 chomp; 63 my @line_components = split /\s/; 64 scalar @line_components > 2 or 65 die "Invalid input passed to rtcd.pl via $opts{config}."; 66 # $line_components[0] = #define 67 # $line_components[1] = flag name (CONFIG_SOMETHING or HAVE_SOMETHING) 68 # $line_components[2] = flag value (0 or 1) 69 $config{$line_components[1]} = "$line_components[2]" eq "1" ? "yes" : ""; 70} 71close CONFIG_FILE; 72 73# 74# Routines for the RTCD DSL to call 75# 76sub aom_config($) { 77 return (defined $config{$_[0]}) ? $config{$_[0]} : ""; 78} 79 80sub specialize { 81 if (@_ <= 1) { 82 die "'specialize' must be called with a function name and at least one ", 83 "architecture ('C' is implied): \n@_\n"; 84 } 85 my $fn=$_[0]; 86 shift; 87 foreach my $opt (@_) { 88 eval "\$${fn}_${opt}=${fn}_${opt}"; 89 } 90} 91 92sub add_proto { 93 my $fn = splice(@_, -2, 1); 94 $ALL_FUNCS{$fn} = \@_; 95 specialize $fn, "c"; 96} 97 98sub require { 99 foreach my $fn (keys %ALL_FUNCS) { 100 foreach my $opt (@_) { 101 my $ofn = eval "\$${fn}_${opt}"; 102 next if !$ofn; 103 104 # if we already have a default, then we can disable it, as we know 105 # we can do better. 106 my $best = eval "\$${fn}_default"; 107 if ($best) { 108 my $best_ofn = eval "\$${best}"; 109 if ($best_ofn && "$best_ofn" ne "$ofn") { 110 eval "\$${best}_link = 'false'"; 111 } 112 } 113 eval "\$${fn}_default=${fn}_${opt}"; 114 eval "\$${fn}_${opt}_link='true'"; 115 } 116 } 117} 118 119sub forward_decls { 120 push @ALL_FORWARD_DECLS, @_; 121} 122 123# 124# Include the user's directives 125# 126foreach my $f (@ARGV) { 127 open FILE, "<", $f or die "cannot open $f: $!\n"; 128 my $contents = join('', <FILE>); 129 close FILE; 130 eval $contents or warn "eval failed: $@\n"; 131} 132 133# 134# Process the directives according to the command line 135# 136sub process_forward_decls() { 137 foreach (@ALL_FORWARD_DECLS) { 138 $_->(); 139 } 140} 141 142sub determine_indirection { 143 aom_config("CONFIG_RUNTIME_CPU_DETECT") eq "yes" or &require(@ALL_ARCHS); 144 foreach my $fn (keys %ALL_FUNCS) { 145 my $n = ""; 146 my @val = @{$ALL_FUNCS{$fn}}; 147 my $args = pop @val; 148 my $rtyp = "@val"; 149 my $dfn = eval "\$${fn}_default"; 150 $dfn = eval "\$${dfn}"; 151 foreach my $opt (@_) { 152 my $ofn = eval "\$${fn}_${opt}"; 153 next if !$ofn; 154 my $link = eval "\$${fn}_${opt}_link"; 155 next if $link && $link eq "false"; 156 $n .= "x"; 157 } 158 if ($n eq "x") { 159 eval "\$${fn}_indirect = 'false'"; 160 } else { 161 eval "\$${fn}_indirect = 'true'"; 162 } 163 } 164} 165 166sub declare_function_pointers { 167 foreach my $fn (sort keys %ALL_FUNCS) { 168 my @val = @{$ALL_FUNCS{$fn}}; 169 my $args = pop @val; 170 my $rtyp = "@val"; 171 my $dfn = eval "\$${fn}_default"; 172 $dfn = eval "\$${dfn}"; 173 foreach my $opt (@_) { 174 my $ofn = eval "\$${fn}_${opt}"; 175 next if !$ofn; 176 print "$rtyp ${ofn}($args);\n"; 177 } 178 if (eval "\$${fn}_indirect" eq "false") { 179 print "#define ${fn} ${dfn}\n"; 180 } else { 181 print "RTCD_EXTERN $rtyp (*${fn})($args);\n"; 182 } 183 print "\n"; 184 } 185} 186 187sub set_function_pointers { 188 foreach my $fn (sort keys %ALL_FUNCS) { 189 my @val = @{$ALL_FUNCS{$fn}}; 190 my $args = pop @val; 191 my $rtyp = "@val"; 192 my $dfn = eval "\$${fn}_default"; 193 $dfn = eval "\$${dfn}"; 194 if (eval "\$${fn}_indirect" eq "true") { 195 print " $fn = $dfn;\n"; 196 foreach my $opt (@_) { 197 my $ofn = eval "\$${fn}_${opt}"; 198 next if !$ofn; 199 next if "$ofn" eq "$dfn"; 200 my $link = eval "\$${fn}_${opt}_link"; 201 next if $link && $link eq "false"; 202 my $cond = eval "\$have_${opt}"; 203 print " if (${cond}) $fn = $ofn;\n" 204 } 205 } 206 } 207} 208 209sub filter { 210 my @filtered; 211 foreach (@_) { push @filtered, $_ unless $disabled{$_}; } 212 return @filtered; 213} 214 215# 216# Helper functions for generating the arch specific RTCD files 217# 218sub common_top() { 219 my $include_guard = uc($opts{sym})."_H_"; 220 print <<EOF; 221// This file is generated. Do not edit. 222#ifndef ${include_guard} 223#define ${include_guard} 224 225#ifdef RTCD_C 226#define RTCD_EXTERN 227#else 228#define RTCD_EXTERN extern 229#endif 230 231EOF 232 233process_forward_decls(); 234print <<EOF; 235 236#ifdef __cplusplus 237extern "C" { 238#endif 239 240EOF 241declare_function_pointers("c", @ALL_ARCHS); 242 243print <<EOF; 244void $opts{sym}(void); 245 246EOF 247} 248 249sub common_bottom() { 250 print <<EOF; 251 252#ifdef __cplusplus 253} // extern "C" 254#endif 255 256#endif 257EOF 258} 259 260sub x86() { 261 determine_indirection("c", @ALL_ARCHS); 262 263 # Assign the helper variable for each enabled extension 264 foreach my $opt (@ALL_ARCHS) { 265 my $opt_uc = uc $opt; 266 eval "\$have_${opt}=\"flags & HAS_${opt_uc}\""; 267 } 268 269 common_top; 270 print <<EOF; 271#ifdef RTCD_C 272#include "aom_ports/x86.h" 273static void setup_rtcd_internal(void) 274{ 275 int flags = x86_simd_caps(); 276 277 (void)flags; 278 279EOF 280 281 set_function_pointers("c", @ALL_ARCHS); 282 283 print <<EOF; 284} 285#endif 286EOF 287 common_bottom; 288} 289 290sub arm() { 291 determine_indirection("c", @ALL_ARCHS); 292 293 # Assign the helper variable for each enabled extension 294 foreach my $opt (@ALL_ARCHS) { 295 my $opt_uc = uc $opt; 296 eval "\$have_${opt}=\"flags & HAS_${opt_uc}\""; 297 } 298 299 common_top; 300 print <<EOF; 301#include "config/aom_config.h" 302 303#ifdef RTCD_C 304#include "aom_ports/arm.h" 305static void setup_rtcd_internal(void) 306{ 307 int flags = aom_arm_cpu_caps(); 308 309 (void)flags; 310 311EOF 312 313 set_function_pointers("c", @ALL_ARCHS); 314 315 print <<EOF; 316} 317#endif 318EOF 319 common_bottom; 320} 321 322sub mips() { 323 determine_indirection("c", @ALL_ARCHS); 324 325 # Assign the helper variable for each enabled extension 326 foreach my $opt (@ALL_ARCHS) { 327 my $opt_uc = uc $opt; 328 eval "\$have_${opt}=\"flags & HAS_${opt_uc}\""; 329 } 330 331 common_top; 332 333 print <<EOF; 334#include "config/aom_config.h" 335 336#ifdef RTCD_C 337static void setup_rtcd_internal(void) 338{ 339EOF 340 341 set_function_pointers("c", @ALL_ARCHS); 342 343 print <<EOF; 344#if HAVE_DSPR2 345void aom_dsputil_static_init(); 346aom_dsputil_static_init(); 347#endif 348} 349#endif 350EOF 351 common_bottom; 352} 353 354sub ppc() { 355 determine_indirection("c", @ALL_ARCHS); 356 357 # Assign the helper variable for each enabled extension 358 foreach my $opt (@ALL_ARCHS) { 359 my $opt_uc = uc $opt; 360 eval "\$have_${opt}=\"flags & HAS_${opt_uc}\""; 361 } 362 363 common_top; 364 365 print <<EOF; 366#include "config/aom_config.h" 367 368#ifdef RTCD_C 369#include "aom_ports/ppc.h" 370static void setup_rtcd_internal(void) 371{ 372 int flags = ppc_simd_caps(); 373 374 (void)flags; 375 376EOF 377 378 set_function_pointers("c", @ALL_ARCHS); 379 380 print <<EOF; 381} 382#endif 383EOF 384 common_bottom; 385} 386 387sub unoptimized() { 388 determine_indirection "c"; 389 common_top; 390 print <<EOF; 391#include "config/aom_config.h" 392 393#ifdef RTCD_C 394static void setup_rtcd_internal(void) 395{ 396EOF 397 398 set_function_pointers "c"; 399 400 print <<EOF; 401} 402#endif 403EOF 404 common_bottom; 405} 406 407# 408# Main Driver 409# 410 411&require("c"); 412&require(keys %required); 413if ($opts{arch} eq 'x86') { 414 @ALL_ARCHS = filter(qw/mmx sse sse2 sse3 ssse3 sse4_1 sse4_2 avx avx2/); 415 x86; 416} elsif ($opts{arch} eq 'x86_64') { 417 @ALL_ARCHS = filter(qw/mmx sse sse2 sse3 ssse3 sse4_1 sse4_2 avx avx2/); 418 @REQUIRES = filter(qw/mmx sse sse2/); 419 &require(@REQUIRES); 420 x86; 421} elsif ($opts{arch} eq 'mips32' || $opts{arch} eq 'mips64') { 422 @ALL_ARCHS = filter("$opts{arch}"); 423 if (aom_config("HAVE_DSPR2") eq "yes") { 424 @ALL_ARCHS = filter("$opts{arch}", qw/dspr2/); 425 } elsif (aom_config("HAVE_MSA") eq "yes") { 426 @ALL_ARCHS = filter("$opts{arch}", qw/msa/); 427 } 428 mips; 429} elsif ($opts{arch} =~ /armv[78]\w?/) { 430 @ALL_ARCHS = filter(qw/neon/); 431 arm; 432} elsif ($opts{arch} eq 'arm64' ) { 433 @ALL_ARCHS = filter(qw/neon/); 434 &require("neon"); 435 arm; 436} elsif ($opts{arch} eq 'ppc') { 437 @ALL_ARCHS = filter(qw/vsx/); 438 ppc; 439} else { 440 unoptimized; 441} 442 443__END__ 444 445=head1 NAME 446 447rtcd - 448 449=head1 SYNOPSIS 450 451Usage: rtcd.pl [options] FILE 452 453See 'perldoc rtcd.pl' for more details. 454 455=head1 DESCRIPTION 456 457Reads the Run Time CPU Detections definitions from FILE and generates a 458C header file on stdout. 459 460=head1 OPTIONS 461 462Options: 463 --arch=ARCH Architecture to generate defs for (required) 464 --disable-EXT Disable support for EXT extensions 465 --require-EXT Require support for EXT extensions 466 --sym=SYMBOL Unique symbol to use for RTCD initialization function 467 --config=FILE Path to file containing C preprocessor directives to parse 468