Skip to content

Commit 04492cb

Browse files
authored
GH-91095: Specialize calls to normal Python classes. (GH-99331)
1 parent c01da28 commit 04492cb

20 files changed

+511
-189
lines changed

Diff for: Include/cpython/object.h

+1
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,7 @@ struct _specialization_cache {
246246
// *args nor **kwargs (as required by BINARY_SUBSCR_GETITEM):
247247
PyObject *getitem;
248248
uint32_t getitem_version;
249+
PyObject *init;
249250
};
250251

251252
/* The *real* layout of a type object when allocated on the heap */

Diff for: Include/internal/pycore_frame.h

+24
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,30 @@ _PyFrame_PushUnchecked(PyThreadState *tstate, PyFunctionObject *func, int null_l
272272
return new_frame;
273273
}
274274

275+
/* Pushes a trampoline frame without checking for space.
276+
* Must be guarded by _PyThreadState_HasStackSpace() */
277+
static inline _PyInterpreterFrame *
278+
_PyFrame_PushTrampolineUnchecked(PyThreadState *tstate, PyCodeObject *code, int stackdepth, int prev_instr)
279+
{
280+
CALL_STAT_INC(frames_pushed);
281+
_PyInterpreterFrame *frame = (_PyInterpreterFrame *)tstate->datastack_top;
282+
tstate->datastack_top += code->co_framesize;
283+
assert(tstate->datastack_top < tstate->datastack_limit);
284+
frame->f_funcobj = Py_None;
285+
frame->f_executable = Py_NewRef(code);
286+
#ifdef Py_DEBUG
287+
frame->f_builtins = NULL;
288+
frame->f_globals = NULL;
289+
#endif
290+
frame->f_locals = NULL;
291+
frame->stacktop = code->co_nlocalsplus + stackdepth;
292+
frame->frame_obj = NULL;
293+
frame->prev_instr = _PyCode_CODE(code) + prev_instr;
294+
frame->owner = FRAME_OWNED_BY_THREAD;
295+
frame->return_offset = 0;
296+
return frame;
297+
}
298+
275299
static inline
276300
PyGenObject *_PyFrame_GetGenerator(_PyInterpreterFrame *frame)
277301
{

Diff for: Include/internal/pycore_object.h

+2
Original file line numberDiff line numberDiff line change
@@ -355,8 +355,10 @@ static inline int _PyType_SUPPORTS_WEAKREFS(PyTypeObject *type) {
355355
}
356356

357357
extern PyObject* _PyType_AllocNoTrack(PyTypeObject *type, Py_ssize_t nitems);
358+
PyObject *_PyType_NewManagedObject(PyTypeObject *type);
358359

359360
extern int _PyObject_InitializeDict(PyObject *obj);
361+
int _PyObject_InitInlineValues(PyObject *obj, PyTypeObject *tp);
360362
extern int _PyObject_StoreInstanceAttribute(PyObject *obj, PyDictValues *values,
361363
PyObject *name, PyObject *value);
362364
PyObject * _PyObject_GetInstanceAttribute(PyObject *obj, PyDictValues *values,

Diff for: Include/internal/pycore_opcode.h

+23-23
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: Include/opcode.h

+55-53
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: Lib/_opcode_metadata.py

+1
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@
8585
"CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS",
8686
"CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS",
8787
"CALL_NO_KW_METHOD_DESCRIPTOR_FAST",
88+
"CALL_NO_KW_ALLOC_AND_ENTER_INIT",
8889
],
8990
}
9091

Diff for: Lib/opcode.py

+1
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ def pseudo_op(name, op, real_ops):
9797
def_op('UNARY_NOT', 12)
9898

9999
def_op('UNARY_INVERT', 15)
100+
def_op('EXIT_INIT_CHECK', 16)
100101

101102
# We reserve 17 as it is the initial value for the specializing counter
102103
# This helps us catch cases where we attempt to execute a cache.

Diff for: Lib/test/test_sys.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1557,7 +1557,7 @@ def delx(self): del self.__x
15571557
'10P' # PySequenceMethods
15581558
'2P' # PyBufferProcs
15591559
'6P'
1560-
'1PI' # Specializer cache
1560+
'1PIP' # Specializer cache
15611561
)
15621562
class newstyleclass(object): pass
15631563
# Separate block for PyDictKeysObject with 8 keys and 5 entries

Diff for: Lib/test/test_sys_settrace.py

+23-1
Original file line numberDiff line numberDiff line change
@@ -1614,8 +1614,30 @@ def func():
16141614

16151615
self.run_and_compare(func, EXPECTED_EVENTS)
16161616

1617-
def test_settrace_error(self):
1617+
def test_correct_tracing_quickened_call_class_init(self):
1618+
1619+
class C:
1620+
def __init__(self):
1621+
self
1622+
1623+
def func():
1624+
C()
16181625

1626+
EXPECTED_EVENTS = [
1627+
(0, 'call'),
1628+
(1, 'line'),
1629+
(-3, 'call'),
1630+
(-2, 'line'),
1631+
(-2, 'return'),
1632+
(1, 'return')]
1633+
1634+
self.run_and_compare(func, EXPECTED_EVENTS)
1635+
# Quicken
1636+
for _ in range(100):
1637+
func()
1638+
self.run_and_compare(func, EXPECTED_EVENTS)
1639+
1640+
def test_settrace_error(self):
16191641
raised = False
16201642
def error_once(frame, event, arg):
16211643
nonlocal raised

0 commit comments

Comments
 (0)