Skip to content

Commit 38f199d

Browse files
authored
[mypyc] Do all vtable setup dynamically at runtime (#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)