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