GH-104405: Add missing PEP 523 checks (GH-104406)

This commit is contained in:
Brandt Bucher 2023-05-12 15:23:13 -07:00 committed by GitHub
parent a10b026f0f
commit 1eb950ca55
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 505 additions and 420 deletions

View file

@ -1748,28 +1748,80 @@ SUFFICIENT_TO_DEOPT_AND_SPECIALIZE = 100
class Test_Pep523API(unittest.TestCase): class Test_Pep523API(unittest.TestCase):
def do_test(self, func): def do_test(self, func, names):
calls = [] actual_calls = []
start = SUFFICIENT_TO_DEOPT_AND_SPECIALIZE start = SUFFICIENT_TO_DEOPT_AND_SPECIALIZE
count = start + SUFFICIENT_TO_DEOPT_AND_SPECIALIZE count = start + SUFFICIENT_TO_DEOPT_AND_SPECIALIZE
for i in range(count): try:
if i == start: for i in range(count):
_testinternalcapi.set_eval_frame_record(calls) if i == start:
func() _testinternalcapi.set_eval_frame_record(actual_calls)
_testinternalcapi.set_eval_frame_default() func()
self.assertEqual(len(calls), SUFFICIENT_TO_DEOPT_AND_SPECIALIZE) finally:
for name in calls: _testinternalcapi.set_eval_frame_default()
self.assertEqual(name, func.__name__) 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 test_inlined_binary_subscr(self):
def func1(): class C:
pass def __getitem__(self, other):
self.do_test(func1) return None
def func():
C()[42]
names = ["func", "__getitem__"]
self.do_test(func, names)
def test_pep523_with_specialization_with_default(self): def test_inlined_call(self):
def func2(x=None): def inner(x=42):
pass 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__": if __name__ == "__main__":

View file

@ -0,0 +1,2 @@
Fix an issue where some :term:`bytecode` instructions could ignore
:pep:`523` when "inlining" calls.

View file

