]>
Commit | Line | Data |
---|---|---|
df26ea53 | 1 | #!/usr/bin/python |
b168057a | 2 | # Copyright (C) 2014-2015 Free Software Foundation, Inc. |
df26ea53 SP |
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 | |
56737508 | 53 | double timing; |
df26ea53 SP |
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 = ''' | |
56737508 SP |
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)) | |
df26ea53 SP |
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 | ||
15eaf6ff SP |
131 | # Test initialization. |
132 | if directives['init']: | |
133 | print('#define BENCH_INIT %s' % directives['init']) | |
134 | ||
df26ea53 SP |
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;' % (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()): | |
56737508 | 179 | out = [' {%s, 0},' % v for v in vals] |
df26ea53 SP |
180 | |
181 | # Members for the variants structure list that we will | |
182 | # print later. | |
cb5e4aad | 183 | variants.append(' {"%s", %d, in%d},' % (k, len(vals), i)) |
df26ea53 SP |
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': [], | |
15eaf6ff SP |
239 | 'ret': '', |
240 | 'init': '' | |
df26ea53 SP |
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:])) |