blob: 5c6106c99bd838a056c834f418e3550a1e58e203 [file] [log] [blame]
James Zern805078a2014-02-23 16:33:14 -08001#!/usr/bin/env perl
Johannaecbba62017-12-15 09:03:23 -08002##
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##
James Zern805078a2014-02-23 16:33:14 -080012no strict 'refs';
13use warnings;
14use Getopt::Long;
James Zern29943512014-07-24 14:55:19 -070015Getopt::Long::Configure("auto_help") if $Getopt::Long::VERSION > 2.32;
James Zern805078a2014-02-23 16:33:14 -080016
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>) {
Johann8645a532014-09-10 10:27:58 -070061 next if !/^(?:CONFIG_|HAVE_)/;
James Zern805078a2014-02-23 16:33:14 -080062 chomp;
James Zern2b3496f2017-02-13 18:52:09 -080063 s/\r$//;
James Zern805078a2014-02-23 16:33:14 -080064 my @pair = split /=/;
65 $config{$pair[0]} = $pair[1];
66}
67close CONFIG_FILE;
68
69#
70# Routines for the RTCD DSL to call
71#
Yaowu Xuf883b422016-08-30 14:01:10 -070072sub aom_config($) {
James Zern805078a2014-02-23 16:33:14 -080073 return (defined $config{$_[0]}) ? $config{$_[0]} : "";
74}
75
76sub specialize {
Urvang Joshi5ddac0a2017-03-30 14:44:48 -070077 if (@_ <= 1) {
78 die "'specialize' must be called with a function name and at least one ",
79 "architecture ('C' is implied): \n@_\n";
80 }
James Zern805078a2014-02-23 16:33:14 -080081 my $fn=$_[0];
82 shift;
83 foreach my $opt (@_) {
84 eval "\$${fn}_${opt}=${fn}_${opt}";
85 }
86}
87
88sub add_proto {
89 my $fn = splice(@_, -2, 1);
90 $ALL_FUNCS{$fn} = \@_;
91 specialize $fn, "c";
92}
93
94sub require {
95 foreach my $fn (keys %ALL_FUNCS) {
96 foreach my $opt (@_) {
97 my $ofn = eval "\$${fn}_${opt}";
98 next if !$ofn;
99
100 # if we already have a default, then we can disable it, as we know
101 # we can do better.
102 my $best = eval "\$${fn}_default";
103 if ($best) {
104 my $best_ofn = eval "\$${best}";
105 if ($best_ofn && "$best_ofn" ne "$ofn") {
106 eval "\$${best}_link = 'false'";
107 }
108 }
109 eval "\$${fn}_default=${fn}_${opt}";
110 eval "\$${fn}_${opt}_link='true'";
111 }
112 }
113}
114
115sub forward_decls {
116 push @ALL_FORWARD_DECLS, @_;
117}
118
119#
120# Include the user's directives
121#
122foreach my $f (@ARGV) {
123 open FILE, "<", $f or die "cannot open $f: $!\n";
124 my $contents = join('', <FILE>);
125 close FILE;
126 eval $contents or warn "eval failed: $@\n";
127}
128
129#
130# Process the directives according to the command line
131#
132sub process_forward_decls() {
133 foreach (@ALL_FORWARD_DECLS) {
134 $_->();
135 }
136}
137
138sub determine_indirection {
Yaowu Xuf883b422016-08-30 14:01:10 -0700139 aom_config("CONFIG_RUNTIME_CPU_DETECT") eq "yes" or &require(@ALL_ARCHS);
James Zern805078a2014-02-23 16:33:14 -0800140 foreach my $fn (keys %ALL_FUNCS) {
141 my $n = "";
142 my @val = @{$ALL_FUNCS{$fn}};
143 my $args = pop @val;
144 my $rtyp = "@val";
145 my $dfn = eval "\$${fn}_default";
146 $dfn = eval "\$${dfn}";
147 foreach my $opt (@_) {
148 my $ofn = eval "\$${fn}_${opt}";
149 next if !$ofn;
150 my $link = eval "\$${fn}_${opt}_link";
151 next if $link && $link eq "false";
152 $n .= "x";
153 }
154 if ($n eq "x") {
155 eval "\$${fn}_indirect = 'false'";
156 } else {
157 eval "\$${fn}_indirect = 'true'";
158 }
159 }
160}
161
162sub declare_function_pointers {
163 foreach my $fn (sort keys %ALL_FUNCS) {
164 my @val = @{$ALL_FUNCS{$fn}};
165 my $args = pop @val;
166 my $rtyp = "@val";
167 my $dfn = eval "\$${fn}_default";
168 $dfn = eval "\$${dfn}";
169 foreach my $opt (@_) {
170 my $ofn = eval "\$${fn}_${opt}";
171 next if !$ofn;
172 print "$rtyp ${ofn}($args);\n";
173 }
174 if (eval "\$${fn}_indirect" eq "false") {
175 print "#define ${fn} ${dfn}\n";
176 } else {
177 print "RTCD_EXTERN $rtyp (*${fn})($args);\n";
178 }
179 print "\n";
180 }
181}
182
183sub set_function_pointers {
184 foreach my $fn (sort keys %ALL_FUNCS) {
185 my @val = @{$ALL_FUNCS{$fn}};
186 my $args = pop @val;
187 my $rtyp = "@val";
188 my $dfn = eval "\$${fn}_default";
189 $dfn = eval "\$${dfn}";
190 if (eval "\$${fn}_indirect" eq "true") {
191 print " $fn = $dfn;\n";
192 foreach my $opt (@_) {
193 my $ofn = eval "\$${fn}_${opt}";
194 next if !$ofn;
195 next if "$ofn" eq "$dfn";
196 my $link = eval "\$${fn}_${opt}_link";
197 next if $link && $link eq "false";
198 my $cond = eval "\$have_${opt}";
199 print " if (${cond}) $fn = $ofn;\n"
200 }
201 }
202 }
203}
204
205sub filter {
206 my @filtered;
207 foreach (@_) { push @filtered, $_ unless $disabled{$_}; }
208 return @filtered;
209}
210
211#
212# Helper functions for generating the arch specific RTCD files
213#
214sub common_top() {
215 my $include_guard = uc($opts{sym})."_H_";
216 print <<EOF;
Johann368c51a2017-12-15 09:08:41 -0800217// This file is generated. Do not edit.
James Zern805078a2014-02-23 16:33:14 -0800218#ifndef ${include_guard}
219#define ${include_guard}
220
221#ifdef RTCD_C
222#define RTCD_EXTERN
223#else
224#define RTCD_EXTERN extern
225#endif
226
James Zern9d3fb752014-09-18 19:43:19 -0700227EOF
228
229process_forward_decls();
230print <<EOF;
231
James Zern805078a2014-02-23 16:33:14 -0800232#ifdef __cplusplus
233extern "C" {
234#endif
235
236EOF
James Zern805078a2014-02-23 16:33:14 -0800237declare_function_pointers("c", @ALL_ARCHS);
238
239print <<EOF;
240void $opts{sym}(void);
241
242EOF
243}
244
245sub common_bottom() {
246 print <<EOF;
247
248#ifdef __cplusplus
249} // extern "C"
250#endif
251
252#endif
253EOF
254}
255
256sub x86() {
257 determine_indirection("c", @ALL_ARCHS);
258
259 # Assign the helper variable for each enabled extension
260 foreach my $opt (@ALL_ARCHS) {
261 my $opt_uc = uc $opt;
262 eval "\$have_${opt}=\"flags & HAS_${opt_uc}\"";
263 }
264
265 common_top;
266 print <<EOF;
267#ifdef RTCD_C
Yaowu Xuc27fc142016-08-22 16:08:15 -0700268#include "aom_ports/x86.h"
James Zern805078a2014-02-23 16:33:14 -0800269static void setup_rtcd_internal(void)
270{
271 int flags = x86_simd_caps();
272
273 (void)flags;
274
275EOF
276
277 set_function_pointers("c", @ALL_ARCHS);
278
279 print <<EOF;
280}
281#endif
282EOF
283 common_bottom;
284}
285
286sub arm() {
287 determine_indirection("c", @ALL_ARCHS);
288
289 # Assign the helper variable for each enabled extension
290 foreach my $opt (@ALL_ARCHS) {
291 my $opt_uc = uc $opt;
Johannce239312014-05-07 11:01:31 -0700292 # Enable neon assembly based on HAVE_NEON logic instead of adding new
293 # HAVE_NEON_ASM logic
294 if ($opt eq 'neon_asm') { $opt_uc = 'NEON' }
James Zern805078a2014-02-23 16:33:14 -0800295 eval "\$have_${opt}=\"flags & HAS_${opt_uc}\"";
296 }
297
298 common_top;
299 print <<EOF;
Yaowu Xuf883b422016-08-30 14:01:10 -0700300#include "aom_config.h"
James Zern805078a2014-02-23 16:33:14 -0800301
302#ifdef RTCD_C
Yaowu Xuc27fc142016-08-22 16:08:15 -0700303#include "aom_ports/arm.h"
James Zern805078a2014-02-23 16:33:14 -0800304static void setup_rtcd_internal(void)
305{
306 int flags = arm_cpu_caps();
307
308 (void)flags;
309
310EOF
311
312 set_function_pointers("c", @ALL_ARCHS);
313
314 print <<EOF;
315}
316#endif
317EOF
318 common_bottom;
319}
320
321sub mips() {
322 determine_indirection("c", @ALL_ARCHS);
323 common_top;
324
325 print <<EOF;
Yaowu Xuf883b422016-08-30 14:01:10 -0700326#include "aom_config.h"
James Zern805078a2014-02-23 16:33:14 -0800327
328#ifdef RTCD_C
329static void setup_rtcd_internal(void)
330{
331EOF
332
333 set_function_pointers("c", @ALL_ARCHS);
334
335 print <<EOF;
336#if HAVE_DSPR2
Yaowu Xuf883b422016-08-30 14:01:10 -0700337void aom_dsputil_static_init();
338aom_dsputil_static_init();
James Zern805078a2014-02-23 16:33:14 -0800339#endif
340}
341#endif
342EOF
343 common_bottom;
344}
345
346sub unoptimized() {
347 determine_indirection "c";
348 common_top;
349 print <<EOF;
Yaowu Xuf883b422016-08-30 14:01:10 -0700350#include "aom_config.h"
James Zern805078a2014-02-23 16:33:14 -0800351
352#ifdef RTCD_C
353static void setup_rtcd_internal(void)
354{
355EOF
356
357 set_function_pointers "c";
358
359 print <<EOF;
360}
361#endif
362EOF
363 common_bottom;
364}
365
366#
367# Main Driver
368#
369
370&require("c");
371if ($opts{arch} eq 'x86') {
372 @ALL_ARCHS = filter(qw/mmx sse sse2 sse3 ssse3 sse4_1 avx avx2/);
373 x86;
374} elsif ($opts{arch} eq 'x86_64') {
375 @ALL_ARCHS = filter(qw/mmx sse sse2 sse3 ssse3 sse4_1 avx avx2/);
376 @REQUIRES = filter(keys %required ? keys %required : qw/mmx sse sse2/);
377 &require(@REQUIRES);
378 x86;
Gordana Cmiljanovic1c31e3e2014-08-07 19:09:47 +0200379} elsif ($opts{arch} eq 'mips32' || $opts{arch} eq 'mips64') {
380 @ALL_ARCHS = filter("$opts{arch}");
James Zern805078a2014-02-23 16:33:14 -0800381 open CONFIG_FILE, $opts{config} or
382 die "Error opening config file '$opts{config}': $!\n";
383 while (<CONFIG_FILE>) {
384 if (/HAVE_DSPR2=yes/) {
Gordana Cmiljanovic1c31e3e2014-08-07 19:09:47 +0200385 @ALL_ARCHS = filter("$opts{arch}", qw/dspr2/);
James Zern805078a2014-02-23 16:33:14 -0800386 last;
387 }
Parag Salasakar84ec68d2015-03-16 12:36:59 +0530388 if (/HAVE_MSA=yes/) {
389 @ALL_ARCHS = filter("$opts{arch}", qw/msa/);
390 last;
391 }
James Zern805078a2014-02-23 16:33:14 -0800392 }
393 close CONFIG_FILE;
394 mips;
James Zern6ea881c2014-12-15 18:39:51 -0800395} elsif ($opts{arch} =~ /armv7\w?/) {
Ralph Gilesbe111b32017-05-12 16:44:45 -0700396 @ALL_ARCHS = filter(qw/neon_asm neon/);
Johann24fbfa42014-07-29 11:28:23 -0700397 &require(@REQUIRES);
Johannce239312014-05-07 11:01:31 -0700398 arm;
Tom Fineganc47e4202014-09-19 10:45:15 -0700399} elsif ($opts{arch} eq 'armv8' || $opts{arch} eq 'arm64' ) {
Johannce239312014-05-07 11:01:31 -0700400 @ALL_ARCHS = filter(qw/neon/);
James Zern805078a2014-02-23 16:33:14 -0800401 arm;
402} else {
403 unoptimized;
404}
405
406__END__
407
408=head1 NAME
409
410rtcd -
411
412=head1 SYNOPSIS
413
414Usage: rtcd.pl [options] FILE
415
416See 'perldoc rtcd.pl' for more details.
417
418=head1 DESCRIPTION
419
420Reads the Run Time CPU Detections definitions from FILE and generates a
421C header file on stdout.
422
423=head1 OPTIONS
424
425Options:
426 --arch=ARCH Architecture to generate defs for (required)
427 --disable-EXT Disable support for EXT extensions
428 --require-EXT Require support for EXT extensions
429 --sym=SYMBOL Unique symbol to use for RTCD initialization function
430 --config=FILE File with CONFIG_FOO=yes lines to parse