#! /usr/bin/perl -w # Copyright (C) 2013 Free Software Foundation, Inc. # This file is part of the GNU C Library. # The GNU C Library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # The GNU C Library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # You should have received a copy of the GNU Lesser General Public # License along with the GNU C Library; if not, see # . use strict; use warnings; # Generate a benchmark source file for a given input. if (@ARGV < 1) { die "Usage: bench.pl " } my $func = $ARGV[0]; my @args; my $ret = "void"; my $getret = ""; # We create a hash of inputs for each variant of the test. my $variant = ""; my @curvals; my %vals; my @include_headers; my @include_sources; my $incl; open INPUTS, "<$func-inputs" or die $!; LINE:while () { chomp; # Directives. if (/^## ([\w-]+): (.*)/) { # Function argument types. if ($1 eq "args") { @args = split(":", $2); } # Function return type. elsif ($1 eq "ret") { $ret = $2; } elsif ($1 eq "includes") { @include_headers = split (",", $2); } elsif ($1 eq "include-sources") { @include_sources = split (",", $2); } # New variant. This is the only directive allowed in the body of the # inputs to separate inputs into variants. All others should be at the # top or else all hell will break loose. elsif ($1 eq "name") { # Save values in the previous variant. my @copy = @curvals; $vals{$variant} = \@copy; # Prepare for the next. $variant=$2; undef @curvals; next LINE; } else { die "Unknown directive: ".$1; } } # Skip over comments and blank lines. if (/^#/ || /^$/) { next LINE; } push (@curvals, $_); } my $bench_func = "#define CALL_BENCH_FUNC(v, i) $func ("; # Output variables. These include the return value as well as any pointers # that may get passed into the function, denoted by the <> around the type. my $outvars = ""; if ($ret ne "void") { $outvars = "static $ret volatile ret;\n"; } # Print the definitions and macros. foreach $incl (@include_headers) { print "#include <" . $incl . ">\n"; } # Print the source files. foreach $incl (@include_sources) { print "#include \"" . $incl . "\"\n"; } if (@args > 0) { # Save values in the last variant. $vals{$variant} = \@curvals; my $struct = "struct _variants { const char *name; int count; struct args *in; };\n"; my $arg_struct = "struct args {"; my $num = 0; my $arg; foreach $arg (@args) { if ($num > 0) { $bench_func = "$bench_func,"; } $_ = $arg; if (/<(.*)\*>/) { # Output variables. These have to be pointers, so dereference once by # dropping one *. $outvars = $outvars . "static $1 out$num;\n"; $bench_func = "$bench_func &out$num"; } else { $arg_struct = "$arg_struct $arg volatile arg$num;"; $bench_func = "$bench_func variants[v].in[i].arg$num"; } $num = $num + 1; } $arg_struct = $arg_struct . "};\n"; $bench_func = $bench_func . ");\n"; print $bench_func; print $arg_struct; print $struct; my $c = 0; my $key; # Print the input arrays. foreach $key (keys %vals) { my @arr = @{$vals{$key}}; print "struct args in" . $c . "[" . @arr . "] = {\n"; foreach (@arr) { print "{$_},\n"; } print "};\n\n"; $c += 1; } # The variants. Each variant then points to the appropriate input array we # defined above. print "struct _variants variants[" . (keys %vals) . "] = {\n"; $c = 0; foreach $key (keys %vals) { print "{\"$func($key)\", " . @{$vals{$key}} . ", in$c},\n"; $c += 1; } print "};\n\n"; # Finally, print the last set of macros. print "#define NUM_VARIANTS $c\n"; print "#define NUM_SAMPLES(i) (variants[i].count)\n"; print "#define VARIANT(i) (variants[i].name)\n"; } else { print $bench_func . ");\n"; print "#define NUM_VARIANTS (1)\n"; print "#define NUM_SAMPLES(v) (1)\n"; print "#define VARIANT(v) FUNCNAME \"()\"\n" } # Print the output variable definitions. print "$outvars\n"; # In some cases not storing a return value seems to result in the function call # being optimized out. if ($ret ne "void") { $getret = "ret = "; } # And we're done. print "#define BENCH_FUNC(i, j) ({$getret CALL_BENCH_FUNC (i, j);})\n"; print "#define FUNCNAME \"$func\"\n"; print "#include \"bench-skeleton.c\"\n";