1 #!/usr/bin/env perl 2 3 no strict 'refs'; 4 use warnings; 5 use Getopt::Long; 6 Getopt::Long::Configure("auto_help"); 7 8 my %ALL_FUNCS = (); 9 my @ALL_ARCHS; 10 my @ALL_FORWARD_DECLS; 11 my @REQUIRES; 12 13 my %opts = (); 14 my %disabled = (); 15 my %required = (); 16 17 my @argv; 18 foreach (@ARGV) { 19 $disabled{$1} = 1, next if /--disable-(.*)/; 20 $required{$1} = 1, next if /--require-(.*)/; 21 push @argv, $_; 22 } 23 24 # NB: use GetOptions() instead of GetOptionsFromArray() for compatibility. 25 @ARGV = @argv; 26 GetOptions( 27 \%opts, 28 'arch=s', 29 'sym=s', 30 'config=s', 31 ); 32 33 foreach my $opt (qw/arch config/) { 34 if (!defined($opts{$opt})) { 35 warn "--$opt is required!\n"; 36 Getopt::Long::HelpMessage('-exit' => 1); 37 } 38 } 39 40 foreach my $defs_file (@ARGV) { 41 if (!-f $defs_file) { 42 warn "$defs_file: $!\n"; 43 Getopt::Long::HelpMessage('-exit' => 1); 44 } 45 } 46 47 open CONFIG_FILE, $opts{config} or 48 die "Error opening config file '$opts{config}': $!\n"; 49 50 my %config = (); 51 while (<CONFIG_FILE>) { 52 next if !/^CONFIG_/; 53 chomp; 54 my @pair = split /=/; 55 $config{$pair[0]} = $pair[1]; 56 } 57 close CONFIG_FILE; 58 59 # 60 # Routines for the RTCD DSL to call 61 # 62 sub vpx_config($) { 63 return (defined $config{$_[0]}) ? $config{$_[0]} : ""; 64 } 65 66 sub specialize { 67 my $fn=$_[0]; 68 shift; 69 foreach my $opt (@_) { 70 eval "\$${fn}_${opt}=${fn}_${opt}"; 71 } 72 } 73 74 sub add_proto { 75 my $fn = splice(@_, -2, 1); 76 $ALL_FUNCS{$fn} = \@_; 77 specialize $fn, "c"; 78 } 79 80 sub require { 81 foreach my $fn (keys %ALL_FUNCS) { 82 foreach my $opt (@_) { 83 my $ofn = eval "\$${fn}_${opt}"; 84 next if !$ofn; 85 86 # if we already have a default, then we can disable it, as we know 87 # we can do better. 88 my $best = eval "\$${fn}_default"; 89 if ($best) { 90 my $best_ofn = eval "\$${best}"; 91 if ($best_ofn && "$best_ofn" ne "$ofn") { 92 eval "\$${best}_link = 'false'"; 93 } 94 } 95 eval "\$${fn}_default=${fn}_${opt}"; 96 eval "\$${fn}_${opt}_link='true'"; 97 } 98 } 99 } 100 101 sub forward_decls { 102 push @ALL_FORWARD_DECLS, @_; 103 } 104 105 # 106 # Include the user's directives 107 # 108 foreach my $f (@ARGV) { 109 open FILE, "<", $f or die "cannot open $f: $!\n"; 110 my $contents = join('', <FILE>); 111 close FILE; 112 eval $contents or warn "eval failed: $@\n"; 113 } 114 115 # 116 # Process the directives according to the command line 117 # 118 sub process_forward_decls() { 119 foreach (@ALL_FORWARD_DECLS) { 120 $_->(); 121 } 122 } 123 124 sub determine_indirection { 125 vpx_config("CONFIG_RUNTIME_CPU_DETECT") eq "yes" or &require(@ALL_ARCHS); 126 foreach my $fn (keys %ALL_FUNCS) { 127 my $n = ""; 128 my @val = @{$ALL_FUNCS{$fn}}; 129 my $args = pop @val; 130 my $rtyp = "@val"; 131 my $dfn = eval "\$${fn}_default"; 132 $dfn = eval "\$${dfn}"; 133 foreach my $opt (@_) { 134 my $ofn = eval "\$${fn}_${opt}"; 135 next if !$ofn; 136 my $link = eval "\$${fn}_${opt}_link"; 137 next if $link && $link eq "false"; 138 $n .= "x"; 139 } 140 if ($n eq "x") { 141 eval "\$${fn}_indirect = 'false'"; 142 } else { 143 eval "\$${fn}_indirect = 'true'"; 144 } 145 } 146 } 147 148 sub declare_function_pointers { 149 foreach my $fn (sort keys %ALL_FUNCS) { 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 print "$rtyp ${ofn}($args);\n"; 159 } 160 if (eval "\$${fn}_indirect" eq "false") { 161 print "#define ${fn} ${dfn}\n"; 162 } else { 163 print "RTCD_EXTERN $rtyp (*${fn})($args);\n"; 164 } 165 print "\n"; 166 } 167 } 168 169 sub set_function_pointers { 170 foreach my $fn (sort keys %ALL_FUNCS) { 171 my @val = @{$ALL_FUNCS{$fn}}; 172 my $args = pop @val; 173 my $rtyp = "@val"; 174 my $dfn = eval "\$${fn}_default"; 175 $dfn = eval "\$${dfn}"; 176 if (eval "\$${fn}_indirect" eq "true") { 177 print " $fn = $dfn;\n"; 178 foreach my $opt (@_) { 179 my $ofn = eval "\$${fn}_${opt}"; 180 next if !$ofn; 181 next if "$ofn" eq "$dfn"; 182 my $link = eval "\$${fn}_${opt}_link"; 183 next if $link && $link eq "false"; 184 my $cond = eval "\$have_${opt}"; 185 print " if (${cond}) $fn = $ofn;\n" 186 } 187 } 188 } 189 } 190 191 sub filter { 192 my @filtered; 193 foreach (@_) { push @filtered, $_ unless $disabled{$_}; } 194 return @filtered; 195 } 196 197 # 198 # Helper functions for generating the arch specific RTCD files 199 # 200 sub common_top() { 201 my $include_guard = uc($opts{sym})."_H_"; 202 print <<EOF; 203 #ifndef ${include_guard} 204 #define ${include_guard} 205 206 #ifdef RTCD_C 207 #define RTCD_EXTERN 208 #else 209 #define RTCD_EXTERN extern 210 #endif 211 212 #ifdef __cplusplus 213 extern "C" { 214 #endif 215 216 EOF 217 218 process_forward_decls(); 219 print "\n"; 220 declare_function_pointers("c", @ALL_ARCHS); 221 222 print <<EOF; 223 void $opts{sym}(void); 224 225 EOF 226 } 227 228 sub common_bottom() { 229 print <<EOF; 230 231 #ifdef __cplusplus 232 } // extern "C" 233 #endif 234 235 #endif 236 EOF 237 } 238 239 sub x86() { 240 determine_indirection("c", @ALL_ARCHS); 241 242 # Assign the helper variable for each enabled extension 243 foreach my $opt (@ALL_ARCHS) { 244 my $opt_uc = uc $opt; 245 eval "\$have_${opt}=\"flags & HAS_${opt_uc}\""; 246 } 247 248 common_top; 249 print <<EOF; 250 #ifdef RTCD_C 251 #include "vpx_ports/x86.h" 252 static void setup_rtcd_internal(void) 253 { 254 int flags = x86_simd_caps(); 255 256 (void)flags; 257 258 EOF 259 260 set_function_pointers("c", @ALL_ARCHS); 261 262 print <<EOF; 263 } 264 #endif 265 EOF 266 common_bottom; 267 } 268 269 sub arm() { 270 determine_indirection("c", @ALL_ARCHS); 271 272 # Assign the helper variable for each enabled extension 273 foreach my $opt (@ALL_ARCHS) { 274 my $opt_uc = uc $opt; 275 eval "\$have_${opt}=\"flags & HAS_${opt_uc}\""; 276 } 277 278 common_top; 279 print <<EOF; 280 #include "vpx_config.h" 281 282 #ifdef RTCD_C 283 #include "vpx_ports/arm.h" 284 static void setup_rtcd_internal(void) 285 { 286 int flags = arm_cpu_caps(); 287 288 (void)flags; 289 290 EOF 291 292 set_function_pointers("c", @ALL_ARCHS); 293 294 print <<EOF; 295 } 296 #endif 297 EOF 298 common_bottom; 299 } 300 301 sub mips() { 302 determine_indirection("c", @ALL_ARCHS); 303 common_top; 304 305 print <<EOF; 306 #include "vpx_config.h" 307 308 #ifdef RTCD_C 309 static void setup_rtcd_internal(void) 310 { 311 EOF 312 313 set_function_pointers("c", @ALL_ARCHS); 314 315 print <<EOF; 316 #if HAVE_DSPR2 317 #if CONFIG_VP8 318 void dsputil_static_init(); 319 dsputil_static_init(); 320 #endif 321 #if CONFIG_VP9 322 void vp9_dsputil_static_init(); 323 vp9_dsputil_static_init(); 324 #endif 325 #endif 326 } 327 #endif 328 EOF 329 common_bottom; 330 } 331 332 sub unoptimized() { 333 determine_indirection "c"; 334 common_top; 335 print <<EOF; 336 #include "vpx_config.h" 337 338 #ifdef RTCD_C 339 static void setup_rtcd_internal(void) 340 { 341 EOF 342 343 set_function_pointers "c"; 344 345 print <<EOF; 346 } 347 #endif 348 EOF 349 common_bottom; 350 } 351 352 # 353 # Main Driver 354 # 355 356 &require("c"); 357 if ($opts{arch} eq 'x86') { 358 @ALL_ARCHS = filter(qw/mmx sse sse2 sse3 ssse3 sse4_1 avx avx2/); 359 x86; 360 } elsif ($opts{arch} eq 'x86_64') { 361 @ALL_ARCHS = filter(qw/mmx sse sse2 sse3 ssse3 sse4_1 avx avx2/); 362 @REQUIRES = filter(keys %required ? keys %required : qw/mmx sse sse2/); 363 &require(@REQUIRES); 364 x86; 365 } elsif ($opts{arch} eq 'mips32') { 366 @ALL_ARCHS = filter(qw/mips32/); 367 open CONFIG_FILE, $opts{config} or 368 die "Error opening config file '$opts{config}': $!\n"; 369 while (<CONFIG_FILE>) { 370 if (/HAVE_DSPR2=yes/) { 371 @ALL_ARCHS = filter(qw/mips32 dspr2/); 372 last; 373 } 374 } 375 close CONFIG_FILE; 376 mips; 377 } elsif ($opts{arch} eq 'armv5te') { 378 @ALL_ARCHS = filter(qw/edsp/); 379 arm; 380 } elsif ($opts{arch} eq 'armv6') { 381 @ALL_ARCHS = filter(qw/edsp media/); 382 arm; 383 } elsif ($opts{arch} eq 'armv7') { 384 @ALL_ARCHS = filter(qw/edsp media neon/); 385 arm; 386 } else { 387 unoptimized; 388 } 389 390 __END__ 391 392 =head1 NAME 393 394 rtcd - 395 396 =head1 SYNOPSIS 397 398 Usage: rtcd.pl [options] FILE 399 400 See 'perldoc rtcd.pl' for more details. 401 402 =head1 DESCRIPTION 403 404 Reads the Run Time CPU Detections definitions from FILE and generates a 405 C header file on stdout. 406 407 =head1 OPTIONS 408 409 Options: 410 --arch=ARCH Architecture to generate defs for (required) 411 --disable-EXT Disable support for EXT extensions 412 --require-EXT Require support for EXT extensions 413 --sym=SYMBOL Unique symbol to use for RTCD initialization function 414 --config=FILE File with CONFIG_FOO=yes lines to parse 415