glthread: simplify repeated function sequences in marshal_generated.c
[mesa.git] / src / mapi / mapi_abi.py
1
2 # Mesa 3-D graphics library
3 #
4 # Copyright (C) 2010 LunarG Inc.
5 #
6 # Permission is hereby granted, free of charge, to any person obtaining a
7 # copy of this software and associated documentation files (the "Software"),
8 # to deal in the Software without restriction, including without limitation
9 # the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 # and/or sell copies of the Software, and to permit persons to whom the
11 # Software is furnished to do so, subject to the following conditions:
12 #
13 # The above copyright notice and this permission notice shall be included
14 # in all copies or substantial portions of the Software.
15 #
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 # DEALINGS IN THE SOFTWARE.
23 #
24 # Authors:
25 # Chia-I Wu <olv@lunarg.com>
26
27 from __future__ import print_function
28
29 import sys
30 # make it possible to import glapi
31 import os
32 GLAPI = os.path.join(".", os.path.dirname(__file__), "glapi", "gen")
33 sys.path.insert(0, GLAPI)
34
35 from operator import attrgetter
36 import re
37 from optparse import OptionParser
38 import gl_XML
39 import glX_XML
40
41
42 # number of dynamic entries
43 ABI_NUM_DYNAMIC_ENTRIES = 256
44
45 class ABIEntry(object):
46 """Represent an ABI entry."""
47
48 _match_c_param = re.compile(
49 '^(?P<type>[\w\s*]+?)(?P<name>\w+)(\[(?P<array>\d+)\])?$')
50
51 def __init__(self, cols, attrs, xml_data = None):
52 self._parse(cols)
53
54 self.slot = attrs['slot']
55 self.hidden = attrs['hidden']
56 self.alias = attrs['alias']
57 self.handcode = attrs['handcode']
58 self.xml_data = xml_data
59
60 def c_prototype(self):
61 return '%s %s(%s)' % (self.c_return(), self.name, self.c_params())
62
63 def c_return(self):
64 ret = self.ret
65 if not ret:
66 ret = 'void'
67
68 return ret
69
70 def c_params(self):
71 """Return the parameter list used in the entry prototype."""
72 c_params = []
73 for t, n, a in self.params:
74 sep = '' if t.endswith('*') else ' '
75 arr = '[%d]' % a if a else ''
76 c_params.append(t + sep + n + arr)
77 if not c_params:
78 c_params.append('void')
79
80 return ", ".join(c_params)
81
82 def c_args(self):
83 """Return the argument list used in the entry invocation."""
84 c_args = []
85 for t, n, a in self.params:
86 c_args.append(n)
87
88 return ", ".join(c_args)
89
90 def _parse(self, cols):
91 ret = cols.pop(0)
92 if ret == 'void':
93 ret = None
94
95 name = cols.pop(0)
96
97 params = []
98 if not cols:
99 raise Exception(cols)
100 elif len(cols) == 1 and cols[0] == 'void':
101 pass
102 else:
103 for val in cols:
104 params.append(self._parse_param(val))
105
106 self.ret = ret
107 self.name = name
108 self.params = params
109
110 def _parse_param(self, c_param):
111 m = self._match_c_param.match(c_param)
112 if not m:
113 raise Exception('unrecognized param ' + c_param)
114
115 c_type = m.group('type').strip()
116 c_name = m.group('name')
117 c_array = m.group('array')
118 c_array = int(c_array) if c_array else 0
119
120 return (c_type, c_name, c_array)
121
122 def __str__(self):
123 return self.c_prototype()
124
125 def __lt__(self, other):
126 # compare slot, alias, and then name
127 if self.slot == other.slot:
128 if not self.alias:
129 return True
130 elif not other.alias:
131 return False
132
133 return self.name < other.name
134
135 return self.slot < other.slot
136
137
138 def abi_parse_xml(xml):
139 """Parse a GLAPI XML file for ABI entries."""
140 api = gl_XML.parse_GL_API(xml, glX_XML.glx_item_factory())
141
142 entry_dict = {}
143 for func in api.functionIterateByOffset():
144 # make sure func.name appear first
145 entry_points = func.entry_points[:]
146 entry_points.remove(func.name)
147 entry_points.insert(0, func.name)
148
149 for name in entry_points:
150 attrs = {
151 'slot': func.offset,
152 'hidden': not func.is_static_entry_point(name),
153 'alias': None if name == func.name else func.name,
154 'handcode': bool(func.has_different_protocol(name)),
155 }
156
157 # post-process attrs
158 if attrs['alias']:
159 try:
160 alias = entry_dict[attrs['alias']]
161 except KeyError:
162 raise Exception('failed to alias %s' % attrs['alias'])
163 if alias.alias:
164 raise Exception('recursive alias %s' % ent.name)
165 attrs['alias'] = alias
166 if attrs['handcode']:
167 attrs['handcode'] = func.static_glx_name(name)
168 else:
169 attrs['handcode'] = None
170
171 if name in entry_dict:
172 raise Exception('%s is duplicated' % (name))
173
174 cols = []
175 cols.append(func.return_type)
176 cols.append(name)
177 params = func.get_parameter_string(name)
178 cols.extend([p.strip() for p in params.split(',')])
179
180 ent = ABIEntry(cols, attrs, func)
181 entry_dict[ent.name] = ent
182
183 entries = sorted(entry_dict.values())
184
185 return entries
186
187 def abi_sanity_check(entries):
188 if not entries:
189 return
190
191 all_names = []
192 last_slot = entries[-1].slot
193 i = 0
194 for slot in range(last_slot + 1):
195 if entries[i].slot != slot:
196 raise Exception('entries are not ordered by slots')
197 if entries[i].alias:
198 raise Exception('first entry of slot %d aliases %s'
199 % (slot, entries[i].alias.name))
200 handcode = None
201 while i < len(entries) and entries[i].slot == slot:
202 ent = entries[i]
203 if not handcode and ent.handcode:
204 handcode = ent.handcode
205 elif ent.handcode != handcode:
206 raise Exception('two aliases with handcode %s != %s',
207 ent.handcode, handcode)
208
209 if ent.name in all_names:
210 raise Exception('%s is duplicated' % (ent.name))
211 if ent.alias and ent.alias.name not in all_names:
212 raise Exception('failed to alias %s' % (ent.alias.name))
213 all_names.append(ent.name)
214 i += 1
215 if i < len(entries):
216 raise Exception('there are %d invalid entries' % (len(entries) - 1))
217
218 class ABIPrinter(object):
219 """MAPI Printer"""
220
221 def __init__(self, entries):
222 self.entries = entries
223
224 # sort entries by their names
225 self.entries_sorted_by_names = sorted(self.entries, key=attrgetter('name'))
226
227 self.indent = ' ' * 3
228 self.noop_warn = 'noop_warn'
229 self.noop_generic = 'noop_generic'
230 self.current_get = 'entry_current_get'
231
232 self.api_defines = []
233 self.api_headers = ['"KHR/khrplatform.h"']
234 self.api_call = 'KHRONOS_APICALL'
235 self.api_entry = 'KHRONOS_APIENTRY'
236 self.api_attrs = 'KHRONOS_APIATTRIBUTES'
237
238 self.c_header = ''
239
240 self.lib_need_table_size = True
241 self.lib_need_noop_array = True
242 self.lib_need_stubs = True
243 self.lib_need_all_entries = True
244 self.lib_need_non_hidden_entries = False
245
246 def c_notice(self):
247 return '/* This file is automatically generated by mapi_abi.py. Do not modify. */'
248
249 def c_public_includes(self):
250 """Return includes of the client API headers."""
251 defines = ['#define ' + d for d in self.api_defines]
252 includes = ['#include ' + h for h in self.api_headers]
253 return "\n".join(defines + includes)
254
255 def need_entry_point(self, ent):
256 """Return True if an entry point is needed for the entry."""
257 # non-handcode hidden aliases may share the entry they alias
258 use_alias = (ent.hidden and ent.alias and not ent.handcode)
259 return not use_alias
260
261 def c_public_declarations(self, prefix):
262 """Return the declarations of public entry points."""
263 decls = []
264 for ent in self.entries:
265 if not self.need_entry_point(ent):
266 continue
267 export = self.api_call if not ent.hidden else ''
268 if not ent.hidden or not self.lib_need_non_hidden_entries:
269 decls.append(self._c_decl(ent, prefix, True, export) + ';')
270
271 return "\n".join(decls)
272
273 def c_mapi_table(self):
274 """Return defines of the dispatch table size."""
275 num_static_entries = self.entries[-1].slot + 1
276 return ('#define MAPI_TABLE_NUM_STATIC %d\n' + \
277 '#define MAPI_TABLE_NUM_DYNAMIC %d') % (
278 num_static_entries, ABI_NUM_DYNAMIC_ENTRIES)
279
280 def _c_function(self, ent, prefix, mangle=False, stringify=False):
281 """Return the function name of an entry."""
282 formats = {
283 True: { True: '%s_STR(%s)', False: '%s(%s)' },
284 False: { True: '"%s%s"', False: '%s%s' },
285 }
286 fmt = formats[prefix.isupper()][stringify]
287 name = ent.name
288 if mangle and ent.hidden:
289 name = '_dispatch_stub_' + str(ent.slot)
290 return fmt % (prefix, name)
291
292 def _c_function_call(self, ent, prefix):
293 """Return the function name used for calling."""
294 if ent.handcode:
295 # _c_function does not handle this case
296 formats = { True: '%s(%s)', False: '%s%s' }
297 fmt = formats[prefix.isupper()]
298 name = fmt % (prefix, ent.handcode)
299 elif self.need_entry_point(ent):
300 name = self._c_function(ent, prefix, True)
301 else:
302 name = self._c_function(ent.alias, prefix, True)
303 return name
304
305 def _c_decl(self, ent, prefix, mangle=False, export=''):
306 """Return the C declaration for the entry."""
307 decl = '%s %s %s(%s)' % (ent.c_return(), self.api_entry,
308 self._c_function(ent, prefix, mangle), ent.c_params())
309 if export:
310 decl = export + ' ' + decl
311 if self.api_attrs:
312 decl += ' ' + self.api_attrs
313
314 return decl
315
316 def _c_cast(self, ent):
317 """Return the C cast for the entry."""
318 cast = '%s (%s *)(%s)' % (
319 ent.c_return(), self.api_entry, ent.c_params())
320
321 return cast
322
323 def c_public_dispatches(self, prefix, no_hidden):
324 """Return the public dispatch functions."""
325 dispatches = []
326 for ent in self.entries:
327 if ent.hidden and no_hidden:
328 continue
329
330 if not self.need_entry_point(ent):
331 continue
332
333 export = self.api_call if not ent.hidden else ''
334
335 proto = self._c_decl(ent, prefix, True, export)
336 cast = self._c_cast(ent)
337
338 ret = ''
339 if ent.ret:
340 ret = 'return '
341 stmt1 = self.indent
342 stmt1 += 'const struct _glapi_table *_tbl = %s();' % (
343 self.current_get)
344 stmt2 = self.indent
345 stmt2 += 'mapi_func _func = ((const mapi_func *) _tbl)[%d];' % (
346 ent.slot)
347 stmt3 = self.indent
348 stmt3 += '%s((%s) _func)(%s);' % (ret, cast, ent.c_args())
349
350 disp = '%s\n{\n%s\n%s\n%s\n}' % (proto, stmt1, stmt2, stmt3)
351
352 if ent.handcode:
353 disp = '#if 0\n' + disp + '\n#endif'
354
355 dispatches.append(disp)
356
357 return '\n\n'.join(dispatches)
358
359 def c_public_initializer(self, prefix):
360 """Return the initializer for public dispatch functions."""
361 names = []
362 for ent in self.entries:
363 if ent.alias:
364 continue
365
366 name = '%s(mapi_func) %s' % (self.indent,
367 self._c_function_call(ent, prefix))
368 names.append(name)
369
370 return ',\n'.join(names)
371
372 def c_stub_string_pool(self):
373 """Return the string pool for use by stubs."""
374 # sort entries by their names
375 sorted_entries = sorted(self.entries, key=attrgetter('name'))
376
377 pool = []
378 offsets = {}
379 count = 0
380 for ent in sorted_entries:
381 offsets[ent] = count
382 pool.append('%s' % (ent.name))
383 count += len(ent.name) + 1
384
385 pool_str = self.indent + '"' + \
386 ('\\0"\n' + self.indent + '"').join(pool) + '";'
387 return (pool_str, offsets)
388
389 def c_stub_initializer(self, prefix, pool_offsets):
390 """Return the initializer for struct mapi_stub array."""
391 stubs = []
392 for ent in self.entries_sorted_by_names:
393 stubs.append('%s{ (void *) %d, %d, NULL }' % (
394 self.indent, pool_offsets[ent], ent.slot))
395
396 return ',\n'.join(stubs)
397
398 def c_noop_functions(self, prefix, warn_prefix):
399 """Return the noop functions."""
400 noops = []
401 for ent in self.entries:
402 if ent.alias:
403 continue
404
405 proto = self._c_decl(ent, prefix, False, 'static')
406
407 stmt1 = self.indent;
408 space = ''
409 for t, n, a in ent.params:
410 stmt1 += "%s(void) %s;" % (space, n)
411 space = ' '
412
413 if ent.params:
414 stmt1 += '\n';
415
416 stmt1 += self.indent + '%s(%s);' % (self.noop_warn,
417 self._c_function(ent, warn_prefix, False, True))
418
419 if ent.ret:
420 stmt2 = self.indent + 'return (%s) 0;' % (ent.ret)
421 noop = '%s\n{\n%s\n%s\n}' % (proto, stmt1, stmt2)
422 else:
423 noop = '%s\n{\n%s\n}' % (proto, stmt1)
424
425 noops.append(noop)
426
427 return '\n\n'.join(noops)
428
429 def c_noop_initializer(self, prefix, use_generic):
430 """Return an initializer for the noop dispatch table."""
431 entries = [self._c_function(ent, prefix)
432 for ent in self.entries if not ent.alias]
433 if use_generic:
434 entries = [self.noop_generic] * len(entries)
435
436 entries.extend([self.noop_generic] * ABI_NUM_DYNAMIC_ENTRIES)
437
438 pre = self.indent + '(mapi_func) '
439 return pre + (',\n' + pre).join(entries)
440
441 def c_asm_gcc(self, prefix, no_hidden):
442 asm = []
443
444 for ent in self.entries:
445 if ent.hidden and no_hidden:
446 continue
447
448 if not self.need_entry_point(ent):
449 continue
450
451 name = self._c_function(ent, prefix, True, True)
452
453 if ent.handcode:
454 asm.append('#if 0')
455
456 if ent.hidden:
457 asm.append('".hidden "%s"\\n"' % (name))
458
459 if ent.alias and not (ent.alias.hidden and no_hidden):
460 asm.append('".globl "%s"\\n"' % (name))
461 asm.append('".set "%s", "%s"\\n"' % (name,
462 self._c_function(ent.alias, prefix, True, True)))
463 else:
464 asm.append('STUB_ASM_ENTRY(%s)"\\n"' % (name))
465 asm.append('"\\t"STUB_ASM_CODE("%d")"\\n"' % (ent.slot))
466
467 if ent.handcode:
468 asm.append('#endif')
469 asm.append('')
470
471 return "\n".join(asm)
472
473 def output_for_lib(self):
474 print(self.c_notice())
475
476 if self.c_header:
477 print()
478 print(self.c_header)
479
480 print()
481 print('#ifdef MAPI_TMP_DEFINES')
482 print(self.c_public_includes())
483 print()
484 print(self.c_public_declarations(self.prefix_lib))
485 print('#undef MAPI_TMP_DEFINES')
486 print('#endif /* MAPI_TMP_DEFINES */')
487
488 if self.lib_need_table_size:
489 print()
490 print('#ifdef MAPI_TMP_TABLE')
491 print(self.c_mapi_table())
492 print('#undef MAPI_TMP_TABLE')
493 print('#endif /* MAPI_TMP_TABLE */')
494
495 if self.lib_need_noop_array:
496 print()
497 print('#ifdef MAPI_TMP_NOOP_ARRAY')
498 print('#ifdef DEBUG')
499 print()
500 print(self.c_noop_functions(self.prefix_noop, self.prefix_warn))
501 print()
502 print('const mapi_func table_%s_array[] = {' % (self.prefix_noop))
503 print(self.c_noop_initializer(self.prefix_noop, False))
504 print('};')
505 print()
506 print('#else /* DEBUG */')
507 print()
508 print('const mapi_func table_%s_array[] = {' % (self.prefix_noop))
509 print(self.c_noop_initializer(self.prefix_noop, True))
510 print('};')
511 print()
512 print('#endif /* DEBUG */')
513 print('#undef MAPI_TMP_NOOP_ARRAY')
514 print('#endif /* MAPI_TMP_NOOP_ARRAY */')
515
516 if self.lib_need_stubs:
517 pool, pool_offsets = self.c_stub_string_pool()
518 print()
519 print('#ifdef MAPI_TMP_PUBLIC_STUBS')
520 print('static const char public_string_pool[] =')
521 print(pool)
522 print()
523 print('static const struct mapi_stub public_stubs[] = {')
524 print(self.c_stub_initializer(self.prefix_lib, pool_offsets))
525 print('};')
526 print('#undef MAPI_TMP_PUBLIC_STUBS')
527 print('#endif /* MAPI_TMP_PUBLIC_STUBS */')
528
529 if self.lib_need_all_entries:
530 print()
531 print('#ifdef MAPI_TMP_PUBLIC_ENTRIES')
532 print(self.c_public_dispatches(self.prefix_lib, False))
533 print()
534 print('static const mapi_func public_entries[] = {')
535 print(self.c_public_initializer(self.prefix_lib))
536 print('};')
537 print('#undef MAPI_TMP_PUBLIC_ENTRIES')
538 print('#endif /* MAPI_TMP_PUBLIC_ENTRIES */')
539
540 print()
541 print('#ifdef MAPI_TMP_STUB_ASM_GCC')
542 print('__asm__(')
543 print(self.c_asm_gcc(self.prefix_lib, False))
544 print(');')
545 print('#undef MAPI_TMP_STUB_ASM_GCC')
546 print('#endif /* MAPI_TMP_STUB_ASM_GCC */')
547
548 if self.lib_need_non_hidden_entries:
549 all_hidden = True
550 for ent in self.entries:
551 if not ent.hidden:
552 all_hidden = False
553 break
554 if not all_hidden:
555 print()
556 print('#ifdef MAPI_TMP_PUBLIC_ENTRIES_NO_HIDDEN')
557 print(self.c_public_dispatches(self.prefix_lib, True))
558 print()
559 print('/* does not need public_entries */')
560 print('#undef MAPI_TMP_PUBLIC_ENTRIES_NO_HIDDEN')
561 print('#endif /* MAPI_TMP_PUBLIC_ENTRIES_NO_HIDDEN */')
562
563 print()
564 print('#ifdef MAPI_TMP_STUB_ASM_GCC_NO_HIDDEN')
565 print('__asm__(')
566 print(self.c_asm_gcc(self.prefix_lib, True))
567 print(');')
568 print('#undef MAPI_TMP_STUB_ASM_GCC_NO_HIDDEN')
569 print('#endif /* MAPI_TMP_STUB_ASM_GCC_NO_HIDDEN */')
570
571 class GLAPIPrinter(ABIPrinter):
572 """OpenGL API Printer"""
573
574 def __init__(self, entries):
575 for ent in entries:
576 self._override_for_api(ent)
577 super(GLAPIPrinter, self).__init__(entries)
578
579 self.api_defines = ['GL_GLEXT_PROTOTYPES']
580 self.api_headers = ['"GL/gl.h"', '"GL/glext.h"']
581 self.api_call = 'GLAPI'
582 self.api_entry = 'APIENTRY'
583 self.api_attrs = ''
584
585 self.lib_need_table_size = False
586 self.lib_need_noop_array = False
587 self.lib_need_stubs = False
588 self.lib_need_all_entries = False
589 self.lib_need_non_hidden_entries = True
590
591 self.prefix_lib = 'GLAPI_PREFIX'
592 self.prefix_noop = 'noop'
593 self.prefix_warn = self.prefix_lib
594
595 self.c_header = self._get_c_header()
596
597 def _override_for_api(self, ent):
598 """Override attributes of an entry if necessary for this
599 printer."""
600 # By default, no override is necessary.
601 pass
602
603 def _get_c_header(self):
604 header = """#ifndef _GLAPI_TMP_H_
605 #define _GLAPI_TMP_H_
606 #define GLAPI_PREFIX(func) gl##func
607 #define GLAPI_PREFIX_STR(func) "gl"#func
608
609 typedef int GLclampx;
610 #endif /* _GLAPI_TMP_H_ */"""
611
612 return header
613
614 class SharedGLAPIPrinter(GLAPIPrinter):
615 """Shared GLAPI API Printer"""
616
617 def __init__(self, entries):
618 super(SharedGLAPIPrinter, self).__init__(entries)
619
620 self.lib_need_table_size = True
621 self.lib_need_noop_array = True
622 self.lib_need_stubs = True
623 self.lib_need_all_entries = True
624 self.lib_need_non_hidden_entries = False
625
626 self.prefix_lib = 'shared'
627 self.prefix_warn = 'gl'
628
629 def _override_for_api(self, ent):
630 ent.hidden = True
631 ent.handcode = False
632
633 def _get_c_header(self):
634 header = """#ifndef _GLAPI_TMP_H_
635 #define _GLAPI_TMP_H_
636 typedef int GLclampx;
637 #endif /* _GLAPI_TMP_H_ */"""
638
639 return header
640
641 def parse_args():
642 printers = ['glapi', 'es1api', 'es2api', 'shared-glapi']
643
644 parser = OptionParser(usage='usage: %prog [options] <xml_file>')
645 parser.add_option('-p', '--printer', dest='printer',
646 help='printer to use: %s' % (", ".join(printers)))
647
648 options, args = parser.parse_args()
649 if not args or options.printer not in printers:
650 parser.print_help()
651 sys.exit(1)
652
653 if not args[0].endswith('.xml'):
654 parser.print_help()
655 sys.exit(1)
656
657 return (args[0], options)
658
659 def main():
660 printers = {
661 'glapi': GLAPIPrinter,
662 'shared-glapi': SharedGLAPIPrinter,
663 }
664
665 filename, options = parse_args()
666
667 entries = abi_parse_xml(filename)
668 abi_sanity_check(entries)
669
670 printer = printers[options.printer](entries)
671 printer.output_for_lib()
672
673 if __name__ == '__main__':
674 main()