@ -494,6 +494,7 @@ dummy_func(
} }
inst(BINARY_SUBSCR_GETITEM, (unused/1, container, sub -- unused)) { inst(BINARY_SUBSCR_GETITEM, (unused/1, container, sub -- unused)) {
DEOPT_IF(tstate->interp->eval_frame, BINARY_SUBSCR);
PyTypeObject *tp = Py_TYPE(container); PyTypeObject *tp = Py_TYPE(container);
DEOPT_IF(!PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE), BINARY_SUBSCR); DEOPT_IF(!PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE), BINARY_SUBSCR);
PyHeapTypeObject *ht = (PyHeapTypeObject *)tp; PyHeapTypeObject *ht = (PyHeapTypeObject *)tp;
@ -830,8 +831,9 @@ dummy_func(
DECREMENT_ADAPTIVE_COUNTER(cache->counter); DECREMENT_ADAPTIVE_COUNTER(cache->counter);
#endif /* ENABLE_SPECIALIZATION */ #endif /* ENABLE_SPECIALIZATION */
assert(frame != &entry_frame); assert(frame != &entry_frame);
if ((Py_TYPE(receiver) == &PyGen_Type || if ((tstate->interp->eval_frame == NULL) &&
Py_TYPE(receiver) == &PyCoro_Type) && ((PyGenObject *)receiver)->gi_frame_state < FRAME_EXECUTING) (Py_TYPE(receiver) == &PyGen_Type || Py_TYPE(receiver) == &PyCoro_Type) &&
((PyGenObject *)receiver)->gi_frame_state < FRAME_EXECUTING)
{ {
PyGenObject *gen = (PyGenObject *)receiver; PyGenObject *gen = (PyGenObject *)receiver;
_PyInterpreterFrame *gen_frame = (_PyInterpreterFrame *)gen->gi_iframe; _PyInterpreterFrame *gen_frame = (_PyInterpreterFrame *)gen->gi_iframe;
@ -867,6 +869,7 @@ dummy_func(
} }
inst(SEND_GEN, (unused/1, receiver, v -- receiver, unused)) { inst(SEND_GEN, (unused/1, receiver, v -- receiver, unused)) {
DEOPT_IF(tstate->interp->eval_frame, SEND);
PyGenObject *gen = (PyGenObject *)receiver; PyGenObject *gen = (PyGenObject *)receiver;
DEOPT_IF(Py_TYPE(gen) != &PyGen_Type && DEOPT_IF(Py_TYPE(gen) != &PyGen_Type &&
Py_TYPE(gen) != &PyCoro_Type, SEND); Py_TYPE(gen) != &PyCoro_Type, SEND);
@ -2331,6 +2334,7 @@ dummy_func(
} }
inst(FOR_ITER_GEN, (unused/1, iter -- iter, unused)) { inst(FOR_ITER_GEN, (unused/1, iter -- iter, unused)) {
DEOPT_IF(tstate->interp->eval_frame, FOR_ITER);
PyGenObject *gen = (PyGenObject *)iter; PyGenObject *gen = (PyGenObject *)iter;
DEOPT_IF(Py_TYPE(gen) != &PyGen_Type, FOR_ITER); DEOPT_IF(Py_TYPE(gen) != &PyGen_Type, FOR_ITER);
DEOPT_IF(gen->gi_frame_state >= FRAME_EXECUTING, FOR_ITER); DEOPT_IF(gen->gi_frame_state >= FRAME_EXECUTING, FOR_ITER);

View file

@ -105,6 +105,7 @@
#define DISPATCH_INLINED(NEW_FRAME) \ #define DISPATCH_INLINED(NEW_FRAME) \
do { \ do { \
assert(tstate->interp->eval_frame == NULL); \
_PyFrame_SetStackPointer(frame, stack_pointer); \ _PyFrame_SetStackPointer(frame, stack_pointer); \
frame->prev_instr = next_instr - 1; \ frame->prev_instr = next_instr - 1; \
(NEW_FRAME)->previous = frame; \ (NEW_FRAME)->previous = frame; \

File diff suppressed because it is too large Load diff

View file

@ -783,6 +783,10 @@ _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name)
if (version == 0) { if (version == 0) {
goto fail; goto fail;
} }
if (_PyInterpreterState_GET()->eval_frame) {
SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OTHER);
goto fail;
}
write_u32(lm_cache->keys_version, version); write_u32(lm_cache->keys_version, version);
assert(type->tp_version_tag != 0); assert(type->tp_version_tag != 0);
write_u32(lm_cache->type_version, type->tp_version_tag); 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) { if (version == 0) {
goto fail; goto fail;
} }
if (_PyInterpreterState_GET()->eval_frame) {
SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OTHER);
goto fail;
}
write_u32(lm_cache->keys_version, version); write_u32(lm_cache->keys_version, version);
/* borrowed */ /* borrowed */
write_obj(lm_cache->descr, descr); write_obj(lm_cache->descr, descr);
@ -1371,6 +1379,10 @@ _Py_Specialize_BinarySubscr(
SPECIALIZATION_FAIL(BINARY_SUBSCR, SPEC_FAIL_OUT_OF_VERSIONS); SPECIALIZATION_FAIL(BINARY_SUBSCR, SPEC_FAIL_OUT_OF_VERSIONS);
goto fail; goto fail;
} }
if (_PyInterpreterState_GET()->eval_frame) {
SPECIALIZATION_FAIL(BINARY_SUBSCR, SPEC_FAIL_OTHER);
goto fail;
}
PyHeapTypeObject *ht = (PyHeapTypeObject *)container_type; PyHeapTypeObject *ht = (PyHeapTypeObject *)container_type;
// This pointer is invalidated by PyType_Modified (see the comment on // This pointer is invalidated by PyType_Modified (see the comment on
// struct _specialization_cache): // 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 || 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 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; instr->op.code = FOR_ITER_GEN;
goto success; goto success;
} }
SPECIALIZATION_FAIL(FOR_ITER, SPECIALIZATION_FAIL(FOR_ITER,
_PySpecialization_ClassifyIterator(iter)); _PySpecialization_ClassifyIterator(iter));
failure:
STAT_INC(FOR_ITER, failure); STAT_INC(FOR_ITER, failure);
instr->op.code = FOR_ITER; instr->op.code = FOR_ITER;
cache->counter = adaptive_counter_backoff(cache->counter); cache->counter = adaptive_counter_backoff(cache->counter);
@ -2214,11 +2231,16 @@ _Py_Specialize_Send(PyObject *receiver, _Py_CODEUNIT *instr)
_PySendCache *cache = (_PySendCache *)(instr + 1); _PySendCache *cache = (_PySendCache *)(instr + 1);
PyTypeObject *tp = Py_TYPE(receiver); PyTypeObject *tp = Py_TYPE(receiver);
if (tp == &PyGen_Type || tp == &PyCoro_Type) { 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; instr->op.code = SEND_GEN;
goto success; goto success;
} }
SPECIALIZATION_FAIL(SEND, SPECIALIZATION_FAIL(SEND,
_PySpecialization_ClassifyIterator(receiver)); _PySpecialization_ClassifyIterator(receiver));
failure:
STAT_INC(SEND, failure); STAT_INC(SEND, failure);
instr->op.code = SEND; instr->op.code = SEND;
cache->counter = adaptive_counter_backoff(cache->counter); cache->counter = adaptive_counter_backoff(cache->counter);