Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mesa
Path: blob/21.2-virgl/src/mapi/mapi_abi.py
4558 views
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 <[email protected]>
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('#ifdef MemoryBarrier')
485
print('#undef MemoryBarrier')
486
print('#endif')
487
print()
488
print(self.c_public_declarations(self.prefix_lib))
489
print('#undef MAPI_TMP_DEFINES')
490
print('#endif /* MAPI_TMP_DEFINES */')
491
492
if self.lib_need_table_size:
493
print()
494
print('#ifdef MAPI_TMP_TABLE')
495
print(self.c_mapi_table())
496
print('#undef MAPI_TMP_TABLE')
497
print('#endif /* MAPI_TMP_TABLE */')
498
499
if self.lib_need_noop_array:
500
print()
501
print('#ifdef MAPI_TMP_NOOP_ARRAY')
502
print('#ifdef DEBUG')
503
print()
504
print(self.c_noop_functions(self.prefix_noop, self.prefix_warn))
505
print()
506
print('const mapi_func table_%s_array[] = {' % (self.prefix_noop))
507
print(self.c_noop_initializer(self.prefix_noop, False))
508
print('};')
509
print()
510
print('#else /* DEBUG */')
511
print()
512
print('const mapi_func table_%s_array[] = {' % (self.prefix_noop))
513
print(self.c_noop_initializer(self.prefix_noop, True))
514
print('};')
515
print()
516
print('#endif /* DEBUG */')
517
print('#undef MAPI_TMP_NOOP_ARRAY')
518
print('#endif /* MAPI_TMP_NOOP_ARRAY */')
519
520
if self.lib_need_stubs:
521
pool, pool_offsets = self.c_stub_string_pool()
522
print()
523
print('#ifdef MAPI_TMP_PUBLIC_STUBS')
524
print('static const char public_string_pool[] =')
525
print(pool)
526
print()
527
print('static const struct mapi_stub public_stubs[] = {')
528
print(self.c_stub_initializer(self.prefix_lib, pool_offsets))
529
print('};')
530
print('#undef MAPI_TMP_PUBLIC_STUBS')
531
print('#endif /* MAPI_TMP_PUBLIC_STUBS */')
532
533
if self.lib_need_all_entries:
534
print()
535
print('#ifdef MAPI_TMP_PUBLIC_ENTRIES')
536
print(self.c_public_dispatches(self.prefix_lib, False))
537
print()
538
print('static const mapi_func public_entries[] = {')
539
print(self.c_public_initializer(self.prefix_lib))
540
print('};')
541
print('#undef MAPI_TMP_PUBLIC_ENTRIES')
542
print('#endif /* MAPI_TMP_PUBLIC_ENTRIES */')
543
544
print()
545
print('#ifdef MAPI_TMP_STUB_ASM_GCC')
546
print('__asm__(')
547
print(self.c_asm_gcc(self.prefix_lib, False))
548
print(');')
549
print('#undef MAPI_TMP_STUB_ASM_GCC')
550
print('#endif /* MAPI_TMP_STUB_ASM_GCC */')
551
552
if self.lib_need_non_hidden_entries:
553
all_hidden = True
554
for ent in self.entries:
555
if not ent.hidden:
556
all_hidden = False
557
break
558
if not all_hidden:
559
print()
560
print('#ifdef MAPI_TMP_PUBLIC_ENTRIES_NO_HIDDEN')
561
print(self.c_public_dispatches(self.prefix_lib, True))
562
print()
563
print('/* does not need public_entries */')
564
print('#undef MAPI_TMP_PUBLIC_ENTRIES_NO_HIDDEN')
565
print('#endif /* MAPI_TMP_PUBLIC_ENTRIES_NO_HIDDEN */')
566
567
print()
568
print('#ifdef MAPI_TMP_STUB_ASM_GCC_NO_HIDDEN')
569
print('__asm__(')
570
print(self.c_asm_gcc(self.prefix_lib, True))
571
print(');')
572
print('#undef MAPI_TMP_STUB_ASM_GCC_NO_HIDDEN')
573
print('#endif /* MAPI_TMP_STUB_ASM_GCC_NO_HIDDEN */')
574
575
class GLAPIPrinter(ABIPrinter):
576
"""OpenGL API Printer"""
577
578
def __init__(self, entries):
579
for ent in entries:
580
self._override_for_api(ent)
581
super(GLAPIPrinter, self).__init__(entries)
582
583
self.api_defines = ['GL_GLEXT_PROTOTYPES']
584
self.api_headers = ['"GL/gl.h"', '"GL/glext.h"']
585
self.api_call = 'GLAPI'
586
self.api_entry = 'APIENTRY'
587
self.api_attrs = ''
588
589
self.lib_need_table_size = False
590
self.lib_need_noop_array = False
591
self.lib_need_stubs = False
592
self.lib_need_all_entries = False
593
self.lib_need_non_hidden_entries = True
594
595
self.prefix_lib = 'GLAPI_PREFIX'
596
self.prefix_noop = 'noop'
597
self.prefix_warn = self.prefix_lib
598
599
self.c_header = self._get_c_header()
600
601
def _override_for_api(self, ent):
602
"""Override attributes of an entry if necessary for this
603
printer."""
604
# By default, no override is necessary.
605
pass
606
607
def _get_c_header(self):
608
header = """#ifndef _GLAPI_TMP_H_
609
#define _GLAPI_TMP_H_
610
#define GLAPI_PREFIX(func) gl##func
611
#define GLAPI_PREFIX_STR(func) "gl"#func
612
613
typedef int GLclampx;
614
#endif /* _GLAPI_TMP_H_ */"""
615
616
return header
617
618
class SharedGLAPIPrinter(GLAPIPrinter):
619
"""Shared GLAPI API Printer"""
620
621
def __init__(self, entries):
622
super(SharedGLAPIPrinter, self).__init__(entries)
623
624
self.lib_need_table_size = True
625
self.lib_need_noop_array = True
626
self.lib_need_stubs = True
627
self.lib_need_all_entries = True
628
self.lib_need_non_hidden_entries = False
629
630
self.prefix_lib = 'shared'
631
self.prefix_warn = 'gl'
632
633
def _override_for_api(self, ent):
634
ent.hidden = True
635
ent.handcode = False
636
637
def _get_c_header(self):
638
header = """#ifndef _GLAPI_TMP_H_
639
#define _GLAPI_TMP_H_
640
typedef int GLclampx;
641
#endif /* _GLAPI_TMP_H_ */"""
642
643
return header
644
645
def parse_args():
646
printers = ['glapi', 'es1api', 'es2api', 'shared-glapi']
647
648
parser = OptionParser(usage='usage: %prog [options] <xml_file>')
649
parser.add_option('-p', '--printer', dest='printer',
650
help='printer to use: %s' % (", ".join(printers)))
651
652
options, args = parser.parse_args()
653
if not args or options.printer not in printers:
654
parser.print_help()
655
sys.exit(1)
656
657
if not args[0].endswith('.xml'):
658
parser.print_help()
659
sys.exit(1)
660
661
return (args[0], options)
662
663
def main():
664
printers = {
665
'glapi': GLAPIPrinter,
666
'shared-glapi': SharedGLAPIPrinter,
667
}
668
669
filename, options = parse_args()
670
671
entries = abi_parse_xml(filename)
672
abi_sanity_check(entries)
673
674
printer = printers[options.printer](entries)
675
printer.output_for_lib()
676
677
if __name__ == '__main__':
678
main()
679
680