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