Skip to content

Commit 38f199d

Browse files
authored
[mypyc] Do all vtable setup dynamically at runtime (python#7629)
This lets us eliminate the CPy_FixupTraitVtable hack where we fixed up vtables at runtime in a hokey manner and will allow us to populate vtables with entries loaded dynamically from other modules once we have separate compilation.
1 parent 643a58b commit 38f199d

File tree

4 files changed

+95
-98
lines changed

4 files changed

+95
-98
lines changed

mypyc/emitclass.py

Lines changed: 35 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ def emit_line() -> None:
198198
generate_dealloc_for_class(cl, dealloc_name, clear_name, emitter)
199199
emit_line()
200200
generate_native_getters_and_setters(cl, emitter)
201-
vtable_name = generate_vtables(cl, vtable_name, emitter)
201+
vtable_name = generate_vtables(cl, vtable_setup_name, vtable_name, emitter)
202202
emit_line()
203203
if needs_getseters:
204204
generate_getseter_declarations(cl, emitter)
@@ -222,7 +222,6 @@ def emit_line() -> None:
222222
t=emitter.type_struct_name(cl)))
223223

224224
emitter.emit_line()
225-
generate_trait_vtable_setup(cl, vtable_setup_name, vtable_name, emitter)
226225
if generate_full:
227226
generate_setup_for_class(cl, setup_name, defaults_fn, vtable_name, emitter)
228227
emitter.emit_line()
@@ -307,39 +306,63 @@ def generate_native_getters_and_setters(cl: ClassIR,
307306

308307

309308
def generate_vtables(base: ClassIR,
309+
vtable_setup_name: str,
310310
vtable_name: str,
311311
emitter: Emitter) -> str:
312-
"""Emit the vtables for a class.
312+
"""Emit the vtables and vtable setup functions for a class.
313313
314314
This includes both the primary vtable and any trait implementation vtables.
315315
316+
To account for both dynamic loading and dynamic class creation,
317+
vtables are populated dynamically at class creation time, so we
318+
emit empty array definitions to store the vtables and a function to
319+
populate them.
320+
316321
Returns the expression to use to refer to the vtable, which might be
317-
different than the name, if there are trait vtables."""
322+
different than the name, if there are trait vtables.
323+
"""
324+
325+
def trait_vtable_name(trait: ClassIR) -> str:
326+
return '{}_{}_trait_vtable'.format(
327+
base.name_prefix(emitter.names), trait.name_prefix(emitter.names))
328+
329+
# Emit array definitions with enough space for all the entries
330+
emitter.emit_line('static CPyVTableItem {}[{}];'.format(
331+
vtable_name,
332+
max(1, len(base.vtable_entries) + 2 * len(base.trait_vtables))))
333+
for trait, vtable in base.trait_vtables.items():
334+
emitter.emit_line('static CPyVTableItem {}[{}];'.format(
335+
trait_vtable_name(trait),
336+
max(1, len(vtable))))
337+
338+
# Emit vtable setup function
339+
emitter.emit_line('static bool')
340+
emitter.emit_line('{}{}(void)'.format(NATIVE_PREFIX, vtable_setup_name))
341+
emitter.emit_line('{')
318342

319343
subtables = []
320344
for trait, vtable in base.trait_vtables.items():
321-
name = '{}_{}_trait_vtable'.format(
322-
base.name_prefix(emitter.names), trait.name_prefix(emitter.names))
345+
name = trait_vtable_name(trait)
323346
generate_vtable(vtable, name, emitter, [])
324347
subtables.append((trait, name))
325348

326349
generate_vtable(base.vtable_entries, vtable_name, emitter, subtables)
327350

351+
emitter.emit_line('return 1;')
352+
emitter.emit_line('}')
353+
328354
return vtable_name if not subtables else "{} + {}".format(vtable_name, len(subtables) * 2)
329355

330356

331357
def generate_vtable(entries: VTableEntries,
332358
vtable_name: str,
333359
emitter: Emitter,
334360
subtables: List[Tuple[ClassIR, str]]) -> None:
335-
emitter.emit_line('static CPyVTableItem {}[] = {{'.format(vtable_name))
361+
emitter.emit_line('CPyVTableItem {}_scratch[] = {{'.format(vtable_name))
336362
if subtables:
337363
emitter.emit_line('/* Array of trait vtables */')
338364
for trait, table in subtables:
339-
# N.B: C only lets us store constant values. We do a nasty hack of
340-
# storing a pointer to the location, which we will then dynamically
341-
# patch up on module load in CPy_FixupTraitVtable.
342-
emitter.emit_line('(CPyVTableItem)&{}, (CPyVTableItem){},'.format(
365+
emitter.emit_line('(CPyVTableItem){}, (CPyVTableItem){},'.format(
343366
emitter.type_struct_name(trait), table))
344367
emitter.emit_line('/* Start of real vtable */')
345368

@@ -355,24 +378,7 @@ def generate_vtable(entries: VTableEntries,
355378
if not entries:
356379
emitter.emit_line('NULL')
357380
emitter.emit_line('};')
358-
359-
360-
def generate_trait_vtable_setup(cl: ClassIR,
361-
vtable_setup_name: str,
362-
vtable_name: str,
363-
emitter: Emitter) -> None:
364-
"""Generate a native function that fixes up the trait vtables of a class.
365-
366-
This needs to be called before a class is used.
367-
"""
368-
emitter.emit_line('static bool')
369-
emitter.emit_line('{}{}(void)'.format(NATIVE_PREFIX, vtable_setup_name))
370-
emitter.emit_line('{')
371-
if cl.trait_vtables and not cl.is_trait:
372-
emitter.emit_lines('CPy_FixupTraitVtable({}_vtable, {});'.format(
373-
cl.name_prefix(emitter.names), len(cl.trait_vtables)))
374-
emitter.emit_line('return 1;')
375-
emitter.emit_line('}')
381+
emitter.emit_line('memcpy({name}, {name}_scratch, sizeof({name}));'.format(name=vtable_name))
376382

377383

378384
def generate_setup_for_class(cl: ClassIR,

mypyc/genops.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1483,10 +1483,12 @@ def allocate_class(self, cdef: ClassDef) -> None:
14831483
tp = self.primitive_op(pytype_from_template_op,
14841484
[template, tp_bases, modname], cdef.line)
14851485
# Immediately fix up the trait vtables, before doing anything with the class.
1486-
self.add(Call(
1487-
FuncDecl(cdef.name + '_trait_vtable_setup',
1488-
None, self.module_name,
1489-
FuncSignature([], bool_rprimitive)), [], -1))
1486+
ir = self.mapper.type_to_ir[cdef.info]
1487+
if not ir.is_trait and not ir.builtin_base:
1488+
self.add(Call(
1489+
FuncDecl(cdef.name + '_trait_vtable_setup',
1490+
None, self.module_name,
1491+
FuncSignature([], bool_rprimitive)), [], -1))
14901492
# Populate a '__mypyc_attrs__' field containing the list of attrs
14911493
self.primitive_op(py_setattr_op, [
14921494
tp, self.load_static_unicode('__mypyc_attrs__'),

mypyc/lib-rt/CPy.h

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -57,15 +57,6 @@ static inline CPyVTableItem *CPy_FindTraitVtable(PyTypeObject *trait, CPyVTableI
5757
}
5858
}
5959

60-
// At load time, we need to patch up trait vtables to contain actual pointers
61-
// to the type objects of the trait, rather than an indirection.
62-
static inline void CPy_FixupTraitVtable(CPyVTableItem *vtable, int count) {
63-
int i;
64-
for (i = 0; i < count; i++) {
65-
vtable[i*2] = *(CPyVTableItem *)vtable[i*2];
66-
}
67-
}
68-
6960
static bool _CPy_IsSafeMetaClass(PyTypeObject *metaclass) {
7061
// mypyc classes can't work with metaclasses in
7162
// general. Through some various nasty hacks we *do*

mypyc/test-data/genops-classes.test

Lines changed: 54 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -369,31 +369,30 @@ def __top_level__():
369369
r50 :: object
370370
r51 :: str
371371
r52, r53 :: object
372-
r54 :: bool
373-
r55 :: str
374-
r56 :: tuple
375-
r57 :: bool
376-
r58 :: dict
377-
r59 :: str
378-
r60 :: bool
379-
r61, r62 :: object
380-
r63 :: dict
381-
r64 :: str
382-
r65 :: object
383-
r66 :: dict
384-
r67 :: str
385-
r68, r69 :: object
386-
r70 :: tuple
387-
r71 :: str
388-
r72, r73 :: object
389-
r74 :: bool
390-
r75, r76 :: str
391-
r77 :: tuple
392-
r78 :: bool
393-
r79 :: dict
394-
r80 :: str
395-
r81 :: bool
396-
r82 :: None
372+
r54 :: str
373+
r55 :: tuple
374+
r56 :: bool
375+
r57 :: dict
376+
r58 :: str
377+
r59 :: bool
378+
r60, r61 :: object
379+
r62 :: dict
380+
r63 :: str
381+
r64 :: object
382+
r65 :: dict
383+
r66 :: str
384+
r67, r68 :: object
385+
r69 :: tuple
386+
r70 :: str
387+
r71, r72 :: object
388+
r73 :: bool
389+
r74, r75 :: str
390+
r76 :: tuple
391+
r77 :: bool
392+
r78 :: dict
393+
r79 :: str
394+
r80 :: bool
395+
r81 :: None
397396
L0:
398397
r0 = builtins :: module
399398
r1 = builtins.None :: object
@@ -462,38 +461,37 @@ L6:
462461
r51 = unicode_7 :: static ('__main__')
463462
r52 = __main__.S_template :: type
464463
r53 = pytype_from_template(r52, r50, r51)
465-
r54 = S_trait_vtable_setup()
466-
r55 = unicode_8 :: static ('__mypyc_attrs__')
467-
r56 = () :: tuple
468-
r57 = setattr r53, r55, r56
464+
r54 = unicode_8 :: static ('__mypyc_attrs__')
465+
r55 = () :: tuple
466+
r56 = setattr r53, r54, r55
469467
__main__.S = r53 :: type
470-
r58 = __main__.globals :: static
471-
r59 = unicode_10 :: static ('S')
472-
r60 = r58.__setitem__(r59, r53) :: dict
473-
r61 = __main__.C :: type
474-
r62 = __main__.S :: type
475-
r63 = __main__.globals :: static
476-
r64 = unicode_3 :: static ('Generic')
477-
r65 = r63[r64] :: dict
478-
r66 = __main__.globals :: static
479-
r67 = unicode_6 :: static ('T')
480-
r68 = r66[r67] :: dict
481-
r69 = r65[r68] :: object
482-
r70 = (r61, r62, r69) :: tuple
483-
r71 = unicode_7 :: static ('__main__')
484-
r72 = __main__.D_template :: type
485-
r73 = pytype_from_template(r72, r70, r71)
486-
r74 = D_trait_vtable_setup()
487-
r75 = unicode_8 :: static ('__mypyc_attrs__')
488-
r76 = unicode_11 :: static ('__dict__')
489-
r77 = (r76) :: tuple
490-
r78 = setattr r73, r75, r77
491-
__main__.D = r73 :: type
492-
r79 = __main__.globals :: static
493-
r80 = unicode_12 :: static ('D')
494-
r81 = r79.__setitem__(r80, r73) :: dict
495-
r82 = None
496-
return r82
468+
r57 = __main__.globals :: static
469+
r58 = unicode_10 :: static ('S')
470+
r59 = r57.__setitem__(r58, r53) :: dict
471+
r60 = __main__.C :: type
472+
r61 = __main__.S :: type
473+
r62 = __main__.globals :: static
474+
r63 = unicode_3 :: static ('Generic')
475+
r64 = r62[r63] :: dict
476+
r65 = __main__.globals :: static
477+
r66 = unicode_6 :: static ('T')
478+
r67 = r65[r66] :: dict
479+
r68 = r64[r67] :: object
480+
r69 = (r60, r61, r68) :: tuple
481+
r70 = unicode_7 :: static ('__main__')
482+
r71 = __main__.D_template :: type
483+
r72 = pytype_from_template(r71, r69, r70)
484+
r73 = D_trait_vtable_setup()
485+
r74 = unicode_8 :: static ('__mypyc_attrs__')
486+
r75 = unicode_11 :: static ('__dict__')
487+
r76 = (r75) :: tuple
488+
r77 = setattr r72, r74, r76
489+
__main__.D = r72 :: type
490+
r78 = __main__.globals :: static
491+
r79 = unicode_12 :: static ('D')
492+
r80 = r78.__setitem__(r79, r72) :: dict
493+
r81 = None
494+
return r81
497495

498496
[case testIsInstance]
499497
class A: pass

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy