| xf.li | bdd93d5 | 2023-05-12 07:10:14 -0700 | [diff] [blame] | 1 | #!/usr/bin/python | 
|  | 2 | # Copyright (C) 2014-2016 Free Software Foundation, Inc. | 
|  | 3 | # This file is part of the GNU C Library. | 
|  | 4 | # | 
|  | 5 | # The GNU C Library is free software; you can redistribute it and/or | 
|  | 6 | # modify it under the terms of the GNU Lesser General Public | 
|  | 7 | # License as published by the Free Software Foundation; either | 
|  | 8 | # version 2.1 of the License, or (at your option) any later version. | 
|  | 9 | # | 
|  | 10 | # The GNU C Library is distributed in the hope that it will be useful, | 
|  | 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|  | 13 | # Lesser General Public License for more details. | 
|  | 14 | # | 
|  | 15 | # You should have received a copy of the GNU Lesser General Public | 
|  | 16 | # License along with the GNU C Library; if not, see | 
|  | 17 | # <http://www.gnu.org/licenses/>. | 
|  | 18 |  | 
|  | 19 | """Benchmark program generator script | 
|  | 20 |  | 
|  | 21 | This script takes a function name as input and generates a program using | 
|  | 22 | an input file located in the benchtests directory.  The name of the | 
|  | 23 | input file should be of the form foo-inputs where 'foo' is the name of | 
|  | 24 | the function. | 
|  | 25 | """ | 
|  | 26 |  | 
|  | 27 | from __future__ import print_function | 
|  | 28 | import sys | 
|  | 29 | import os | 
|  | 30 | import itertools | 
|  | 31 |  | 
|  | 32 | # Macro definitions for functions that take no arguments.  For functions | 
|  | 33 | # that take arguments, the STRUCT_TEMPLATE, ARGS_TEMPLATE and | 
|  | 34 | # VARIANTS_TEMPLATE are used instead. | 
|  | 35 | DEFINES_TEMPLATE = ''' | 
|  | 36 | #define CALL_BENCH_FUNC(v, i) %(func)s () | 
|  | 37 | #define NUM_VARIANTS (1) | 
|  | 38 | #define NUM_SAMPLES(v) (1) | 
|  | 39 | #define VARIANT(v) FUNCNAME "()" | 
|  | 40 | ''' | 
|  | 41 |  | 
|  | 42 | # Structures to store arguments for the function call.  A function may | 
|  | 43 | # have its inputs partitioned to represent distinct performance | 
|  | 44 | # characteristics or distinct flavors of the function.  Each such | 
|  | 45 | # variant is represented by the _VARIANT structure.  The ARGS structure | 
|  | 46 | # represents a single set of arguments. | 
|  | 47 | STRUCT_TEMPLATE = ''' | 
|  | 48 | #define CALL_BENCH_FUNC(v, i) %(func)s (%(func_args)s) | 
|  | 49 |  | 
|  | 50 | struct args | 
|  | 51 | { | 
|  | 52 | %(args)s | 
|  | 53 | double timing; | 
|  | 54 | }; | 
|  | 55 |  | 
|  | 56 | struct _variants | 
|  | 57 | { | 
|  | 58 | const char *name; | 
|  | 59 | int count; | 
|  | 60 | struct args *in; | 
|  | 61 | }; | 
|  | 62 | ''' | 
|  | 63 |  | 
|  | 64 | # The actual input arguments. | 
|  | 65 | ARGS_TEMPLATE = ''' | 
|  | 66 | struct args in%(argnum)d[%(num_args)d] = { | 
|  | 67 | %(args)s | 
|  | 68 | }; | 
|  | 69 | ''' | 
|  | 70 |  | 
|  | 71 | # The actual variants, along with macros defined to access the variants. | 
|  | 72 | VARIANTS_TEMPLATE = ''' | 
|  | 73 | struct _variants variants[%(num_variants)d] = { | 
|  | 74 | %(variants)s | 
|  | 75 | }; | 
|  | 76 |  | 
|  | 77 | #define NUM_VARIANTS %(num_variants)d | 
|  | 78 | #define NUM_SAMPLES(i) (variants[i].count) | 
|  | 79 | #define VARIANT(i) (variants[i].name) | 
|  | 80 | ''' | 
|  | 81 |  | 
|  | 82 | # Epilogue for the generated source file. | 
|  | 83 | EPILOGUE = ''' | 
|  | 84 | #define RESULT(__v, __i) (variants[(__v)].in[(__i)].timing) | 
|  | 85 | #define RESULT_ACCUM(r, v, i, old, new) \\ | 
|  | 86 | ((RESULT ((v), (i))) = (RESULT ((v), (i)) * (old) + (r)) / ((new) + 1)) | 
|  | 87 | #define BENCH_FUNC(i, j) ({%(getret)s CALL_BENCH_FUNC (i, j);}) | 
|  | 88 | #define FUNCNAME "%(func)s" | 
|  | 89 | #include "bench-skeleton.c"''' | 
|  | 90 |  | 
|  | 91 |  | 
|  | 92 | def gen_source(func, directives, all_vals): | 
|  | 93 | """Generate source for the function | 
|  | 94 |  | 
|  | 95 | Generate the C source for the function from the values and | 
|  | 96 | directives. | 
|  | 97 |  | 
|  | 98 | Args: | 
|  | 99 | func: The function name | 
|  | 100 | directives: A dictionary of directives applicable to this function | 
|  | 101 | all_vals: A dictionary input values | 
|  | 102 | """ | 
|  | 103 | # The includes go in first. | 
|  | 104 | for header in directives['includes']: | 
|  | 105 | print('#include <%s>' % header) | 
|  | 106 |  | 
|  | 107 | for header in directives['include-sources']: | 
|  | 108 | print('#include "%s"' % header) | 
|  | 109 |  | 
|  | 110 | # Print macros.  This branches out to a separate routine if | 
|  | 111 | # the function takes arguments. | 
|  | 112 | if not directives['args']: | 
|  | 113 | print(DEFINES_TEMPLATE % {'func': func}) | 
|  | 114 | outargs = [] | 
|  | 115 | else: | 
|  | 116 | outargs = _print_arg_data(func, directives, all_vals) | 
|  | 117 |  | 
|  | 118 | # Print the output variable definitions if necessary. | 
|  | 119 | for out in outargs: | 
|  | 120 | print(out) | 
|  | 121 |  | 
|  | 122 | # If we have a return value from the function, make sure it is | 
|  | 123 | # assigned to prevent the compiler from optimizing out the | 
|  | 124 | # call. | 
|  | 125 | if directives['ret']: | 
|  | 126 | print('static %s volatile ret;' % directives['ret']) | 
|  | 127 | getret = 'ret = ' | 
|  | 128 | else: | 
|  | 129 | getret = '' | 
|  | 130 |  | 
|  | 131 | # Test initialization. | 
|  | 132 | if directives['init']: | 
|  | 133 | print('#define BENCH_INIT %s' % directives['init']) | 
|  | 134 |  | 
|  | 135 | print(EPILOGUE % {'getret': getret, 'func': func}) | 
|  | 136 |  | 
|  | 137 |  | 
|  | 138 | def _print_arg_data(func, directives, all_vals): | 
|  | 139 | """Print argument data | 
|  | 140 |  | 
|  | 141 | This is a helper function for gen_source that prints structure and | 
|  | 142 | values for arguments and their variants and returns output arguments | 
|  | 143 | if any are found. | 
|  | 144 |  | 
|  | 145 | Args: | 
|  | 146 | func: Function name | 
|  | 147 | directives: A dictionary of directives applicable to this function | 
|  | 148 | all_vals: A dictionary input values | 
|  | 149 |  | 
|  | 150 | Returns: | 
|  | 151 | Returns a list of definitions for function arguments that act as | 
|  | 152 | output parameters. | 
|  | 153 | """ | 
|  | 154 | # First, all of the definitions.  We process writing of | 
|  | 155 | # CALL_BENCH_FUNC, struct args and also the output arguments | 
|  | 156 | # together in a single traversal of the arguments list. | 
|  | 157 | func_args = [] | 
|  | 158 | arg_struct = [] | 
|  | 159 | outargs = [] | 
|  | 160 |  | 
|  | 161 | for arg, i in zip(directives['args'], itertools.count()): | 
|  | 162 | if arg[0] == '<' and arg[-1] == '>': | 
|  | 163 | pos = arg.rfind('*') | 
|  | 164 | if pos == -1: | 
|  | 165 | die('Output argument must be a pointer type') | 
|  | 166 |  | 
|  | 167 | outargs.append('static %s out%d __attribute__((used));' % (arg[1:pos], i)) | 
|  | 168 | func_args.append(' &out%d' % i) | 
|  | 169 | else: | 
|  | 170 | arg_struct.append('  %s volatile arg%d;' % (arg, i)) | 
|  | 171 | func_args.append('variants[v].in[i].arg%d' % i) | 
|  | 172 |  | 
|  | 173 | print(STRUCT_TEMPLATE % {'args' : '\n'.join(arg_struct), 'func': func, | 
|  | 174 | 'func_args': ', '.join(func_args)}) | 
|  | 175 |  | 
|  | 176 | # Now print the values. | 
|  | 177 | variants = [] | 
|  | 178 | for (k, vals), i in zip(all_vals.items(), itertools.count()): | 
|  | 179 | out = ['  {%s, 0},' % v for v in vals] | 
|  | 180 |  | 
|  | 181 | # Members for the variants structure list that we will | 
|  | 182 | # print later. | 
|  | 183 | variants.append('  {"%s", %d, in%d},' % (k, len(vals), i)) | 
|  | 184 | print(ARGS_TEMPLATE % {'argnum': i, 'num_args': len(vals), | 
|  | 185 | 'args': '\n'.join(out)}) | 
|  | 186 |  | 
|  | 187 | # Print the variants and the last set of macros. | 
|  | 188 | print(VARIANTS_TEMPLATE % {'num_variants': len(all_vals), | 
|  | 189 | 'variants': '\n'.join(variants)}) | 
|  | 190 | return outargs | 
|  | 191 |  | 
|  | 192 |  | 
|  | 193 | def _process_directive(d_name, d_val): | 
|  | 194 | """Process a directive. | 
|  | 195 |  | 
|  | 196 | Evaluate the directive name and value passed and return the | 
|  | 197 | processed value. This is a helper function for parse_file. | 
|  | 198 |  | 
|  | 199 | Args: | 
|  | 200 | d_name: Name of the directive | 
|  | 201 | d_val: The string value to process | 
|  | 202 |  | 
|  | 203 | Returns: | 
|  | 204 | The processed value, which may be the string as it is or an object | 
|  | 205 | that describes the directive. | 
|  | 206 | """ | 
|  | 207 | # Process the directive values if necessary.  name and ret don't | 
|  | 208 | # need any processing. | 
|  | 209 | if d_name.startswith('include'): | 
|  | 210 | d_val = d_val.split(',') | 
|  | 211 | elif d_name == 'args': | 
|  | 212 | d_val = d_val.split(':') | 
|  | 213 |  | 
|  | 214 | # Return the values. | 
|  | 215 | return d_val | 
|  | 216 |  | 
|  | 217 |  | 
|  | 218 | def parse_file(func): | 
|  | 219 | """Parse an input file | 
|  | 220 |  | 
|  | 221 | Given a function name, open and parse an input file for the function | 
|  | 222 | and get the necessary parameters for the generated code and the list | 
|  | 223 | of inputs. | 
|  | 224 |  | 
|  | 225 | Args: | 
|  | 226 | func: The function name | 
|  | 227 |  | 
|  | 228 | Returns: | 
|  | 229 | A tuple of two elements, one a dictionary of directives and the | 
|  | 230 | other a dictionary of all input values. | 
|  | 231 | """ | 
|  | 232 | all_vals = {} | 
|  | 233 | # Valid directives. | 
|  | 234 | directives = { | 
|  | 235 | 'name': '', | 
|  | 236 | 'args': [], | 
|  | 237 | 'includes': [], | 
|  | 238 | 'include-sources': [], | 
|  | 239 | 'ret': '', | 
|  | 240 | 'init': '' | 
|  | 241 | } | 
|  | 242 |  | 
|  | 243 | try: | 
|  | 244 | with open('%s-inputs' % func) as f: | 
|  | 245 | for line in f: | 
|  | 246 | # Look for directives and parse it if found. | 
|  | 247 | if line.startswith('##'): | 
|  | 248 | try: | 
|  | 249 | d_name, d_val = line[2:].split(':', 1) | 
|  | 250 | d_name = d_name.strip() | 
|  | 251 | d_val = d_val.strip() | 
|  | 252 | directives[d_name] = _process_directive(d_name, d_val) | 
|  | 253 | except (IndexError, KeyError): | 
|  | 254 | die('Invalid directive: %s' % line[2:]) | 
|  | 255 |  | 
|  | 256 | # Skip blank lines and comments. | 
|  | 257 | line = line.split('#', 1)[0].rstrip() | 
|  | 258 | if not line: | 
|  | 259 | continue | 
|  | 260 |  | 
|  | 261 | # Otherwise, we're an input.  Add to the appropriate | 
|  | 262 | # input set. | 
|  | 263 | cur_name = directives['name'] | 
|  | 264 | all_vals.setdefault(cur_name, []) | 
|  | 265 | all_vals[cur_name].append(line) | 
|  | 266 | except IOError as ex: | 
|  | 267 | die("Failed to open input file (%s): %s" % (ex.filename, ex.strerror)) | 
|  | 268 |  | 
|  | 269 | return directives, all_vals | 
|  | 270 |  | 
|  | 271 |  | 
|  | 272 | def die(msg): | 
|  | 273 | """Exit with an error | 
|  | 274 |  | 
|  | 275 | Prints an error message to the standard error stream and exits with | 
|  | 276 | a non-zero status. | 
|  | 277 |  | 
|  | 278 | Args: | 
|  | 279 | msg: The error message to print to standard error | 
|  | 280 | """ | 
|  | 281 | print('%s\n' % msg, file=sys.stderr) | 
|  | 282 | sys.exit(os.EX_DATAERR) | 
|  | 283 |  | 
|  | 284 |  | 
|  | 285 | def main(args): | 
|  | 286 | """Main function | 
|  | 287 |  | 
|  | 288 | Use the first command line argument as function name and parse its | 
|  | 289 | input file to generate C source that calls the function repeatedly | 
|  | 290 | for the input. | 
|  | 291 |  | 
|  | 292 | Args: | 
|  | 293 | args: The command line arguments with the program name dropped | 
|  | 294 |  | 
|  | 295 | Returns: | 
|  | 296 | os.EX_USAGE on error and os.EX_OK on success. | 
|  | 297 | """ | 
|  | 298 | if len(args) != 1: | 
|  | 299 | print('Usage: %s <function>' % sys.argv[0]) | 
|  | 300 | return os.EX_USAGE | 
|  | 301 |  | 
|  | 302 | directives, all_vals = parse_file(args[0]) | 
|  | 303 | gen_source(args[0], directives, all_vals) | 
|  | 304 | return os.EX_OK | 
|  | 305 |  | 
|  | 306 |  | 
|  | 307 | if __name__ == '__main__': | 
|  | 308 | sys.exit(main(sys.argv[1:])) |