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 print <<EOF; 225// This file is generated. Do not edit. 226#ifndef ${include_guard} 227#define ${include_guard} 228 229#ifdef RTCD_C 230#define RTCD_EXTERN 231#else 232#define RTCD_EXTERN extern 233#endif 234 235EOF 236 237process_forward_decls(); 238print <<EOF; 239 240#ifdef __cplusplus 241extern "C" { 242#endif 243 244EOF 245declare_function_pointers("c", @ALL_ARCHS); 246 247print <<EOF; 248void $opts{sym}(void); 249 250EOF 251} 252 253sub common_bottom() { 254 print <<EOF; 255 256#ifdef __cplusplus 257} // extern "C" 258#endif 259 260#endif 261EOF 262} 263 264sub x86() { 265 determine_indirection("c", @ALL_ARCHS); 266 267 # Assign the helper variable for each enabled extension 268 foreach my $opt (@ALL_ARCHS) { 269 my $opt_uc = uc $opt; 270 eval "\$have_${opt}=\"flags & HAS_${opt_uc}\""; 271 } 272 273 common_top; 274 print <<EOF; 275#ifdef RTCD_C 276#include "aom_ports/x86.h" 277static void setup_rtcd_internal(void) 278{ 279 int flags = x86_simd_caps(); 280 281 (void)flags; 282 283EOF 284 285 set_function_pointers("c", @ALL_ARCHS); 286 287 print <<EOF; 288} 289#endif 290EOF 291 common_bottom; 292} 293 294sub arm() { 295 determine_indirection("c", @ALL_ARCHS); 296 297 # Assign the helper variable for each enabled extension 298 foreach my $opt (@ALL_ARCHS) { 299 my $opt_uc = uc $opt; 300 eval "\$have_${opt}=\"flags & HAS_${opt_uc}\""; 301 } 302 303 common_top; 304 print <<EOF; 305#include "config/aom_config.h" 306 307#ifdef RTCD_C 308#include "aom_ports/arm.h" 309static void setup_rtcd_internal(void) 310{ 311 int flags = aom_arm_cpu_caps(); 312 313 (void)flags; 314 315EOF 316 317 set_function_pointers("c", @ALL_ARCHS); 318 319 print <<EOF; 320} 321#endif 322EOF 323 common_bottom; 324} 325 326sub ppc() { 327 determine_indirection("c", @ALL_ARCHS); 328 329 # Assign the helper variable for each enabled extension 330 foreach my $opt (@ALL_ARCHS) { 331 my $opt_uc = uc $opt; 332 eval "\$have_${opt}=\"flags & HAS_${opt_uc}\""; 333 } 334 335 common_top; 336 337 print <<EOF; 338#include "config/aom_config.h" 339 340#ifdef RTCD_C 341#include "aom_ports/ppc.h" 342static void setup_rtcd_internal(void) 343{ 344 int flags = ppc_simd_caps(); 345 346 (void)flags; 347 348EOF 349 350 set_function_pointers("c", @ALL_ARCHS); 351 352 print <<EOF; 353} 354#endif 355EOF 356 common_bottom; 357} 358 359sub unoptimized() { 360 determine_indirection "c"; 361 common_top; 362 print <<EOF; 363#include "config/aom_config.h" 364 365#ifdef RTCD_C 366static void setup_rtcd_internal(void) 367{ 368EOF 369 370 set_function_pointers "c"; 371 372 print <<EOF; 373} 374#endif 375EOF 376 common_bottom; 377} 378 379# 380# Main Driver 381# 382 383&require("c"); 384&require(keys %required); 385if ($opts{arch} eq 'x86') { 386 @ALL_ARCHS = filter(qw/mmx sse sse2 sse3 ssse3 sse4_1 sse4_2 avx avx2/); 387 x86; 388} elsif ($opts{arch} eq 'x86_64') { 389 @ALL_ARCHS = filter(qw/mmx sse sse2 sse3 ssse3 sse4_1 sse4_2 avx avx2/); 390 @REQUIRES = filter(qw/mmx sse sse2/); 391 &require(@REQUIRES); 392 x86; 393} elsif ($opts{arch} =~ /armv[78]\w?/) { 394 @ALL_ARCHS = filter(qw/neon/); 395 arm; 396} elsif ($opts{arch} eq 'arm64' ) { 397 @ALL_ARCHS = filter(qw/neon arm_crc32 neon_dotprod neon_i8mm sve sve2/); 398 @REQUIRES = filter(qw/neon/); 399 &require(@REQUIRES); 400 arm; 401} elsif ($opts{arch} eq 'ppc') { 402 @ALL_ARCHS = filter(qw/vsx/); 403 ppc; 404} else { 405 unoptimized; 406} 407 408__END__ 409 410=head1 NAME 411 412rtcd - 413 414=head1 SYNOPSIS 415 416Usage: rtcd.pl [options] FILE 417 418See 'perldoc rtcd.pl' for more details. 419 420=head1 DESCRIPTION 421 422Reads the Run Time CPU Detections definitions from FILE and generates a 423C header file on stdout. 424 425=head1 OPTIONS 426 427Options: 428 --arch=ARCH Architecture to generate defs for (required) 429 --disable-EXT Disable support for EXT extensions 430 --require-EXT Require support for EXT extensions 431 --sym=SYMBOL Unique symbol to use for RTCD initialization function 432 --config=FILE Path to file containing C preprocessor directives to parse 433