https://github.com/python/cpython/commit/da53660f35db2dfb1e6181e603468dfe5758f3b8
commit: da53660f35db2dfb1e6181e603468dfe5758f3b8
branch: main
author: Sam Gross <colesb...@gmail.com>
committer: colesbury <colesb...@gmail.com>
date: 2025-04-21T15:54:25-04:00
summary:

gh-131586: Avoid refcount contention in context managers (gh-131851)

This avoid reference count contention in the free threading build
when calling special methods like `__enter__` and `__exit__`.

files:
M Include/internal/pycore_object.h
M Include/internal/pycore_opcode_metadata.h
M Include/internal/pycore_uop_ids.h
M Include/internal/pycore_uop_metadata.h
M Objects/typeobject.c
M Python/bytecodes.c
M Python/executor_cases.c.h
M Python/generated_cases.c.h
M Python/optimizer_bytecodes.c
M Python/optimizer_cases.c.h
M Tools/ftscalingbench/ftscalingbench.py

diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h
index e5034ff4dcc42b..b7e162c8abcabf 100644
--- a/Include/internal/pycore_object.h
+++ b/Include/internal/pycore_object.h
@@ -950,7 +950,7 @@ extern int _PyObject_IsInstanceDictEmpty(PyObject *);
 
 // Export for 'math' shared extension
 PyAPI_FUNC(PyObject*) _PyObject_LookupSpecial(PyObject *, PyObject *);
-PyAPI_FUNC(PyObject*) _PyObject_LookupSpecialMethod(PyObject *self, PyObject 
*attr, PyObject **self_or_null);
+PyAPI_FUNC(int) _PyObject_LookupSpecialMethod(PyObject *attr, _PyStackRef 
*method_and_self);
 
 // Calls the method named `attr` on `self`, but does not set an exception if
 // the attribute does not exist.
diff --git a/Include/internal/pycore_opcode_metadata.h 
b/Include/internal/pycore_opcode_metadata.h
index 521f7a92cf08c4..3f01f7d997f57b 100644
--- a/Include/internal/pycore_opcode_metadata.h
+++ b/Include/internal/pycore_opcode_metadata.h
@@ -1218,7 +1218,7 @@ const struct opcode_metadata 
_PyOpcode_opcode_metadata[266] = {
     [LOAD_LOCALS] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
     [LOAD_NAME] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_NAME_FLAG | 
HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
     [LOAD_SMALL_INT] = { true, INSTR_FMT_IB, HAS_ARG_FLAG },
-    [LOAD_SPECIAL] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | 
HAS_ESCAPES_FLAG },
+    [LOAD_SPECIAL] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | 
HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG },
     [LOAD_SUPER_ATTR] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_NAME_FLAG | 
HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
     [LOAD_SUPER_ATTR_ATTR] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | 
HAS_NAME_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
     [LOAD_SUPER_ATTR_METHOD] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | 
