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