]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-138431: JIT Optimizer --- Fix round-tripping references for str and tuple (GH...
authorKen Jin <kenjin@python.org>
Wed, 3 Sep 2025 18:05:06 +0000 (02:05 +0800)
committerGitHub <noreply@github.com>
Wed, 3 Sep 2025 18:05:06 +0000 (02:05 +0800)
Co-authored-by: Mark Shannon <9448417+markshannon@users.noreply.github.com>
Include/internal/pycore_optimizer.h
Lib/test/test_capi/test_opt.py
Misc/NEWS.d/next/Core_and_Builtins/2025-09-03-15-35-34.gh-issue-138431.EUsrtA.rst [new file with mode: 0644]
Python/optimizer_bytecodes.c
Python/optimizer_cases.c.h

index 3a5ae0a054ed3c4e295204af555e887105dbdf54..01c98c36f052a970a1cff36bc6e9815b689f69a3 100644 (file)
@@ -251,6 +251,12 @@ PyJitRef_Wrap(JitOptSymbol *sym)
     return (JitOptRef){.bits=(uintptr_t)sym};
 }
 
+static inline JitOptRef
+PyJitRef_StripReferenceInfo(JitOptRef ref)
+{
+    return PyJitRef_Wrap(PyJitRef_Unwrap(ref));
+}
+
 static inline JitOptRef
 PyJitRef_Borrow(JitOptRef ref)
 {
index c796b0dd4b5b7efcb59e79b66d79cfae5b5d8390..ffd65dbb1464f8ea52f2d46c6dfda7c6f0addf68 100644 (file)
@@ -2501,6 +2501,22 @@ class TestUopsOptimization(unittest.TestCase):
         # For now... until we constant propagate it away.
         self.assertIn("_BINARY_OP", uops)
 
+    def test_reference_tracking_across_call_doesnt_crash(self):
+
+        def f1():
+            for _ in range(TIER2_THRESHOLD + 1):
+                # Choose a value that won't occur elsewhere to avoid sharing
+                str("value that won't occur elsewhere to avoid sharing")
+
+        f1()
+
+        def f2():
+            for _ in range(TIER2_THRESHOLD + 1):
+                # Choose a value that won't occur elsewhere to avoid sharing
+                tuple((31, -17, 25, "won't occur elsewhere"))
+
+        f2()
+
 
 def global_identity(x):
     return x
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-09-03-15-35-34.gh-issue-138431.EUsrtA.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-09-03-15-35-34.gh-issue-138431.EUsrtA.rst
new file mode 100644 (file)
index 0000000..11fc3c0
--- /dev/null
@@ -0,0 +1 @@
+Fix a bug in the JIT optimizer when round-tripping strings and tuples.
index 781296dc7f48fcd0459ce0dcf4697d4e3eb14eb2..eccbddf0546ab3428405756c44a2c4c9c7b5b9d0 100644 (file)
@@ -762,9 +762,8 @@ dummy_func(void) {
     }
 
     op(_RETURN_VALUE, (retval -- res)) {
-        // We wrap and unwrap the value to mimic PyStackRef_MakeHeapSafe
-        // in bytecodes.c
-        JitOptRef temp = PyJitRef_Wrap(PyJitRef_Unwrap(retval));
+        // Mimics PyStackRef_MakeHeapSafe in the interpreter.
+        JitOptRef temp = PyJitRef_StripReferenceInfo(retval);
         DEAD(retval);
         SAVE_STACK();
         ctx->frame->stack_pointer = stack_pointer;
@@ -925,7 +924,9 @@ dummy_func(void) {
     op(_CALL_STR_1, (unused, unused, arg -- res)) {
         if (sym_matches_type(arg, &PyUnicode_Type)) {
             // e.g. str('foo') or str(foo) where foo is known to be a string
-            res = arg;
+            // Note: we must strip the reference information because it goes
+            // through str() which strips the reference information from it.
+            res = PyJitRef_StripReferenceInfo(arg);
         }
         else {
             res = sym_new_type(ctx, &PyUnicode_Type);
@@ -1065,7 +1066,9 @@ dummy_func(void) {
     op(_CALL_TUPLE_1, (callable, null, arg -- res)) {
         if (sym_matches_type(arg, &PyTuple_Type)) {
             // e.g. tuple((1, 2)) or tuple(foo) where foo is known to be a tuple
-            res = arg;
+            // Note: we must strip the reference information because it goes
+            // through tuple() which strips the reference information from it.
+            res = PyJitRef_StripReferenceInfo(arg);
         }
         else {
             res = sym_new_type(ctx, &PyTuple_Type);
index 14e985b42ea0ce60eb9db467129c2883acd82465..8617355e25f4185a3c1caf9e7d44163640bae4a3 100644 (file)
             JitOptRef retval;
             JitOptRef res;
             retval = stack_pointer[-1];
-            JitOptRef temp = PyJitRef_Wrap(PyJitRef_Unwrap(retval));
+            JitOptRef temp = PyJitRef_StripReferenceInfo(retval);
             stack_pointer += -1;
             assert(WITHIN_STACK_BOUNDS());
             ctx->frame->stack_pointer = stack_pointer;
             JitOptRef res;
             arg = stack_pointer[-1];
             if (sym_matches_type(arg, &PyUnicode_Type)) {
-                res = arg;
+                res = PyJitRef_StripReferenceInfo(arg);
             }
             else {
                 res = sym_new_type(ctx, &PyUnicode_Type);
             JitOptRef res;
             arg = stack_pointer[-1];
             if (sym_matches_type(arg, &PyTuple_Type)) {
-                res = arg;
+                res = PyJitRef_StripReferenceInfo(arg);
             }
             else {
                 res = sym_new_type(ctx, &PyTuple_Type);