https://github.com/python/cpython/commit/acfb4528de0836b6e3845dac76d6d6b1222d5530
commit: acfb4528de0836b6e3845dac76d6d6b1222d5530
branch: main
author: Ken Jin <[email protected]>
committer: markshannon <[email protected]>
date: 2026-03-20T12:26:41Z
summary:

gh-146099: Optimize _GUARD_CODE_VERSION+IP via function version symbols 
(GH-146101)

files:
M Include/internal/pycore_optimizer.h
M Include/internal/pycore_optimizer_types.h
M Lib/test/test_capi/test_opt.py
M Python/optimizer_analysis.c
M Python/optimizer_bytecodes.c
M Python/optimizer_cases.c.h
M Python/optimizer_symbols.c

diff --git a/Include/internal/pycore_optimizer.h 
b/Include/internal/pycore_optimizer.h
index 6b3c39d64c9c32..94c79edc855bbf 100644
--- a/Include/internal/pycore_optimizer.h
+++ b/Include/internal/pycore_optimizer.h
@@ -389,6 +389,8 @@ extern PyCodeObject 
*_Py_uop_sym_get_probable_func_code(JitOptRef sym);
 extern PyObject *_Py_uop_sym_get_probable_value(JitOptRef sym);
 extern PyTypeObject *_Py_uop_sym_get_probable_type(JitOptRef sym);
 extern JitOptRef *_Py_uop_sym_set_stack_depth(JitOptContext *ctx, int 
stack_depth, JitOptRef *current_sp);
+extern uint32_t _Py_uop_sym_get_func_version(JitOptRef ref);
+bool _Py_uop_sym_set_func_version(JitOptContext *ctx, JitOptRef ref, uint32_t 
version);
 
 extern void _Py_uop_abstractcontext_init(JitOptContext *ctx, _PyBloomFilter 
*dependencies);
 extern void _Py_uop_abstractcontext_fini(JitOptContext *ctx);