HAS_NAME_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | 
HAS_ESCAPES_FLAG },
@@ -1427,7 +1427,7 @@ _PyOpcode_macro_expansion[256] = {
     [LOAD_LOCALS] = { .nuops = 1, .uops = { { _LOAD_LOCALS, OPARG_SIMPLE, 0 } 
} },
     [LOAD_NAME] = { .nuops = 1, .uops = { { _LOAD_NAME, OPARG_SIMPLE, 0 } } },
     [LOAD_SMALL_INT] = { .nuops = 1, .uops = { { _LOAD_SMALL_INT, 
OPARG_SIMPLE, 0 } } },
-    [LOAD_SPECIAL] = { .nuops = 1, .uops = { { _LOAD_SPECIAL, OPARG_SIMPLE, 0 
} } },
+    [LOAD_SPECIAL] = { .nuops = 2, .uops = { { _INSERT_NULL, OPARG_SIMPLE, 0 
}, { _LOAD_SPECIAL, OPARG_SIMPLE, 0 } } },
     [LOAD_SUPER_ATTR_ATTR] = { .nuops = 1, .uops = { { _LOAD_SUPER_ATTR_ATTR, 
OPARG_SIMPLE, 1 } } },
     [LOAD_SUPER_ATTR_METHOD] = { .nuops = 1, .uops = { { 
_LOAD_SUPER_ATTR_METHOD, OPARG_SIMPLE, 1 } } },
     [MAKE_CELL] = { .nuops = 1, .uops = { { _MAKE_CELL, OPARG_SIMPLE, 0 } } },
diff --git a/Include/internal/pycore_uop_ids.h 
b/Include/internal/pycore_uop_ids.h
index e9a536919da598..691bc6d6586b0a 100644
--- a/Include/internal/pycore_uop_ids.h
+++ b/Include/internal/pycore_uop_ids.h
@@ -151,6 +151,7 @@ extern "C" {
 #define _INIT_CALL_PY_EXACT_ARGS_2 401
 #define _INIT_CALL_PY_EXACT_ARGS_3 402
 #define _INIT_CALL_PY_EXACT_ARGS_4 403
+#define _INSERT_NULL 404
 #define _INSTRUMENTED_FOR_ITER INSTRUMENTED_FOR_ITER
 #define _INSTRUMENTED_INSTRUCTION INSTRUMENTED_INSTRUCTION
 #define _INSTRUMENTED_JUMP_FORWARD INSTRUMENTED_JUMP_FORWARD
@@ -160,163 +161,163 @@ extern "C" {
 #define _INSTRUMENTED_POP_JUMP_IF_NONE INSTRUMENTED_POP_JUMP_IF_NONE
 #define _INSTRUMENTED_POP_JUMP_IF_NOT_NONE INSTRUMENTED_POP_JUMP_IF_NOT_NONE
 #define _INSTRUMENTED_POP_JUMP_IF_TRUE INSTRUMENTED_POP_JUMP_IF_TRUE
-#define _IS_NONE 404
+#define _IS_NONE 405
 #define _IS_OP IS_OP
-#define _ITER_CHECK_LIST 405
-#define _ITER_CHECK_RANGE 406
-#define _ITER_CHECK_TUPLE 407
-#define _ITER_JUMP_LIST 408
-#define _ITER_JUMP_RANGE 409
-#define _ITER_JUMP_TUPLE 410
-#define _ITER_NEXT_LIST 411
-#define _ITER_NEXT_LIST_TIER_TWO 412
-#define _ITER_NEXT_RANGE 413
-#define _ITER_NEXT_TUPLE 414
-#define _JUMP_TO_TOP 415
+#define _ITER_CHECK_LIST 406
+#define _ITER_CHECK_RANGE 407
+#define _ITER_CHECK_TUPLE 408
+#define _ITER_JUMP_LIST 409
+#define _ITER_JUMP_RANGE 410
+#define _ITER_JUMP_TUPLE 411
+#define _ITER_NEXT_LIST 412
+#define _ITER_NEXT_LIST_TIER_TWO 413
+#define _ITER_NEXT_RANGE 414
+#define _ITER_NEXT_TUPLE 415
+#define _JUMP_TO_TOP 416
 #define _LIST_APPEND LIST_APPEND
 #define _LIST_EXTEND LIST_EXTEND
-#define _LOAD_ATTR 416
-#define _LOAD_ATTR_CLASS 417
+#define _LOAD_ATTR 417
+#define _LOAD_ATTR_CLASS 418
 #define _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN
-#define _LOAD_ATTR_INSTANCE_VALUE 418
-#define _LOAD_ATTR_METHOD_LAZY_DICT 419
-#define _LOAD_ATTR_METHOD_NO_DICT 420
-#define _LOAD_ATTR_METHOD_WITH_VALUES 421
-#define _LOAD_ATTR_MODULE 422
-#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT 423
-#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 424
-#define _LOAD_ATTR_PROPERTY_FRAME 425
-#define _LOAD_ATTR_SLOT 426
-#define _LOAD_ATTR_WITH_HINT 427
+#define _LOAD_ATTR_INSTANCE_VALUE 419
+#define _LOAD_ATTR_METHOD_LAZY_DICT 420
+#define _LOAD_ATTR_METHOD_NO_DICT 421
+#define _LOAD_ATTR_METHOD_WITH_VALUES 422
+#define _LOAD_ATTR_MODULE 423
+#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT 424
+#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 425
+#define _LOAD_ATTR_PROPERTY_FRAME 426
+#define _LOAD_ATTR_SLOT 427
+#define _LOAD_ATTR_WITH_HINT 428
 #define _LOAD_BUILD_CLASS LOAD_BUILD_CLASS
-#define _LOAD_BYTECODE 428
+#define _LOAD_BYTECODE 429
 #define _LOAD_COMMON_CONSTANT LOAD_COMMON_CONSTANT
 #define _LOAD_CONST LOAD_CONST
 #define _LOAD_CONST_IMMORTAL LOAD_CONST_IMMORTAL
-#define _LOAD_CONST_INLINE 429
-#define _LOAD_CONST_INLINE_BORROW 430
+#define _LOAD_CONST_INLINE 430
+#define _LOAD_CONST_INLINE_BORROW 431
 #define _LOAD_CONST_MORTAL LOAD_CONST_MORTAL
 #define _LOAD_DEREF LOAD_DEREF
-#define _LOAD_FAST 431
-#define _LOAD_FAST_0 432
-#define _LOAD_FAST_1 433
-#define _LOAD_FAST_2 434
-#define _LOAD_FAST_3 435
-#define _LOAD_FAST_4 436
-#define _LOAD_FAST_5 437
-#define _LOAD_FAST_6 438
-#define _LOAD_FAST_7 439
+#define _LOAD_FAST 432
+#define _LOAD_FAST_0 433
+#define _LOAD_FAST_1 434
+#define _LOAD_FAST_2 435
+#define _LOAD_FAST_3 436
+#define _LOAD_FAST_4 437
+#define _LOAD_FAST_5 438
+#define _LOAD_FAST_6 439
+#define _LOAD_FAST_7 440
 #define _LOAD_FAST_AND_CLEAR LOAD_FAST_AND_CLEAR
-#define _LOAD_FAST_BORROW 440
-#define _LOAD_FAST_BORROW_0 441
-#define _LOAD_FAST_BORROW_1 442
-#define _LOAD_FAST_BORROW_2 443
-#define _LOAD_FAST_BORROW_3 444
-#define _LOAD_FAST_BORROW_4 445
-#define _LOAD_FAST_BORROW_5 446
-#define _LOAD_FAST_BORROW_6 447
-#define _LOAD_FAST_BORROW_7 448
+#define _LOAD_FAST_BORROW 441
+#define _LOAD_FAST_BORROW_0 442
+#define _LOAD_FAST_BORROW_1 443
+#define _LOAD_FAST_BORROW_2 444
+#define _LOAD_FAST_BORROW_3 445
+#define _LOAD_FAST_BORROW_4 446
+#define _LOAD_FAST_BORROW_5 447
+#define _LOAD_FAST_BORROW_6 448
+#define _LOAD_FAST_BORROW_7 449
 #define _LOAD_FAST_BORROW_LOAD_FAST_BORROW LOAD_FAST_BORROW_LOAD_FAST_BORROW
 #define _LOAD_FAST_CHECK LOAD_FAST_CHECK
 #define _LOAD_FAST_LOAD_FAST LOAD_FAST_LOAD_FAST
 #define _LOAD_FROM_DICT_OR_DEREF LOAD_FROM_DICT_OR_DEREF
 #define _LOAD_FROM_DICT_OR_GLOBALS LOAD_FROM_DICT_OR_GLOBALS
-#define _LOAD_GLOBAL 449
-#define _LOAD_GLOBAL_BUILTINS 450
-#define _LOAD_GLOBAL_MODULE 451
+#define _LOAD_GLOBAL 450
+#define _LOAD_GLOBAL_BUILTINS 451
+#define _LOAD_GLOBAL_MODULE 452
 #define _LOAD_LOCALS LOAD_LOCALS
 #define _LOAD_NAME LOAD_NAME
-#define _LOAD_SMALL_INT 452
-#define _LOAD_SMALL_INT_0 453
-#define _LOAD_SMALL_INT_1 454
-#define _LOAD_SMALL_INT_2 455
-#define _LOAD_SMALL_INT_3 456
-#define _LOAD_SPECIAL LOAD_SPECIAL
+#define _LOAD_SMALL_INT 453
+#define _LOAD_SMALL_INT_0 454
+#define _LOAD_SMALL_INT_1 455
+#define _LOAD_SMALL_INT_2 456
+#define _LOAD_SMALL_INT_3 457
+#define _LOAD_SPECIAL 458
 #define _LOAD_SUPER_ATTR_ATTR LOAD_SUPER_ATTR_ATTR
 #define _LOAD_SUPER_ATTR_METHOD LOAD_SUPER_ATTR_METHOD
-#define _MAKE_CALLARGS_A_TUPLE 457
+#define _MAKE_CALLARGS_A_TUPLE 459
 #define _MAKE_CELL MAKE_CELL
 #define _MAKE_FUNCTION MAKE_FUNCTION
-#define _MAKE_WARM 458
+#define _MAKE_WARM 460
 #define _MAP_ADD MAP_ADD
 #define _MATCH_CLASS MATCH_CLASS
 #define _MATCH_KEYS MATCH_KEYS
 #define _MATCH_MAPPING MATCH_MAPPING
 #define _MATCH_SEQUENCE MATCH_SEQUENCE
-#define _MAYBE_EXPAND_METHOD 459
-#define _MAYBE_EXPAND_METHOD_KW 460
-#define _MONITOR_CALL 461
-#define _MONITOR_CALL_KW 462
-#define _MONITOR_JUMP_BACKWARD 463
-#define _MONITOR_RESUME 464
+#define _MAYBE_EXPAND_METHOD 461
+#define _MAYBE_EXPAND_METHOD_KW 462
+#define _MONITOR_CALL 463
+#define _MONITOR_CALL_KW 464
+#define _MONITOR_JUMP_BACKWARD 465
+#define _MONITOR_RESUME 466
 #define _NOP NOP
 #define _POP_EXCEPT POP_EXCEPT
-#define _POP_JUMP_IF_FALSE 465
-#define _POP_JUMP_IF_TRUE 466
+#define _POP_JUMP_IF_FALSE 467
+#define _POP_JUMP_IF_TRUE 468
 #define _POP_TOP POP_TOP
-#define _POP_TOP_LOAD_CONST_INLINE 467
-#define _POP_TOP_LOAD_CONST_INLINE_BORROW 468
-#define _POP_TWO_LOAD_CONST_INLINE_BORROW 469
+#define _POP_TOP_LOAD_CONST_INLINE 469
+#define _POP_TOP_LOAD_CONST_INLINE_BORROW 470
+#define _POP_TWO_LOAD_CONST_INLINE_BORROW 471
 #define _PUSH_EXC_INFO PUSH_EXC_INFO
-#define _PUSH_FRAME 470
+#define _PUSH_FRAME 472
 #define _PUSH_NULL PUSH_NULL
-#define _PUSH_NULL_CONDITIONAL 471
-#define _PY_FRAME_GENERAL 472
-#define _PY_FRAME_KW 473
-#define _QUICKEN_RESUME 474
-#define _REPLACE_WITH_TRUE 475
+#define _PUSH_NULL_CONDITIONAL 473
+#define _PY_FRAME_GENERAL 474
+#define _PY_FRAME_KW 475
+#define _QUICKEN_RESUME 476
+#define _REPLACE_WITH_TRUE 477
 #define _RESUME_CHECK RESUME_CHECK
 #define _RETURN_GENERATOR RETURN_GENERATOR
 #define _RETURN_VALUE RETURN_VALUE
-#define _SAVE_RETURN_OFFSET 476
-#define _SEND 477
-#define _SEND_GEN_FRAME 478
+#define _SAVE_RETURN_OFFSET 478
+#define _SEND 479
+#define _SEND_GEN_FRAME 480
 #define _SETUP_ANNOTATIONS SETUP_ANNOTATIONS
 #define _SET_ADD SET_ADD
 #define _SET_FUNCTION_ATTRIBUTE SET_FUNCTION_ATTRIBUTE
 #define _SET_UPDATE SET_UPDATE
-#define _START_EXECUTOR 479
-#define _STORE_ATTR 480
-#define _STORE_ATTR_INSTANCE_VALUE 481
-#define _STORE_ATTR_SLOT 482
-#define _STORE_ATTR_WITH_HINT 483
+#define _START_EXECUTOR 481
+#define _STORE_ATTR 482
+#define _STORE_ATTR_INSTANCE_VALUE 483
+#define _STORE_ATTR_SLOT 484
+#define _STORE_ATTR_WITH_HINT 485
 #define _STORE_DEREF STORE_DEREF
-#define _STORE_FAST 484
-#define _STORE_FAST_0 485
-#define _STORE_FAST_1 486
-#define _STORE_FAST_2 487
-#define _STORE_FAST_3 488
-#define _STORE_FAST_4 489
-#define _STORE_FAST_5 490
-#define _STORE_FAST_6 491
-#define _STORE_FAST_7 492
+#define _STORE_FAST 486
+#define _STORE_FAST_0 487
+#define _STORE_FAST_1 488
+#define _STORE_FAST_2 489
+#define _STORE_FAST_3 490
+#define _STORE_FAST_4 491
+#define _STORE_FAST_5 492
+#define _STORE_FAST_6 493
+#define _STORE_FAST_7 494
 #define _STORE_FAST_LOAD_FAST STORE_FAST_LOAD_FAST
 #define _STORE_FAST_STORE_FAST STORE_FAST_STORE_FAST
 #define _STORE_GLOBAL STORE_GLOBAL
 #define _STORE_NAME STORE_NAME
-#define _STORE_SLICE 493
-#define _STORE_SUBSCR 494
-#define _STORE_SUBSCR_DICT 495
-#define _STORE_SUBSCR_LIST_INT 496
+#define _STORE_SLICE 495
+#define _STORE_SUBSCR 496
+#define _STORE_SUBSCR_DICT 497
+#define _STORE_SUBSCR_LIST_INT 498
 #define _SWAP SWAP
-#define _TIER2_RESUME_CHECK 497
-#define _TO_BOOL 498
+#define _TIER2_RESUME_CHECK 499
+#define _TO_BOOL 500
 #define _TO_BOOL_BOOL TO_BOOL_BOOL
 #define _TO_BOOL_INT TO_BOOL_INT
-#define _TO_BOOL_LIST 499
+#define _TO_BOOL_LIST 501
 #define _TO_BOOL_NONE TO_BOOL_NONE
-#define _TO_BOOL_STR 500
+#define _TO_BOOL_STR 502
 #define _UNARY_INVERT UNARY_INVERT
 #define _UNARY_NEGATIVE UNARY_NEGATIVE
 #define _UNARY_NOT UNARY_NOT
 #define _UNPACK_EX UNPACK_EX
-#define _UNPACK_SEQUENCE 501
-#define _UNPACK_SEQUENCE_LIST 502
-#define _UNPACK_SEQUENCE_TUPLE 503
-#define _UNPACK_SEQUENCE_TWO_TUPLE 504
+#define _UNPACK_SEQUENCE 503
+#define _UNPACK_SEQUENCE_LIST 504
+#define _UNPACK_SEQUENCE_TUPLE 505
+#define _UNPACK_SEQUENCE_TWO_TUPLE 506
 #define _WITH_EXCEPT_START WITH_EXCEPT_START
 #define _YIELD_VALUE YIELD_VALUE
-#define MAX_UOP_ID 504
+#define MAX_UOP_ID 506
 
 #ifdef __cplusplus
 }
diff --git a/Include/internal/pycore_uop_metadata.h 
b/Include/internal/pycore_uop_metadata.h
index 874756770c1871..b2df354fdcc61b 100644
--- a/Include/internal/pycore_uop_metadata.h
+++ b/Include/internal/pycore_uop_metadata.h
@@ -209,7 +209,8 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = {
     [_GUARD_NOT_EXHAUSTED_RANGE] = HAS_EXIT_FLAG,
     [_ITER_NEXT_RANGE] = HAS_ERROR_FLAG,
     [_FOR_ITER_GEN_FRAME] = HAS_ARG_FLAG | HAS_DEOPT_FLAG,
-    [_LOAD_SPECIAL] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG,
+    [_INSERT_NULL] = 0,
+    [_LOAD_SPECIAL] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | 
HAS_ESCAPES_FLAG,
     [_WITH_EXCEPT_START] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG,
     [_PUSH_EXC_INFO] = 0,
     [_GUARD_DORV_VALUES_INST_ATTR_FROM_DICT] = HAS_DEOPT_FLAG,
@@ -445,6 +446,7 @@ const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = {
     [_INIT_CALL_PY_EXACT_ARGS_2] = "_INIT_CALL_PY_EXACT_ARGS_2",
     [_INIT_CALL_PY_EXACT_ARGS_3] = "_INIT_CALL_PY_EXACT_ARGS_3",
     [_INIT_CALL_PY_EXACT_ARGS_4] = "_INIT_CALL_PY_EXACT_ARGS_4",
+    [_INSERT_NULL] = "_INSERT_NULL",
     [_IS_NONE] = "_IS_NONE",
     [_IS_OP] = "_IS_OP",
     [_ITER_CHECK_LIST] = "_ITER_CHECK_LIST",
@@ -970,8 +972,10 @@ int _PyUop_num_popped(int opcode, int oparg)
             return 0;
         case _FOR_ITER_GEN_FRAME:
             return 0;
-        case _LOAD_SPECIAL:
+        case _INSERT_NULL:
             return 1;
+        case _LOAD_SPECIAL:
+            return 0;
         case _WITH_EXCEPT_START:
             return 0;
         case _PUSH_EXC_INFO:
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 5663aee3c2e069..a720a98121dc93 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -2794,32 +2794,37 @@ _PyObject_LookupSpecial(PyObject *self, PyObject *attr)
     return res;
 }
 
-/* Steals a reference to self */
-PyObject *
-_PyObject_LookupSpecialMethod(PyObject *self, PyObject *attr, PyObject 
**self_or_null)
+// Lookup the method name `attr` on `self`. On entry, `method_and_self[0]`
+// is null and `method_and_self[1]` is `self`. On exit, `method_and_self[0]`
+// is the method object and `method_and_self[1]` is `self` if the method is
+// not bound.
+// Return 1 on success, -1 on error, and 0 if the method is missing.
+int
+_PyObject_LookupSpecialMethod(PyObject *attr, _PyStackRef *method_and_self)
 {
-    PyObject *res;
-
-    res = _PyType_LookupRef(Py_TYPE(self), attr);
-    if (res == NULL) {
-        Py_DECREF(self);
-        *self_or_null = NULL;
-        return NULL;
+    PyObject *self = PyStackRef_AsPyObjectBorrow(method_and_self[1]);
+    _PyType_LookupStackRefAndVersion(Py_TYPE(self), attr, &method_and_self[0]);
+    PyObject *method_o = PyStackRef_AsPyObjectBorrow(method_and_self[0]);
+    if (method_o == NULL) {
+        return 0;
     }
 
-    if (_PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR)) {
+    if (_PyType_HasFeature(Py_TYPE(method_o), Py_TPFLAGS_METHOD_DESCRIPTOR)) {
         /* Avoid temporary PyMethodObject */
-        *self_or_null = self;
+        return 1;
     }
-    else {
-        descrgetfunc f = Py_TYPE(res)->tp_descr_get;
-        if (f != NULL) {
-            Py_SETREF(res, f(res, self, (PyObject *)(Py_TYPE(self))));
+
+    descrgetfunc f = Py_TYPE(method_o)->tp_descr_get;
+    if (f != NULL) {
+        PyObject *func = f(method_o, self, (PyObject *)(Py_TYPE(self)));
+        if (func == NULL) {
+            return -1;
         }
-        *self_or_null = NULL;
-        Py_DECREF(self);
+        PyStackRef_CLEAR(method_and_self[0]); // clear method
+        method_and_self[0] = PyStackRef_FromPyObjectSteal(func);
     }
-    return res;
+    PyStackRef_CLEAR(method_and_self[1]); // clear self
+    return 1;
 }
 
 static int
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 07df22c761fc1c..ad82e0b060de79 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -3417,28 +3417,33 @@ dummy_func(
             _FOR_ITER_GEN_FRAME +
             _PUSH_FRAME;
 
-        inst(LOAD_SPECIAL, (owner -- attr, self_or_null)) {
-            assert(oparg <= SPECIAL_MAX);
-            PyObject *owner_o = PyStackRef_AsPyObjectSteal(owner);
+        op(_INSERT_NULL, (self -- method_and_self[2])) {
+            method_and_self[1] = self;
+            method_and_self[0] = PyStackRef_NULL;
+            DEAD(self);
+        }
+
+        op(_LOAD_SPECIAL, (method_and_self[2] -- method_and_self[2])) {
             PyObject *name = _Py_SpecialMethods[oparg].name;
-            PyObject *self_or_null_o;
-            PyObject *attr_o = _PyObject_LookupSpecialMethod(owner_o, name, 
&self_or_null_o);
-            if (attr_o == NULL) {
-                if (!_PyErr_Occurred(tstate)) {
-                    const char *errfmt = 
_PyEval_SpecialMethodCanSuggest(owner_o, oparg)
+            int err = _PyObject_LookupSpecialMethod(name, method_and_self);
+            if (err <= 0) {
+                if (err == 0) {
+                    PyObject *owner = 
PyStackRef_AsPyObjectBorrow(method_and_self[1]);
+                    const char *errfmt = 
_PyEval_SpecialMethodCanSuggest(owner, oparg)
                         ? _Py_SpecialMethods[oparg].error_suggestion
                         : _Py_SpecialMethods[oparg].error;
                     assert(!_PyErr_Occurred(tstate));
                     assert(errfmt != NULL);
-                    _PyErr_Format(tstate, PyExc_TypeError, errfmt, owner_o);
+                    _PyErr_Format(tstate, PyExc_TypeError, errfmt, owner);
                 }
-                ERROR_IF(true, error);
+                ERROR_NO_POP();
             }
-            attr = PyStackRef_FromPyObjectSteal(attr_o);
-            self_or_null = self_or_null_o == NULL ?
-                PyStackRef_NULL : PyStackRef_FromPyObjectSteal(self_or_null_o);
         }
 
+        macro(LOAD_SPECIAL) =
+            _INSERT_NULL +
+            _LOAD_SPECIAL;
+
         inst(WITH_EXCEPT_START, (exit_func, exit_self, lasti, unused, val -- 
exit_func, exit_self, lasti, unused, val, res)) {
             /* At the top of the stack are 4 values:
                - val: TOP = exc_info()
diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h
index cd265c383bd380..c65cc9efa5e5eb 100644
--- a/Python/executor_cases.c.h
+++ b/Python/executor_cases.c.h
@@ -4407,43 +4407,42 @@
             break;
         }
 
+        case _INSERT_NULL: {
+            _PyStackRef self;
+            _PyStackRef *method_and_self;
+            self = stack_pointer[-1];
+            method_and_self = &stack_pointer[-1];
+            method_and_self[1] = self;
+            method_and_self[0] = PyStackRef_NULL;
+            stack_pointer += 1;
+            assert(WITHIN_STACK_BOUNDS());
+            break;
+        }
+
         case _LOAD_SPECIAL: {
-            _PyStackRef owner;
-            _PyStackRef attr;
-            _PyStackRef self_or_null;
+            _PyStackRef *method_and_self;
             oparg = CURRENT_OPARG();
-            owner = stack_pointer[-1];
-            assert(oparg <= SPECIAL_MAX);
-            PyObject *owner_o = PyStackRef_AsPyObjectSteal(owner);
+            method_and_self = &stack_pointer[-2];
             PyObject *name = _Py_SpecialMethods[oparg].name;
-            PyObject *self_or_null_o;
-            stack_pointer += -1;
-            assert(WITHIN_STACK_BOUNDS());
             _PyFrame_SetStackPointer(frame, stack_pointer);
-            PyObject *attr_o = _PyObject_LookupSpecialMethod(owner_o, name, 
&self_or_null_o);
+            int err = _PyObject_LookupSpecialMethod(name, method_and_self);
             stack_pointer = _PyFrame_GetStackPointer(frame);
-            if (attr_o == NULL) {
-                if (!_PyErr_Occurred(tstate)) {
+            if (err <= 0) {
+                if (err == 0) {
+                    PyObject *owner = 
PyStackRef_AsPyObjectBorrow(method_and_self[1]);
                     _PyFrame_SetStackPointer(frame, stack_pointer);
-                    const char *errfmt = 
_PyEval_SpecialMethodCanSuggest(owner_o, oparg)
+                    const char *errfmt = 
_PyEval_SpecialMethodCanSuggest(owner, oparg)
                     ? _Py_SpecialMethods[oparg].error_suggestion
                 : _Py_SpecialMethods[oparg].error;
                     stack_pointer = _PyFrame_GetStackPointer(frame);
                     assert(!_PyErr_Occurred(tstate));
                     assert(errfmt != NULL);
                     _PyFrame_SetStackPointer(frame, stack_pointer);
-                    _PyErr_Format(tstate, PyExc_TypeError, errfmt, owner_o);
+                    _PyErr_Format(tstate, PyExc_TypeError, errfmt, owner);
                     stack_pointer = _PyFrame_GetStackPointer(frame);
                 }
                 JUMP_TO_ERROR();
             }
-            attr = PyStackRef_FromPyObjectSteal(attr_o);
-            self_or_null = self_or_null_o == NULL ?
-            PyStackRef_NULL : PyStackRef_FromPyObjectSteal(self_or_null_o);
-            stack_pointer[0] = attr;
-            stack_pointer[1] = self_or_null;
-            stack_pointer += 2;
-            assert(WITHIN_STACK_BOUNDS());
             break;
         }
 
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 911f5ae3e7c0d7..18785c7be4c4e2 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -9342,41 +9342,41 @@
             frame->instr_ptr = next_instr;
             next_instr += 1;
             INSTRUCTION_STATS(LOAD_SPECIAL);
-            _PyStackRef owner;
-            _PyStackRef attr;
-            _PyStackRef self_or_null;
-            owner = stack_pointer[-1];
-            assert(oparg <= SPECIAL_MAX);
-            PyObject *owner_o = PyStackRef_AsPyObjectSteal(owner);
-            PyObject *name = _Py_SpecialMethods[oparg].name;
-            PyObject *self_or_null_o;
-            stack_pointer += -1;
-            assert(WITHIN_STACK_BOUNDS());
-            _PyFrame_SetStackPointer(frame, stack_pointer);
-            PyObject *attr_o = _PyObject_LookupSpecialMethod(owner_o, name, 
&self_or_null_o);
-            stack_pointer = _PyFrame_GetStackPointer(frame);
-            if (attr_o == NULL) {
-                if (!_PyErr_Occurred(tstate)) {
-                    _PyFrame_SetStackPointer(frame, stack_pointer);
-                    const char *errfmt = 
_PyEval_SpecialMethodCanSuggest(owner_o, oparg)
-                    ? _Py_SpecialMethods[oparg].error_suggestion
-                : _Py_SpecialMethods[oparg].error;
-                    stack_pointer = _PyFrame_GetStackPointer(frame);
-                    assert(!_PyErr_Occurred(tstate));
-                    assert(errfmt != NULL);
-                    _PyFrame_SetStackPointer(frame, stack_pointer);
-                    _PyErr_Format(tstate, PyExc_TypeError, errfmt, owner_o);
-                    stack_pointer = _PyFrame_GetStackPointer(frame);
+            _PyStackRef self;
+            _PyStackRef *method_and_self;
+            // _INSERT_NULL
+            {
+                self = stack_pointer[-1];
+                method_and_self = &stack_pointer[-1];
+                method_and_self[1] = self;
+                method_and_self[0] = PyStackRef_NULL;
+            }
+            // _LOAD_SPECIAL
+            {
+                method_and_self = &stack_pointer[-1];
+                PyObject *name = _Py_SpecialMethods[oparg].name;
+                stack_pointer += 1;
+                assert(WITHIN_STACK_BOUNDS());
+                _PyFrame_SetStackPointer(frame, stack_pointer);
+                int err = _PyObject_LookupSpecialMethod(name, method_and_self);
+                stack_pointer = _PyFrame_GetStackPointer(frame);
+                if (err <= 0) {
+                    if (err == 0) {
+                        PyObject *owner = 
PyStackRef_AsPyObjectBorrow(method_and_self[1]);
+                        _PyFrame_SetStackPointer(frame, stack_pointer);
+                        const char *errfmt = 
_PyEval_SpecialMethodCanSuggest(owner, oparg)
+                        ? _Py_SpecialMethods[oparg].error_suggestion
+                    : _Py_SpecialMethods[oparg].error;
+                        stack_pointer = _PyFrame_GetStackPointer(frame);
+                        assert(!_PyErr_Occurred(tstate));
+                        assert(errfmt != NULL);
+                        _PyFrame_SetStackPointer(frame, stack_pointer);
+                        _PyErr_Format(tstate, PyExc_TypeError, errfmt, owner);
+                        stack_pointer = _PyFrame_GetStackPointer(frame);
+                    }
+                    JUMP_TO_LABEL(error);
                 }
-                JUMP_TO_LABEL(error);
             }
-            attr = PyStackRef_FromPyObjectSteal(attr_o);
-            self_or_null = self_or_null_o == NULL ?
-            PyStackRef_NULL : PyStackRef_FromPyObjectSteal(self_or_null_o);
-            stack_pointer[0] = attr;
-            stack_pointer[1] = self_or_null;
-            stack_pointer += 2;
-            assert(WITHIN_STACK_BOUNDS());
             DISPATCH();
         }
 
diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c
index 6f7f9b03d3bf06..9f1e3f6e1e2289 100644
--- a/Python/optimizer_bytecodes.c
+++ b/Python/optimizer_bytecodes.c
@@ -897,9 +897,14 @@ dummy_func(void) {
         }
     }
 
-    op(_LOAD_SPECIAL, (owner -- attr, self_or_null)) {
-        attr = sym_new_not_null(ctx);
-        self_or_null = sym_new_unknown(ctx);
+    op(_INSERT_NULL, (self -- method_and_self[2])) {
+        method_and_self[0] = sym_new_null(ctx);
+        method_and_self[1] = self;
+    }
+
+    op(_LOAD_SPECIAL, (method_and_self[2] -- method_and_self[2])) {
+        method_and_self[0] = sym_new_not_null(ctx);
+        method_and_self[1] = sym_new_unknown(ctx);
     }
 
     op(_JUMP_TO_TOP, (--)) {
diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h
index 983af081e2f8cd..28bca2a373ec2e 100644
--- a/Python/optimizer_cases.c.h
+++ b/Python/optimizer_cases.c.h
@@ -1553,18 +1553,26 @@
             break;
         }
 
-        case _LOAD_SPECIAL: {
-            JitOptSymbol *attr;
-            JitOptSymbol *self_or_null;
-            attr = sym_new_not_null(ctx);
-            self_or_null = sym_new_unknown(ctx);
-            stack_pointer[-1] = attr;
-            stack_pointer[0] = self_or_null;
+        case _INSERT_NULL: {
+            JitOptSymbol *self;
+            JitOptSymbol **method_and_self;
+            self = stack_pointer[-1];
+            method_and_self = &stack_pointer[-1];
+            method_and_self[0] = sym_new_null(ctx);
+            method_and_self[1] = self;
             stack_pointer += 1;
             assert(WITHIN_STACK_BOUNDS());
             break;
         }
 
+        case _LOAD_SPECIAL: {
+            JitOptSymbol **method_and_self;
+            method_and_self = &stack_pointer[-2];
+            method_and_self[0] = sym_new_not_null(ctx);
+            method_and_self[1] = sym_new_unknown(ctx);
+            break;
+        }
+
         case _WITH_EXCEPT_START: {
             JitOptSymbol *res;
             res = sym_new_not_null(ctx);
diff --git a/Tools/ftscalingbench/ftscalingbench.py 
b/Tools/ftscalingbench/ftscalingbench.py
index 364c465bc91b0b..926bc66b944c6f 100644
--- a/Tools/ftscalingbench/ftscalingbench.py
+++ b/Tools/ftscalingbench/ftscalingbench.py
@@ -65,6 +65,19 @@ def object_lookup_special():
     for i in range(N):
         round(i / N)
 
+class MyContextManager:
+    def __enter__(self):
+        pass
+    def __exit__(self, exc_type, exc_value, traceback):
+        pass
+
+@register_benchmark
+def context_manager():
+    N = 1000 * WORK_SCALE
+    for i in range(N):
+        with MyContextManager():
+            pass
+
 @register_benchmark
 def mult_constant():
     x = 1.0

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com

Reply via email to