• 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  # 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