diff --git a/Include/internal/pycore_optimizer_types.h 
b/Include/internal/pycore_optimizer_types.h
index 2958db5b787975..fe45775201b098 100644
--- a/Include/internal/pycore_optimizer_types.h
+++ b/Include/internal/pycore_optimizer_types.h
@@ -36,15 +36,16 @@ typedef enum _JitSymType {
     JIT_SYM_NON_NULL_TAG = 3,
     JIT_SYM_BOTTOM_TAG = 4,
     JIT_SYM_TYPE_VERSION_TAG = 5,
-    JIT_SYM_KNOWN_CLASS_TAG = 6,
-    JIT_SYM_KNOWN_VALUE_TAG = 7,
-    JIT_SYM_TUPLE_TAG = 8,
-    JIT_SYM_TRUTHINESS_TAG = 9,
-    JIT_SYM_COMPACT_INT = 10,
-    JIT_SYM_PREDICATE_TAG = 11,
-    JIT_SYM_RECORDED_VALUE_TAG = 12,
-    JIT_SYM_RECORDED_TYPE_TAG = 13,
-    JIT_SYM_RECORDED_GEN_FUNC_TAG = 14,
+    JIT_SYM_FUNC_VERSION_TAG = 6,
+    JIT_SYM_KNOWN_CLASS_TAG = 7,
+    JIT_SYM_KNOWN_VALUE_TAG = 8,
+    JIT_SYM_TUPLE_TAG = 9,
+    JIT_SYM_TRUTHINESS_TAG = 10,
+    JIT_SYM_COMPACT_INT = 11,
+    JIT_SYM_PREDICATE_TAG = 12,
+    JIT_SYM_RECORDED_VALUE_TAG = 13,
+    JIT_SYM_RECORDED_TYPE_TAG = 14,
+    JIT_SYM_RECORDED_GEN_FUNC_TAG = 15,
 } JitSymType;
 
 typedef struct _jit_opt_known_class {
@@ -58,6 +59,11 @@ typedef struct _jit_opt_known_version {
     uint32_t version;
 } JitOptKnownVersion;
 
+typedef struct _jit_opt_known_func_version {
+    uint8_t tag;
+    uint32_t func_version;
+} JitOptKnownFuncVersion;
+
 typedef struct _jit_opt_known_value {
     uint8_t tag;
     PyObject *value;
@@ -118,6 +124,7 @@ typedef union _jit_opt_symbol {
     JitOptKnownClass cls;
     JitOptKnownValue value;
     JitOptKnownVersion version;
+    JitOptKnownFuncVersion func_version;
     JitOptTuple tuple;
     JitOptTruthiness truthiness;
     JitOptCompactInt compact;
@@ -140,6 +147,7 @@ typedef struct _Py_UOpsAbstractFrame {
     int stack_len;
     int locals_len;
     bool caller; // We have made a call from this frame during the trace
+    JitOptRef callable;
     PyFunctionObject *func;
     PyCodeObject *code;
 
diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py
index df127b55be4068..bd9bc8a1a533c3 100644
--- a/Lib/test/test_capi/test_opt.py
+++ b/Lib/test/test_capi/test_opt.py
@@ -630,6 +630,8 @@ def dummy(x):
         self.assertIn("_PUSH_FRAME", uops)
         self.assertIn("_BINARY_OP_ADD_INT", uops)
         self.assertNotIn("_CHECK_PEP_523", uops)
+        self.assertNotIn("_GUARD_CODE_VERSION__PUSH_FRAME", uops)
+        self.assertNotIn("_GUARD_IP__PUSH_FRAME", uops)
 
     def test_int_type_propagate_through_range(self):
         def testfunc(n):
@@ -1540,8 +1542,9 @@ def testfunc(n):
         self.assertIsNotNone(ex)
         uops = get_opnames(ex)
         self.assertIn("_PUSH_FRAME", uops)
-        # Strength reduced version
-        self.assertIn("_CHECK_FUNCTION_VERSION_INLINE", uops)
+        # Both should be not present, as this is a call
+        # to a simple function with a known function version.
+        self.assertNotIn("_CHECK_FUNCTION_VERSION_INLINE", uops)
         self.assertNotIn("_CHECK_FUNCTION_VERSION", uops)
         # Removed guard
         self.assertNotIn("_CHECK_FUNCTION_EXACT_ARGS", uops)
diff --git a/Python/optimizer_analysis.c b/Python/optimizer_analysis.c
index a5ed5953ec082c..520420e9878575 100644
--- a/Python/optimizer_analysis.c
+++ b/Python/optimizer_analysis.c
@@ -291,6 +291,8 @@ add_op(JitOptContext *ctx, _PyUOpInstruction *this_instr,
 #define sym_get_probable_func_code _Py_uop_sym_get_probable_func_code
 #define sym_get_probable_value _Py_uop_sym_get_probable_value
 #define sym_set_stack_depth(DEPTH, SP) _Py_uop_sym_set_stack_depth(ctx, DEPTH, 
SP)
+#define sym_get_func_version _Py_uop_sym_get_func_version
+#define sym_set_func_version _Py_uop_sym_set_func_version
 
 /* Comparison oparg masks */
 #define COMPARE_LT_MASK 2
diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c
index fff05f06f647c2..2364bf75d104de 100644
--- a/Python/optimizer_bytecodes.c
+++ b/Python/optimizer_bytecodes.c
@@ -864,12 +864,12 @@ dummy_func(void) {
     }
 
     op(_CHECK_FUNCTION_VERSION, (func_version/2, callable, self_or_null, 
unused[oparg] -- callable, self_or_null, unused[oparg])) {
-        if (sym_is_const(ctx, callable) && sym_matches_type(callable, 
&PyFunction_Type)) {
-            assert(PyFunction_Check(sym_get_const(ctx, callable)));
-            ADD_OP(_CHECK_FUNCTION_VERSION_INLINE, 0, func_version);
-            uop_buffer_last(&ctx->out_buffer)->operand1 = 
(uintptr_t)sym_get_const(ctx, callable);
+        if (sym_get_func_version(callable) == func_version) {
+            REPLACE_OP(this_instr, _NOP, 0, 0);
+        }
+        else {
+            sym_set_func_version(ctx, callable, func_version);
         }
-        sym_set_type(callable, &PyFunction_Type);
     }
 
     op(_CHECK_METHOD_VERSION, (func_version/2, callable, null, unused[oparg] 
-- callable, null, unused[oparg])) {
@@ -1779,12 +1779,47 @@ dummy_func(void) {
         sym_set_recorded_gen_func(nos, func);
     }
 
+    op(_GUARD_CODE_VERSION__PUSH_FRAME, (version/2 -- )) {
+        PyCodeObject *co = get_current_code_object(ctx);
+        if (co->co_version == version) {
+            _Py_BloomFilter_Add(dependencies, co);
+            // Functions derive their version from code objects.
+            if (sym_get_func_version(ctx->frame->callable) == version) {
+                REPLACE_OP(this_instr, _NOP, 0, 0);
+            }
+        }
+        else {
+            ctx->done = true;
+        }
+    }
+
+    op(_GUARD_CODE_VERSION_RETURN_VALUE, (version/2 -- )) {
+        if (ctx->frame->caller) {
+            REPLACE_OP(this_instr, _NOP, 0, 0);
+        }
+    }
+
+    op(_GUARD_CODE_VERSION_YIELD_VALUE, (version/2 -- )) {
+        if (ctx->frame->caller) {
+            REPLACE_OP(this_instr, _NOP, 0, 0);
+        }
+    }
+
+    op(_GUARD_CODE_VERSION_RETURN_GENERATOR, (version/2 -- )) {
+        if (ctx->frame->caller) {
+            REPLACE_OP(this_instr, _NOP, 0, 0);
+        }
+    }
+
     op(_GUARD_IP__PUSH_FRAME, (ip/4 --)) {
         (void)ip;
         stack_pointer = sym_set_stack_depth((int)this_instr->operand1, 
stack_pointer);
-        // TO DO
-        // Normal function calls to known functions
-        // do not need an IP guard.
+        if (sym_get_func_version(ctx->frame->callable) != 0 &&
+            // We can remove this guard for simple function call targets.
+            (((PyCodeObject *)ctx->frame->func->func_code)->co_flags &
+                (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR)) == 0) {
+            REPLACE_OP(this_instr, _NOP, 0, 0);
+        }
     }
 
     op(_GUARD_IP_YIELD_VALUE, (ip/4 --)) {
diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h
index 1d1c62f9949e96..40938a4411e3ec 100644
--- a/Python/optimizer_cases.c.h
+++ b/Python/optimizer_cases.c.h
@@ -3046,12 +3046,12 @@
             JitOptRef callable;
             callable = stack_pointer[-2 - oparg];
             uint32_t func_version = (uint32_t)this_instr->operand0;
-            if (sym_is_const(ctx, callable) && sym_matches_type(callable, 
&PyFunction_Type)) {
-                assert(PyFunction_Check(sym_get_const(ctx, callable)));
-                ADD_OP(_CHECK_FUNCTION_VERSION_INLINE, 0, func_version);
-                uop_buffer_last(&ctx->out_buffer)->operand1 = 
(uintptr_t)sym_get_const(ctx, callable);
+            if (sym_get_func_version(callable) == func_version) {
+                REPLACE_OP(this_instr, _NOP, 0, 0);
+            }
+            else {
+                sym_set_func_version(ctx, callable, func_version);
             }
-            sym_set_type(callable, &PyFunction_Type);
             break;
         }
 
@@ -4308,18 +4308,41 @@
         }
 
         case _GUARD_CODE_VERSION__PUSH_FRAME: {
+            uint32_t version = (uint32_t)this_instr->operand0;
+            PyCodeObject *co = get_current_code_object(ctx);
+            if (co->co_version == version) {
+                _Py_BloomFilter_Add(dependencies, co);
+                if (sym_get_func_version(ctx->frame->callable) == version) {
+                    REPLACE_OP(this_instr, _NOP, 0, 0);
+                }
+            }
+            else {
+                ctx->done = true;
+            }
             break;
         }
 
         case _GUARD_CODE_VERSION_YIELD_VALUE: {
+            uint32_t version = (uint32_t)this_instr->operand0;
+            if (ctx->frame->caller) {
+                REPLACE_OP(this_instr, _NOP, 0, 0);
+            }
             break;
         }
 
         case _GUARD_CODE_VERSION_RETURN_VALUE: {
+            uint32_t version = (uint32_t)this_instr->operand0;
+            if (ctx->frame->caller) {
+                REPLACE_OP(this_instr, _NOP, 0, 0);
+            }
             break;
         }
 
         case _GUARD_CODE_VERSION_RETURN_GENERATOR: {
+            uint32_t version = (uint32_t)this_instr->operand0;
+            if (ctx->frame->caller) {
+                REPLACE_OP(this_instr, _NOP, 0, 0);
+            }
             break;
         }
 
@@ -4327,6 +4350,12 @@
             PyObject *ip = (PyObject *)this_instr->operand0;
             (void)ip;
             stack_pointer = sym_set_stack_depth((int)this_instr->operand1, 
stack_pointer);
+            if (sym_get_func_version(ctx->frame->callable) != 0 &&
+                // We can remove this guard for simple function call targets.
+                (((PyCodeObject *)ctx->frame->func->func_code)->co_flags &
+                    (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR)) == 0) {
+                REPLACE_OP(this_instr, _NOP, 0, 0);
+            }
             break;
         }
 
diff --git a/Python/optimizer_symbols.c b/Python/optimizer_symbols.c
index fd784aa11e4b89..c8697b32ab2dd4 100644
--- a/Python/optimizer_symbols.c
+++ b/Python/optimizer_symbols.c
@@ -26,22 +26,23 @@ state represents no information, and the BOTTOM state 
represents contradictory
 information. Though symbols logically progress through all intermediate nodes,
 we often skip in-between states for convenience:
 
-   UNKNOWN-------------------+------+
-   |     |                   |      |
-NULL     |                   |   RECORDED_VALUE*
-|        |                   |      |            <- Anything below this level 
is an object.
-|        NON_NULL-+          |      |
-|          |      |          |      |            <- Anything below this level 
has a known type version.
-|    TYPE_VERSION |          |      |
-|    |            |          |      |            <- Anything below this level 
has a known type.
-|    KNOWN_CLASS  |          |      |
-|    |  |  |   |  |  PREDICATE   RECORDED_VALUE(known type)
-|    |  | INT* |  |          |      |
-|    |  |  |   |  |          |      |            <- Anything below this level 
has a known truthiness.
-| TUPLE |  |   |  TRUTHINESS |      |
-|    |  |  |   |  |          |      |            <- Anything below this level 
is a known constant.
-|    KNOWN_VALUE--+----------+------+
-|    |                                           <- Anything below this level 
is unreachable.
+   UNKNOWN---------------------+------+
+   |     |                     |      |
+NULL     |                     |      RECORDED_VALUE*
+|        |                     |      |            <- Anything below this 
level is an object.
+|        NON_NULL---------+    |      |
+|          |              |    |      |            <- Anything below this 
level has a known type version.
+|    TYPE_VERSION         |    |      |
+|    |                    |    |      |            <- Anything below this 
level has a known type.
+|    KNOWN_CLASS--+       |    |      |
+|    |  |  |      |       | PREDICATE RECORDED_VALUE(known type)
+|    |  | INT*    |       |    |      |
+|    |  |  |      |       |    |      |            <- Anything below this 
level has a known truthiness.
+|    |  |  | FUNC_VERSION |    |      |
+| TUPLE |  |      | TRUTHINESS |      |
+|    |  |  |      |       |    |      |            <- Anything below this 
level is a known constant.
+|    KNOWN_VALUE--+-------+----+------+
+|    |                                             <- Anything below this 
level is unreachable.
 BOTTOM
 
 
@@ -100,6 +101,9 @@ _PyUOpSymPrint(JitOptRef ref)
         case JIT_SYM_TYPE_VERSION_TAG:
             printf("<v%u at %p>", sym->version.version, (void *)sym);
             break;
+        case JIT_SYM_FUNC_VERSION_TAG:
+            printf("<function version=%u>", sym->func_version.func_version);
+            break;
         case JIT_SYM_KNOWN_CLASS_TAG:
             printf("<%s at %p>", sym->cls.type->tp_name, (void *)sym);
             break;
@@ -305,6 +309,11 @@ _Py_uop_sym_set_type(JitOptContext *ctx, JitOptRef ref, 
PyTypeObject *typ)
                 sym_set_bottom(ctx, sym);
             }
             return;
+        case JIT_SYM_FUNC_VERSION_TAG:
+            if (typ != &PyFunction_Type) {
+                sym_set_bottom(ctx, sym);
+            }
+            return;
         case JIT_SYM_KNOWN_VALUE_TAG:
             if (Py_TYPE(sym->value.value) != typ) {
                 Py_CLEAR(sym->value.value);
@@ -408,6 +417,12 @@ _Py_uop_sym_set_type_version(JitOptContext *ctx, JitOptRef 
ref, unsigned int ver
                 return false;
             }
             return true;
+        case JIT_SYM_FUNC_VERSION_TAG:
+            if (version != PyFunction_Type.tp_version_tag) {
+                sym_set_bottom(ctx, sym);
+                return false;
+            }
+            return true;
         case JIT_SYM_BOTTOM_TAG:
             return false;
         case JIT_SYM_NON_NULL_TAG:
@@ -455,6 +470,87 @@ _Py_uop_sym_set_type_version(JitOptContext *ctx, JitOptRef 
ref, unsigned int ver
     Py_UNREACHABLE();
 }
 
+bool
+_Py_uop_sym_set_func_version(JitOptContext *ctx, JitOptRef ref, uint32_t 
version)
+{
+    JitOptSymbol *sym = PyJitRef_Unwrap(ref);
+    JitSymType tag = sym->tag;
+    switch(tag) {
+        case JIT_SYM_NULL_TAG:
+            sym_set_bottom(ctx, sym);
+            return false;
+        case JIT_SYM_KNOWN_CLASS_TAG:
+            if (sym->cls.type != &PyFunction_Type) {
+                sym_set_bottom(ctx, sym);
+                return false;
+            }
+            sym->tag = JIT_SYM_FUNC_VERSION_TAG;
+            sym->version.version = version;
+            return true;
+        case JIT_SYM_KNOWN_VALUE_TAG:
+            if (Py_TYPE(sym->value.value) != &PyFunction_Type ||
+                ((PyFunctionObject *)sym->value.value)->func_version != 
version) {
+                Py_CLEAR(sym->value.value);
+                sym_set_bottom(ctx, sym);
+                return false;
+            }
+            return true;
+        case JIT_SYM_TYPE_VERSION_TAG:
+            if (sym->version.version != PyFunction_Type.tp_version_tag) {
+                sym_set_bottom(ctx, sym);
+                return false;
+            }
+            sym->tag = JIT_SYM_FUNC_VERSION_TAG;
+            sym->version.version = version;
+            return true;
+        case JIT_SYM_FUNC_VERSION_TAG:
+            if (sym->func_version.func_version != version) {
+                sym_set_bottom(ctx, sym);
+                return false;
+            }
+            return true;
+        case JIT_SYM_BOTTOM_TAG:
+            return false;
+        case JIT_SYM_NON_NULL_TAG:
+        case JIT_SYM_UNKNOWN_TAG:
+            sym->tag = JIT_SYM_FUNC_VERSION_TAG;
+            sym->func_version.func_version = version;
+            return true;
+        case JIT_SYM_RECORDED_GEN_FUNC_TAG:
+        case JIT_SYM_COMPACT_INT:
+        case JIT_SYM_TUPLE_TAG:
+        case JIT_SYM_PREDICATE_TAG:
+        case JIT_SYM_TRUTHINESS_TAG:
+            sym_set_bottom(ctx, sym);
+            return true;
+        case JIT_SYM_RECORDED_VALUE_TAG: {
+            PyObject *val = sym->recorded_value.value;
+            if (Py_TYPE(val) != &PyFunction_Type ||
+                ((PyFunctionObject *)sym->recorded_value.value)->func_version 
!= version) {
+                sym_set_bottom(ctx, sym);
+                return false;
+            }
+            // Promote to known value, as we have guarded/checked on it.
+            sym->tag = JIT_SYM_KNOWN_VALUE_TAG;
+            // New ownership. We need to NewRef here, as
+            // it's originally kept alive by the trace buffer.
+            sym->value.value = Py_NewRef(val);
+            return true;
+        }
+        case JIT_SYM_RECORDED_TYPE_TAG:
+            if (sym->recorded_type.type == &PyFunction_Type) {
+                sym->tag = JIT_SYM_FUNC_VERSION_TAG;
+                sym->func_version.func_version = version;
+                return true;
+            }
+            else {
+                sym_set_bottom(ctx, sym);
+                return false;
+            }
+    }
+    Py_UNREACHABLE();
+}
+
 void
 _Py_uop_sym_set_const(JitOptContext *ctx, JitOptRef ref, PyObject *const_val)
 {
@@ -500,6 +596,14 @@ _Py_uop_sym_set_const(JitOptContext *ctx, JitOptRef ref, 
PyObject *const_val)
             }
             make_const(sym, const_val);
             return;
+        case JIT_SYM_FUNC_VERSION_TAG:
+            if (Py_TYPE(const_val) != &PyFunction_Type ||
+                ((PyFunctionObject *)const_val)->func_version != 
sym->func_version.func_version) {
+                sym_set_bottom(ctx, sym);
+                return;
+            }
+            make_const(sym, const_val);
+            return;
         case JIT_SYM_BOTTOM_TAG:
             return;
         case JIT_SYM_RECORDED_VALUE_TAG:
@@ -675,6 +779,8 @@ _Py_uop_sym_get_type(JitOptRef ref)
             return Py_TYPE(sym->value.value);
         case JIT_SYM_TYPE_VERSION_TAG:
             return _PyType_LookupByVersion(sym->version.version);
+        case JIT_SYM_FUNC_VERSION_TAG:
+            return &PyFunction_Type;
         case JIT_SYM_TUPLE_TAG:
             return &PyTuple_Type;
         case JIT_SYM_PREDICATE_TAG:
@@ -699,6 +805,7 @@ _Py_uop_sym_get_probable_type(JitOptRef ref)
         case JIT_SYM_NON_NULL_TAG:
         case JIT_SYM_UNKNOWN_TAG:
         case JIT_SYM_TYPE_VERSION_TAG:
+        case JIT_SYM_FUNC_VERSION_TAG:
         case JIT_SYM_TUPLE_TAG:
         case JIT_SYM_PREDICATE_TAG:
         case JIT_SYM_TRUTHINESS_TAG:
@@ -731,6 +838,8 @@ _Py_uop_sym_get_type_version(JitOptRef ref)
             return 0;
         case JIT_SYM_TYPE_VERSION_TAG:
             return sym->version.version;
+        case JIT_SYM_FUNC_VERSION_TAG:
+            return PyFunction_Type.tp_version_tag;
         case JIT_SYM_KNOWN_CLASS_TAG:
             return sym->cls.version;
         case JIT_SYM_KNOWN_VALUE_TAG:
@@ -748,6 +857,38 @@ _Py_uop_sym_get_type_version(JitOptRef ref)
     Py_UNREACHABLE();
 }
 
+uint32_t
+_Py_uop_sym_get_func_version(JitOptRef ref)
+{
+    JitOptSymbol *sym = PyJitRef_Unwrap(ref);
+    JitSymType tag = sym->tag;
+    switch(tag) {
+        case JIT_SYM_NULL_TAG:
+        case JIT_SYM_BOTTOM_TAG:
+        case JIT_SYM_NON_NULL_TAG:
+        case JIT_SYM_UNKNOWN_TAG:
+        case JIT_SYM_RECORDED_VALUE_TAG:
+        case JIT_SYM_RECORDED_TYPE_TAG:
+        case JIT_SYM_TYPE_VERSION_TAG:
+        case JIT_SYM_KNOWN_CLASS_TAG:
+        case JIT_SYM_TUPLE_TAG:
+        case JIT_SYM_PREDICATE_TAG:
+        case JIT_SYM_TRUTHINESS_TAG:
+        case JIT_SYM_COMPACT_INT:
+        case JIT_SYM_RECORDED_GEN_FUNC_TAG:
+            return 0;
+        case JIT_SYM_FUNC_VERSION_TAG:
+            return sym->func_version.func_version;
+        case JIT_SYM_KNOWN_VALUE_TAG:
+            if (Py_TYPE(sym->value.value) == &PyFunction_Type) {
+                return ((PyFunctionObject *)sym->value.value)->func_version;
+            }
+            return 0;
+    }
+    Py_UNREACHABLE();
+}
+
+
 bool
 _Py_uop_sym_has_type(JitOptRef sym)
 {
@@ -779,6 +920,7 @@ _Py_uop_sym_get_probable_value(JitOptRef ref)
         case JIT_SYM_UNKNOWN_TAG:
         case JIT_SYM_RECORDED_TYPE_TAG:
         case JIT_SYM_TYPE_VERSION_TAG:
+        case JIT_SYM_FUNC_VERSION_TAG:
         case JIT_SYM_TUPLE_TAG:
         case JIT_SYM_PREDICATE_TAG:
         case JIT_SYM_TRUTHINESS_TAG:
@@ -848,6 +990,8 @@ _Py_uop_sym_truthiness(JitOptContext *ctx, JitOptRef ref)
             return -1;
         case JIT_SYM_KNOWN_VALUE_TAG:
             break;
+        case JIT_SYM_FUNC_VERSION_TAG:
+            return 1;
         case JIT_SYM_TUPLE_TAG:
             return sym->tuple.length != 0;
         case JIT_SYM_TRUTHINESS_TAG:
@@ -996,6 +1140,7 @@ _Py_uop_sym_set_compact_int(JitOptContext *ctx, JitOptRef 
ref)
                 sym_set_bottom(ctx, sym);
             }
             return;
+        case JIT_SYM_FUNC_VERSION_TAG:
         case JIT_SYM_TUPLE_TAG:
         case JIT_SYM_PREDICATE_TAG:
         case JIT_SYM_TRUTHINESS_TAG:
@@ -1183,6 +1328,7 @@ _Py_uop_sym_set_recorded_value(JitOptContext *ctx, 
JitOptRef ref, PyObject *valu
         case JIT_SYM_PREDICATE_TAG:
         case JIT_SYM_TRUTHINESS_TAG:
         case JIT_SYM_COMPACT_INT:
+        case JIT_SYM_FUNC_VERSION_TAG:
             return;
     }
     Py_UNREACHABLE();
@@ -1206,6 +1352,7 @@ _Py_uop_sym_set_recorded_gen_func(JitOptContext *ctx, 
JitOptRef ref, PyFunctionO
         case JIT_SYM_PREDICATE_TAG:
         case JIT_SYM_TRUTHINESS_TAG:
         case JIT_SYM_COMPACT_INT:
+        case JIT_SYM_FUNC_VERSION_TAG:
             sym_set_bottom(ctx, sym);
             return;
         case JIT_SYM_BOTTOM_TAG:
@@ -1302,6 +1449,7 @@ _Py_uop_sym_set_recorded_type(JitOptContext *ctx, 
JitOptRef ref, PyTypeObject *t
         case JIT_SYM_TRUTHINESS_TAG:
         case JIT_SYM_COMPACT_INT:
         case JIT_SYM_RECORDED_GEN_FUNC_TAG:
+        case JIT_SYM_FUNC_VERSION_TAG:
             return;
     }
     Py_UNREACHABLE();
@@ -1330,6 +1478,7 @@ _Py_uop_frame_new_from_symbol(
         frame->func = func;
     }
     assert(frame->stack_pointer != NULL);
+    frame->callable = callable;
     return frame;
 }
 
@@ -1384,6 +1533,8 @@ _Py_uop_frame_new(
         frame->locals[i] = local;
     }
 
+    frame->callable = _Py_uop_sym_new_not_null(ctx);
+
     /* Most optimizations rely on code objects being immutable (including 
sys._getframe modifications),
      * and up to date for instrumentation. */
     _Py_BloomFilter_Add(ctx->dependencies, co);
@@ -1958,6 +2109,15 @@ _Py_uop_symbols_test(PyObject *Py_UNUSED(self), PyObject 
*Py_UNUSED(ignored))
     TEST_PREDICATE(_Py_uop_sym_matches_type(ref_int, &PyLong_Type), "43 is not 
an int");
     TEST_PREDICATE(_Py_uop_sym_get_const(ctx, ref_int) == val_43, "43 isn't 
43");
 
+    // Test func version's important transitions.
+    JitOptRef func_version = _Py_uop_sym_new_not_null(ctx);
+    TEST_PREDICATE(_Py_uop_sym_get_func_version(func_version) == 0, "func 
version should be unset");
+    _Py_uop_sym_set_func_version(ctx, func_version, 172);
+    TEST_PREDICATE(_Py_uop_sym_get_func_version(func_version) == 172, "func 
version should be set");
+    func_version = _Py_uop_sym_new_type(ctx, &PyFunction_Type);
+    _Py_uop_sym_set_func_version(ctx, func_version, 192);
+    TEST_PREDICATE(_Py_uop_sym_get_func_version(func_version) == 192, "func 
version should be set");
+
     // Test recorded values
 
     /* Test that recorded values aren't treated as known values*/

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to