GH-104405: Add missing PEP 523 checks (GH-104406)
This commit is contained in:
parent
a10b026f0f
commit
1eb950ca55
@ -1748,28 +1748,80 @@ SUFFICIENT_TO_DEOPT_AND_SPECIALIZE = 100
|
||||
|
||||
class Test_Pep523API(unittest.TestCase):
|
||||
|
||||
def do_test(self, func):
|
||||
calls = []
|
||||
def do_test(self, func, names):
|
||||
actual_calls = []
|
||||
start = SUFFICIENT_TO_DEOPT_AND_SPECIALIZE
|
||||
count = start + SUFFICIENT_TO_DEOPT_AND_SPECIALIZE
|
||||
for i in range(count):
|
||||
if i == start:
|
||||
_testinternalcapi.set_eval_frame_record(calls)
|
||||
func()
|
||||
_testinternalcapi.set_eval_frame_default()
|
||||
self.assertEqual(len(calls), SUFFICIENT_TO_DEOPT_AND_SPECIALIZE)
|
||||
for name in calls:
|
||||
self.assertEqual(name, func.__name__)
|
||||
try:
|
||||
for i in range(count):
|
||||
if i == start:
|
||||
_testinternalcapi.set_eval_frame_record(actual_calls)
|
||||
func()
|
||||
finally:
|
||||
_testinternalcapi.set_eval_frame_default()
|
||||
expected_calls = names * SUFFICIENT_TO_DEOPT_AND_SPECIALIZE
|
||||
self.assertEqual(len(expected_calls), len(actual_calls))
|
||||
for expected, actual in zip(expected_calls, actual_calls, strict=True):
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_pep523_with_specialization_simple(self):
|
||||
def func1():
|
||||
pass
|
||||
self.do_test(func1)
|
||||
def test_inlined_binary_subscr(self):
|
||||
class C:
|
||||
def __getitem__(self, other):
|
||||
return None
|
||||
def func():
|
||||
C()[42]
|
||||
names = ["func", "__getitem__"]
|
||||
self.do_test(func, names)
|
||||
|
||||
def test_pep523_with_specialization_with_default(self):
|
||||
def func2(x=None):
|
||||
def test_inlined_call(self):
|
||||
def inner(x=42):
|
||||
pass
|
||||
self.do_test(func2)
|
||||
def func():
|
||||
inner()
|
||||
inner(42)
|
||||
names = ["func", "inner", "inner"]
|
||||
self.do_test(func, names)
|
||||
|
||||
def test_inlined_call_function_ex(self):
|
||||
def inner(x):
|
||||
pass
|
||||
def func():
|
||||
inner(*[42])
|
||||
names = ["func", "inner"]
|
||||
self.do_test(func, names)
|
||||
|
||||
def test_inlined_for_iter(self):
|
||||
def gen():
|
||||
yield 42
|
||||
def func():
|
||||
for _ in gen():
|
||||
pass
|
||||
names = ["func", "gen", "gen", "gen"]
|
||||
self.do_test(func, names)
|
||||
|
||||
def test_inlined_load_attr(self):
|
||||
class C:
|
||||
@property
|
||||
def a(self):
|
||||
return 42
|
||||
class D:
|
||||
def __getattribute__(self, name):
|
||||
return 42
|
||||
def func():
|
||||
C().a
|
||||
D().a
|
||||
names = ["func", "a", "__getattribute__"]
|
||||
self.do_test(func, names)
|
||||
|
||||
def test_inlined_send(self):
|
||||
def inner():
|
||||
yield 42
|
||||
def outer():
|
||||
yield from inner()
|
||||
def func():
|
||||
list(outer())
|
||||
names = ["func", "outer", "outer", "inner", "inner", "outer", "inner"]
|
||||
self.do_test(func, names)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
@ -0,0 +1,2 @@
|
||||
Fix an issue where some :term:`bytecode` instructions could ignore
|
||||
:pep:`523` when "inlining" calls.
|
@ -494,6 +494,7 @@ dummy_func(
|
||||
}
|
||||
|
||||
inst(BINARY_SUBSCR_GETITEM, (unused/1, container, sub -- unused)) {
|
||||
DEOPT_IF(tstate->interp->eval_frame, BINARY_SUBSCR);
|
||||
PyTypeObject *tp = Py_TYPE(container);
|
||||
DEOPT_IF(!PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE), BINARY_SUBSCR);
|
||||
PyHeapTypeObject *ht = (PyHeapTypeObject *)tp;
|
||||
@ -830,8 +831,9 @@ dummy_func(
|
||||
DECREMENT_ADAPTIVE_COUNTER(cache->counter);
|
||||
#endif /* ENABLE_SPECIALIZATION */
|
||||
assert(frame != &entry_frame);
|
||||
if ((Py_TYPE(receiver) == &PyGen_Type ||
|
||||
Py_TYPE(receiver) == &PyCoro_Type) && ((PyGenObject *)receiver)->gi_frame_state < FRAME_EXECUTING)
|
||||
if ((tstate->interp->eval_frame == NULL) &&
|
||||
(Py_TYPE(receiver) == &PyGen_Type || Py_TYPE(receiver) == &PyCoro_Type) &&
|
||||
((PyGenObject *)receiver)->gi_frame_state < FRAME_EXECUTING)
|
||||
{
|
||||
PyGenObject *gen = (PyGenObject *)receiver;
|
||||
_PyInterpreterFrame *gen_frame = (_PyInterpreterFrame *)gen->gi_iframe;
|
||||
@ -867,6 +869,7 @@ dummy_func(
|
||||
}
|
||||
|
||||
inst(SEND_GEN, (unused/1, receiver, v -- receiver, unused)) {
|
||||
DEOPT_IF(tstate->interp->eval_frame, SEND);
|
||||
PyGenObject *gen = (PyGenObject *)receiver;
|
||||
DEOPT_IF(Py_TYPE(gen) != &PyGen_Type &&
|
||||
Py_TYPE(gen) != &PyCoro_Type, SEND);
|
||||
@ -2331,6 +2334,7 @@ dummy_func(
|
||||
}
|
||||
|
||||
inst(FOR_ITER_GEN, (unused/1, iter -- iter, unused)) {
|
||||
DEOPT_IF(tstate->interp->eval_frame, FOR_ITER);
|
||||
PyGenObject *gen = (PyGenObject *)iter;
|
||||
DEOPT_IF(Py_TYPE(gen) != &PyGen_Type, FOR_ITER);
|
||||
DEOPT_IF(gen->gi_frame_state >= FRAME_EXECUTING, FOR_ITER);
|
||||
|
@ -105,6 +105,7 @@
|
||||
|
||||
#define DISPATCH_INLINED(NEW_FRAME) \
|
||||
do { \
|
||||
assert(tstate->interp->eval_frame == NULL); \
|
||||
_PyFrame_SetStackPointer(frame, stack_pointer); \
|
||||
frame->prev_instr = next_instr - 1; \
|
||||
(NEW_FRAME)->previous = frame; \
|
||||
|
806
Python/generated_cases.c.h
generated
806
Python/generated_cases.c.h
generated
File diff suppressed because it is too large
Load Diff
@ -783,6 +783,10 @@ _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name)
|
||||
if (version == 0) {
|
||||
goto fail;
|
||||
}
|
||||
if (_PyInterpreterState_GET()->eval_frame) {
|
||||
SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OTHER);
|
||||
goto fail;
|
||||
}
|
||||
write_u32(lm_cache->keys_version, version);
|
||||
assert(type->tp_version_tag != 0);
|
||||
write_u32(lm_cache->type_version, type->tp_version_tag);
|
||||
@ -845,6 +849,10 @@ _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name)
|
||||
if (version == 0) {
|
||||
goto fail;
|
||||
}
|
||||
if (_PyInterpreterState_GET()->eval_frame) {
|
||||
SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OTHER);
|
||||
goto fail;
|
||||
}
|
||||
write_u32(lm_cache->keys_version, version);
|
||||
/* borrowed */
|
||||
write_obj(lm_cache->descr, descr);
|
||||
@ -1371,6 +1379,10 @@ _Py_Specialize_BinarySubscr(
|
||||
SPECIALIZATION_FAIL(BINARY_SUBSCR, SPEC_FAIL_OUT_OF_VERSIONS);
|
||||
goto fail;
|
||||
}
|
||||
if (_PyInterpreterState_GET()->eval_frame) {
|
||||
SPECIALIZATION_FAIL(BINARY_SUBSCR, SPEC_FAIL_OTHER);
|
||||
goto fail;
|
||||
}
|
||||
PyHeapTypeObject *ht = (PyHeapTypeObject *)container_type;
|
||||
// This pointer is invalidated by PyType_Modified (see the comment on
|
||||
// struct _specialization_cache):
|
||||
@ -2192,11 +2204,16 @@ _Py_Specialize_ForIter(PyObject *iter, _Py_CODEUNIT *instr, int oparg)
|
||||
assert(instr[oparg + INLINE_CACHE_ENTRIES_FOR_ITER + 1].op.code == END_FOR ||
|
||||
instr[oparg + INLINE_CACHE_ENTRIES_FOR_ITER + 1].op.code == INSTRUMENTED_END_FOR
|
||||
);
|
||||
if (_PyInterpreterState_GET()->eval_frame) {
|
||||
SPECIALIZATION_FAIL(FOR_ITER, SPEC_FAIL_OTHER);
|
||||
goto failure;
|
||||
}
|
||||
instr->op.code = FOR_ITER_GEN;
|
||||
goto success;
|
||||
}
|
||||
SPECIALIZATION_FAIL(FOR_ITER,
|
||||
_PySpecialization_ClassifyIterator(iter));
|
||||
failure:
|
||||
STAT_INC(FOR_ITER, failure);
|
||||
instr->op.code = FOR_ITER;
|
||||
cache->counter = adaptive_counter_backoff(cache->counter);
|
||||
@ -2214,11 +2231,16 @@ _Py_Specialize_Send(PyObject *receiver, _Py_CODEUNIT *instr)
|
||||
_PySendCache *cache = (_PySendCache *)(instr + 1);
|
||||
PyTypeObject *tp = Py_TYPE(receiver);
|
||||
if (tp == &PyGen_Type || tp == &PyCoro_Type) {
|
||||
if (_PyInterpreterState_GET()->eval_frame) {
|
||||
SPECIALIZATION_FAIL(SEND, SPEC_FAIL_OTHER);
|
||||
goto failure;
|
||||
}
|
||||
instr->op.code = SEND_GEN;
|
||||
goto success;
|
||||
}
|
||||
SPECIALIZATION_FAIL(SEND,
|
||||
_PySpecialization_ClassifyIterator(receiver));
|
||||
failure:
|
||||
STAT_INC(SEND, failure);
|
||||
instr->op.code = SEND;
|
||||
cache->counter = adaptive_counter_backoff(cache->counter);
|
||||
|
Loading…
x
Reference in New Issue
Block a user