Skip to content

GH-91095: Specialize calls to normal Python classes. #99331

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
1 change: 1 addition & 0 deletions Include/cpython/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ struct _specialization_cache {
// *args nor **kwargs (as required by BINARY_SUBSCR_GETITEM):
PyObject *getitem;
uint32_t getitem_version;
PyObject *init;
};

/* The *real* layout of a type object when allocated on the heap */
Expand Down
24 changes: 24 additions & 0 deletions Include/internal/pycore_frame.h
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,30 @@ _PyFrame_PushUnchecked(PyThreadState *tstate, PyFunctionObject *func, int null_l
return new_frame;
}

/* Pushes a trampoline frame without checking for space.
* Must be guarded by _PyThreadState_HasStackSpace() */
static inline _PyInterpreterFrame *
_PyFrame_PushTrampolineUnchecked(PyThreadState *tstate, PyCodeObject *code, int stackdepth, int prev_instr)
{
CALL_STAT_INC(frames_pushed);
_PyInterpreterFrame *frame = (_PyInterpreterFrame *)tstate->datastack_top;
tstate->datastack_top += code->co_framesize;
assert(tstate->datastack_top < tstate->datastack_limit);
frame->f_funcobj = Py_None;
frame->f_executable = Py_NewRef(code);
#ifdef Py_DEBUG
frame->f_builtins = NULL;
frame->f_globals = NULL;
#endif
frame->f_locals = NULL;
frame->stacktop = code->co_nlocalsplus + stackdepth;
frame->frame_obj = NULL;
frame->prev_instr = _PyCode_CODE(code) + prev_instr;
frame->owner = FRAME_OWNED_BY_THREAD;
frame->return_offset = 0;
return frame;
}

static inline
PyGenObject *_PyFrame_GetGenerator(_PyInterpreterFrame *frame)
{
Expand Down
2 changes: 2 additions & 0 deletions Include/internal/pycore_object.h
Original file line number Diff line number Diff line change
Expand Up @@ -355,8 +355,10 @@ static inline int _PyType_SUPPORTS_WEAKREFS(PyTypeObject *type) {
}

extern PyObject* _PyType_AllocNoTrack(PyTypeObject *type, Py_ssize_t nitems);
PyObject *_PyType_NewManagedObject(PyTypeObject *type);

extern int _PyObject_InitializeDict(PyObject *obj);
int _PyObject_InitInlineValues(PyObject *obj, PyTypeObject *tp);
extern int _PyObject_StoreInstanceAttribute(PyObject *obj, PyDictValues *values,
PyObject *name, PyObject *value);
PyObject * _PyObject_GetInstanceAttribute(PyObject *obj, PyDictValues *values,
Expand Down
46 changes: 23 additions & 23 deletions Include/internal/pycore_opcode.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

108 changes: 55 additions & 53 deletions Include/opcode.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Lib/_opcode_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
"CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS",
"CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS",
"CALL_NO_KW_METHOD_DESCRIPTOR_FAST",
"CALL_NO_KW_ALLOC_AND_ENTER_INIT",
],
}

Expand Down
1 change: 1 addition & 0 deletions Lib/opcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ def pseudo_op(name, op, real_ops):
def_op('UNARY_NOT', 12)

def_op('UNARY_INVERT', 15)
def_op('EXIT_INIT_CHECK', 16)

# We reserve 17 as it is the initial value for the specializing counter
# This helps us catch cases where we attempt to execute a cache.
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_sys.py
Original file line number Diff line number Diff line change
Expand Up @@ -1557,7 +1557,7 @@ def delx(self): del self.__x
'10P' # PySequenceMethods
'2P' # PyBufferProcs
'6P'
'1PI' # Specializer cache
'1PIP' # Specializer cache
)
class newstyleclass(object): pass
# Separate block for PyDictKeysObject with 8 keys and 5 entries
Expand Down
24 changes: 23 additions & 1 deletion Lib/test/test_sys_settrace.py
Original file line number Diff line number Diff line change
Expand Up @@ -1614,8 +1614,30 @@ def func():

self.run_and_compare(func, EXPECTED_EVENTS)

def test_settrace_error(self):
def test_correct_tracing_quickened_call_class_init(self):

class C:
def __init__(self):
self

def func():
C()

EXPECTED_EVENTS = [
(0, 'call'),
(1, 'line'),
(-3, 'call'),
(-2, 'line'),
(-2, 'return'),
(1, 'return')]

self.run_and_compare(func, EXPECTED_EVENTS)
# Quicken
for _ in range(100):
func()
self.run_and_compare(func, EXPECTED_EVENTS)

def test_settrace_error(self):
raised = False
def error_once(frame, event, arg):
nonlocal raised
Expand Down
Loading