Skip to content

Commit cec5d85

Browse files
authored
gh-91625: Don't ignore extended args of adaptive opcodes (GH-91626)
1 parent 7659681 commit cec5d85

File tree

5 files changed

+66
-18
lines changed

5 files changed

+66
-18
lines changed

Lib/test/test_descr.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1961,6 +1961,20 @@ def __delitem__(self, key):
19611961
del a[0:10]
19621962
self.assertEqual(a.delitem, (slice(0, 10)))
19631963

1964+
def test_load_attr_extended_arg(self):
1965+
# https://github.com/python/cpython/issues/91625
1966+
class Numbers:
1967+
def __getattr__(self, attr):
1968+
return int(attr.lstrip("_"))
1969+
attrs = ", ".join(f"Z._{n:03d}" for n in range(280))
1970+
code = f"def number_attrs(Z):\n return [ {attrs} ]"
1971+
ns = {}
1972+
exec(code, ns)
1973+
number_attrs = ns["number_attrs"]
1974+
# Warm up the the function for quickening (PEP 659)
1975+
for _ in range(30):
1976+
self.assertEqual(number_attrs(Numbers()), list(range(280)))
1977+
19641978
def test_methods(self):
19651979
# Testing methods...
19661980
class C(object):
@@ -4435,8 +4449,8 @@ def __getattr__(self, attr):
44354449
raise RuntimeError(f"Premature access to sys.stdout.{attr}")
44364450

44374451
with redirect_stdout(StdoutGuard()):
4438-
with self.assertRaises(RuntimeError):
4439-
print("Oops!")
4452+
with self.assertRaises(RuntimeError):
4453+
print("Oops!")
44404454

44414455
def test_vicious_descriptor_nonsense(self):
44424456
# Testing vicious_descriptor_nonsense...

Lib/test/test_dynamic.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,18 @@ def test_eval_gives_lambda_custom_globals(self):
133133

134134
self.assertEqual(foo(), 7)
135135

136+
def test_load_global_specialization_failure_keeps_oparg(self):
137+
# https://github.com/python/cpython/issues/91625
138+
class MyGlobals(dict):
139+
def __missing__(self, key):
140+
return int(key.removeprefix("_number_"))
141+
142+
code = "lambda: " + "+".join(f"_number_{i}" for i in range(1000))
143+
sum_1000 = eval(code, MyGlobals())
144+
expected = sum(range(1000))
145+
# Warm up the the function for quickening (PEP 659)
146+
for _ in range(30):
147+
self.assertEqual(sum_1000(), expected)
136148

137149
if __name__ == "__main__":
138150
unittest.main()

Lib/test/test_unpack.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,5 +151,21 @@ def load_tests(loader, tests, pattern):
151151
return tests
152152

153153

154+
class TestCornerCases(unittest.TestCase):
155+
def test_extended_oparg_not_ignored(self):
156+
# https://github.com/python/cpython/issues/91625
157+
target = "(" + "y,"*400 + ")"
158+
code = f"""def unpack_400(x):
159+
{target} = x
160+
return y
161+
"""
162+
ns = {}
163+
exec(code, ns)
164+
unpack_400 = ns["unpack_400"]
165+
# Warm up the the function for quickening (PEP 659)
166+
for _ in range(30):
167+
y = unpack_400(range(400))
168+
self.assertEqual(y, 399)
169+
154170
if __name__ == "__main__":
155171
unittest.main()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed a bug in which adaptive opcodes ignored any preceding ``EXTENDED_ARG``\ s on specialization failure.

Python/ceval.c

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1351,6 +1351,13 @@ eval_frame_handle_pending(PyThreadState *tstate)
13511351
DISPATCH_GOTO(); \
13521352
}
13531353

