]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-152192: Fix JUMP_BACKWARD passing a truncated oparg to the jit tracer (GH-152382)
authorTimofei <128279579+deadlovelll@users.noreply.github.com>
Tue, 30 Jun 2026 09:46:59 +0000 (12:46 +0300)
committerGitHub <noreply@github.com>
Tue, 30 Jun 2026 09:46:59 +0000 (10:46 +0100)
Lib/test/test_capi/test_opt.py
Misc/NEWS.d/next/Core_and_Builtins/2026-06-26-22-38-21.gh-issue-152192.lX2jIM.rst [new file with mode: 0644]
Modules/_testinternalcapi/test_cases.c.h
Python/bytecodes.c
Python/generated_cases.c.h

index 2248920c266aef58ae56042c329d447f37b6a53e..25b2c393e6773de499b69947787ca61daae9bf3f 100644 (file)
@@ -1,4 +1,5 @@
 import contextlib
+import dis
 import itertools
 import sys
 import textwrap
@@ -247,6 +248,28 @@ class TestUops(unittest.TestCase):
         self.assertTrue(any((opcode, oparg, operand) == ("_LOAD_FAST_BORROW", 259, 0)
                             for opcode, oparg, _, operand in list(ex)))
 
+    def test_jump_backward_extended_arg(self):
+        # gh-152192: a JUMP_BACKWARD that needs an EXTENDED_ARG must record its
+        # deopt target at the EXTENDED_ARG, not the JUMP_BACKWARD.
+        ns = {}
+        src = ("def f(n):\n"
+               "    i = 0\n"
+               "    while i < n:\n"
+               "        i += 1\n"
+               + "".join(f"        a = {j}\n" for j in range(140)))
+        exec(src, ns)
+        f = ns["f"]
+
+        instrs = list(dis.get_instructions(f))
+        ext, jb = next((p, i) for p, i in zip(instrs, instrs[1:])
+                       if i.opname == "JUMP_BACKWARD" and p.opname == "EXTENDED_ARG")
+
+        f(TIER2_THRESHOLD + 1)
+        ex = _opcode.get_executor(f.__code__, ext.offset)
+        set_ips = {t for op, _, t, _ in ex if op == "_SET_IP"}
+        self.assertIn(ext.offset // 2, set_ips)
+        self.assertNotIn(jb.offset // 2, set_ips)
+
     def test_unspecialized_unpack(self):
         # An example of an unspecialized opcode
         def testfunc(x):
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-26-22-38-21.gh-issue-152192.lX2jIM.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-26-22-38-21.gh-issue-152192.lX2jIM.rst
new file mode 100644 (file)
index 0000000..153cbeb
--- /dev/null
@@ -0,0 +1,2 @@
+Fix a truncated ``oparg`` being passed to JIT trace initialization for a
+``JUMP_BACKWARD`` with an ``EXTENDED_ARG``.
index fdc077c9549a1449af8a9a2395de006dc291297d..51234e8749de261c7094809d1e6fc955ce1e5a89 100644 (file)
                         (this_instr->op.code == JUMP_BACKWARD_JIT || is_resume)) &&
                     next_instr->op.code != ENTER_EXECUTOR) {
                     _Py_CODEUNIT *insert_exec_at = this_instr;
-                    while (oparg > 255) {
-                        oparg >>= 8;
+                    for (int tmp = oparg; tmp > 255; tmp >>= 8) {
                         insert_exec_at--;
                     }
                     int succ = _PyJit_TryInitializeTracing(tstate, frame, this_instr, insert_exec_at,
                         (this_instr->op.code == JUMP_BACKWARD_JIT || is_resume)) &&
                     next_instr->op.code != ENTER_EXECUTOR) {
                     _Py_CODEUNIT *insert_exec_at = this_instr;
-                    while (oparg > 255) {
-                        oparg >>= 8;
+                    for (int tmp = oparg; tmp > 255; tmp >>= 8) {
                         insert_exec_at--;
                     }
                     int succ = _PyJit_TryInitializeTracing(tstate, frame, this_instr, insert_exec_at,
index 31596e0bc7a31d2d0bcce79471db355aba134a57..7fb48516e2eae73f923225408f4b39c1094ad2a7 100644 (file)
@@ -3560,8 +3560,8 @@ dummy_func(
                 next_instr->op.code != ENTER_EXECUTOR) {
                 /* Back up over EXTENDED_ARGs so executor is inserted at the correct place */
                 _Py_CODEUNIT *insert_exec_at = this_instr;
-                while (oparg > 255) {
-                    oparg >>= 8;
+                // gh-152192: count with a temporary. oparg must stay intact, it's passed to the tracer below
+                for (int tmp = oparg; tmp > 255; tmp >>= 8) {
                     insert_exec_at--;
                 }
                 int succ = _PyJit_TryInitializeTracing(tstate, frame, this_instr, insert_exec_at,
index 0da86abed67f63b9ba1857efe71f7abddb4a5741..4b472aecc5fc3aa22d3a29a5662ddb73bf84294f 100644 (file)
                         (this_instr->op.code == JUMP_BACKWARD_JIT || is_resume)) &&
                     next_instr->op.code != ENTER_EXECUTOR) {
                     _Py_CODEUNIT *insert_exec_at = this_instr;
-                    while (oparg > 255) {
-                        oparg >>= 8;
+                    for (int tmp = oparg; tmp > 255; tmp >>= 8) {
                         insert_exec_at--;
                     }
                     int succ = _PyJit_TryInitializeTracing(tstate, frame, this_instr, insert_exec_at,
                         (this_instr->op.code == JUMP_BACKWARD_JIT || is_resume)) &&
                     next_instr->op.code != ENTER_EXECUTOR) {
                     _Py_CODEUNIT *insert_exec_at = this_instr;
-                    while (oparg > 255) {
-                        oparg >>= 8;
+                    for (int tmp = oparg; tmp > 255; tmp >>= 8) {
                         insert_exec_at--;
                     }
                     int succ = _PyJit_TryInitializeTracing(tstate, frame, this_instr, insert_exec_at,