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 # 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} 73close CONFIG_FILE; 74 75# 76# Routines for the RTCD DSL to call 77# 78sub aom_config($) { 79 return (defined $config{$_[0]}) ? $config{$_[0]} : ""; 80} 81 82sub 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 94sub 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 102sub 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 123sub forward_decls { 124 push @ALL_FORWARD_DECLS, @_; 125} 126 127# 128# Include the user's directives 129# 130foreach 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# 140sub process_forward_decls() { 141 foreach (@ALL_FORWARD_DECLS) { 142 $_->(); 143 } 144} 145 146sub 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 170sub 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 191sub 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 213sub 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# 222sub 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 248EOF 249 250process_forward_decls(); 251print <<EOF; 252 253#ifdef __cplusplus 254extern "C" { 255#endif 256 257EOF 258declare_function_pointers("c", @ALL_ARCHS); 259 260print <<EOF; 261void $opts{sym}(void); 262 263EOF 264} 265 266sub 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} 275EOF 276} 277 278sub 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" 291static void setup_rtcd_internal(void) 292{ 293 int flags = x86_simd_caps(); 294 295 (void)flags; 296 297EOF 298 299 set_function_pointers("c", @ALL_ARCHS); 300 301 print <<EOF; 302} 303#endif 304EOF 305 common_bottom; 306} 307 308sub 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" 323static void setup_rtcd_internal(void) 324{ 325 int flags = aom_arm_cpu_caps(); 326 327 (void)flags; 328 329EOF 330 331 set_function_pointers("c", @ALL_ARCHS); 332 333 print <<EOF; 334} 335#endif 336EOF 337 common_bottom; 338} 339 340sub 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" 356static void setup_rtcd_internal(void) 357{ 358 int flags = ppc_simd_caps(); 359 360 (void)flags; 361 362EOF 363 364 set_function_pointers("c", @ALL_ARCHS); 365 366 print <<EOF; 367} 368#endif 369EOF 370 common_bottom; 371} 372 373sub unoptimized() { 374 determine_indirection "c"; 375 common_top; 376 print <<EOF; 377#include "config/aom_config.h" 378 379#ifdef RTCD_C 380static void setup_rtcd_internal(void) 381{ 382EOF 383 384 set_function_pointers "c"; 385 386 print <<EOF; 387} 388#endif 389EOF 390 common_bottom; 391} 392 393# 394# Main Driver 395# 396 397&require("c"); 398&require(keys %required); 399if ($opts{arch} eq 'x86') { 400 @ALL_ARCHS = filter(qw/mmx sse sse2 sse3 ssse3 sse4_1 sse4_2 avx avx2/); 401 x86; 402} elsif ($opts{arch} eq 'x86_64') { 403 @ALL_ARCHS = filter(qw/mmx sse sse2 sse3 ssse3 sse4_1 sse4_2 avx avx2/); 404 @REQUIRES = filter(qw/mmx sse sse2/); 405 &require(@REQUIRES); 406 x86; 407} elsif ($opts{arch} =~ /armv[78]\w?/) { 408 @ALL_ARCHS = filter(qw/neon/); 409 arm; 410} elsif ($opts{arch} eq 'arm64' ) { 411 @ALL_ARCHS = filter(qw/neon arm_crc32 neon_dotprod neon_i8mm sve sve2/); 412 @REQUIRES = filter(qw/neon/); 413 &require(@REQUIRES); 414 arm; 415} elsif ($opts{arch} eq 'ppc') { 416 @ALL_ARCHS = filter(qw/vsx/); 417 ppc; 418} else { 419 unoptimized; 420} 421 422__END__ 423 424=head1 NAME 425 426rtcd - 427 428=head1 SYNOPSIS 429 430Usage: rtcd.pl [options] FILE 431 432See 'perldoc rtcd.pl' for more details. 433 434=head1 DESCRIPTION 435 436Reads the Run Time CPU Detections definitions from FILE and generates a 437C header file on stdout. 438 439=head1 OPTIONS 440 441Options: 442 --arch=ARCH Architecture to generate defs for (required) 443 --disable-EXT Disable support for EXT extensions 444 --require-EXT Require support for EXT extensions 445 --sym=SYMBOL Unique symbol to use for RTCD initialization function 446 --config=FILE Path to file containing C preprocessor directives to parse 447