1354+
#define NOTRACE_DISPATCH_SAME_OPARG() \
1355+
{ \
1356+
opcode = _Py_OPCODE(*next_instr); \
1357+
PRE_DISPATCH_GOTO(); \
1358+
DISPATCH_GOTO(); \
1359+
}
1360+
13541361
#define CHECK_EVAL_BREAKER() \
13551362
_Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); \
13561363
if (_Py_atomic_load_relaxed(eval_breaker)) { \
@@ -2158,7 +2165,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
21582165
if (_Py_Specialize_BinarySubscr(container, sub, next_instr) < 0) {
21592166
goto error;
21602167
}
2161-
DISPATCH();
2168+
NOTRACE_DISPATCH_SAME_OPARG();
21622169
}
21632170
else {
21642171
STAT_INC(BINARY_SUBSCR, deferred);
@@ -2323,7 +2330,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
23232330
if (_Py_Specialize_StoreSubscr(container, sub, next_instr) < 0) {
23242331
goto error;
23252332
}
2326-
DISPATCH();
2333+
NOTRACE_DISPATCH_SAME_OPARG();
23272334
}
23282335
else {
23292336
STAT_INC(STORE_SUBSCR, deferred);
@@ -2813,7 +2820,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
28132820
PyObject *seq = TOP();
28142821
next_instr--;
28152822
_Py_Specialize_UnpackSequence(seq, next_instr, oparg);
2816-
DISPATCH();
2823+
NOTRACE_DISPATCH_SAME_OPARG();
28172824
}
28182825
else {
28192826
STAT_INC(UNPACK_SEQUENCE, deferred);
@@ -3056,7 +3063,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
30563063
if (_Py_Specialize_LoadGlobal(GLOBALS(), BUILTINS(), next_instr, name) < 0) {
30573064
goto error;
30583065
}
3059-
DISPATCH();
3066+
NOTRACE_DISPATCH_SAME_OPARG();
30603067
}
30613068
else {
30623069
STAT_INC(LOAD_GLOBAL, deferred);
@@ -3481,7 +3488,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
34813488
if (_Py_Specialize_LoadAttr(owner, next_instr, name) < 0) {
34823489
goto error;
34833490
}
3484-
DISPATCH();
3491+
NOTRACE_DISPATCH_SAME_OPARG();
34853492
}
34863493
else {
34873494
STAT_INC(LOAD_ATTR, deferred);
@@ -3590,7 +3597,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
35903597
if (_Py_Specialize_StoreAttr(owner, next_instr, name) < 0) {
35913598
goto error;
35923599
}
3593-
DISPATCH();
3600+
NOTRACE_DISPATCH_SAME_OPARG();
35943601
}
35953602
else {
35963603
STAT_INC(STORE_ATTR, deferred);
@@ -3718,7 +3725,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
37183725
PyObject *left = SECOND();
37193726
next_instr--;
37203727
_Py_Specialize_CompareOp(left, right, next_instr, oparg);
3721-
DISPATCH();
3728+
NOTRACE_DISPATCH_SAME_OPARG();
37223729
}
37233730
else {
37243731
STAT_INC(COMPARE_OP, deferred);
@@ -4523,7 +4530,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
45234530
if (_Py_Specialize_LoadMethod(owner, next_instr, name) < 0) {
45244531
goto error;
45254532
}
4526-
DISPATCH();
4533+
NOTRACE_DISPATCH_SAME_OPARG();
45274534
}
45284535
else {
45294536
STAT_INC(LOAD_METHOD, deferred);
@@ -4797,7 +4804,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
47974804
if (err < 0) {
47984805
goto error;
47994806
}
4800-
DISPATCH();
4807+
NOTRACE_DISPATCH_SAME_OPARG();
48014808
}
48024809
else {
48034810
STAT_INC(PRECALL, deferred);
@@ -4818,7 +4825,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
48184825
if (err < 0) {
48194826
goto error;
48204827
}
4821-
DISPATCH();
4828+
NOTRACE_DISPATCH_SAME_OPARG();
48224829
}
48234830
else {
48244831
STAT_INC(CALL, deferred);
@@ -5545,7 +5552,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
55455552
PyObject *rhs = TOP();
55465553
next_instr--;
55475554
_Py_Specialize_BinaryOp(lhs, rhs, next_instr, oparg, &GETLOCAL(0));
5548-
DISPATCH();
5555+
NOTRACE_DISPATCH_SAME_OPARG();
55495556
}
55505557
else {
55515558
STAT_INC(BINARY_OP, deferred);
@@ -5564,11 +5571,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
55645571

55655572
TARGET(EXTENDED_ARG) {
55665573
assert(oparg);
5567-
int oldoparg = oparg;
5568-
NEXTOPARG();
5569-
oparg |= oldoparg << 8;
5570-
PRE_DISPATCH_GOTO();
5571-
DISPATCH_GOTO();
5574+
oparg <<= 8;
5575+
oparg |= _Py_OPARG(*next_instr);
5576+
NOTRACE_DISPATCH_SAME_OPARG();
55725577
}
55735578

55745579
TARGET(CACHE) {

0 commit comments

Comments
 (0)