rtcd.pl 8.7 KB

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