diff --git a/Include/internal/pycore_uop_ids.h b/Include/internal/pycore_uop_ids.h index 481ec9307db3b5..322f3cc093f7bb 100644 --- a/Include/internal/pycore_uop_ids.h +++ b/Include/internal/pycore_uop_ids.h @@ -265,73 +265,74 @@ extern "C" { #define _MONITOR_JUMP_BACKWARD 481 #define _MONITOR_RESUME 482 #define _NOP NOP +#define _POP_CALL_TWO_LOAD_CONST_INLINE_BORROW 483 #define _POP_EXCEPT POP_EXCEPT -#define _POP_JUMP_IF_FALSE 483 -#define _POP_JUMP_IF_TRUE 484 +#define _POP_JUMP_IF_FALSE 484 +#define _POP_JUMP_IF_TRUE 485 #define _POP_TOP POP_TOP -#define _POP_TOP_LOAD_CONST_INLINE 485 -#define _POP_TOP_LOAD_CONST_INLINE_BORROW 486 -#define _POP_TWO_LOAD_CONST_INLINE_BORROW 487 +#define _POP_TOP_LOAD_CONST_INLINE 486 +#define _POP_TOP_LOAD_CONST_INLINE_BORROW 487 +#define _POP_TWO_LOAD_CONST_INLINE_BORROW 488 #define _PUSH_EXC_INFO PUSH_EXC_INFO -#define _PUSH_FRAME 488 +#define _PUSH_FRAME 489 #define _PUSH_NULL PUSH_NULL -#define _PUSH_NULL_CONDITIONAL 489 -#define _PY_FRAME_GENERAL 490 -#define _PY_FRAME_KW 491 -#define _QUICKEN_RESUME 492 -#define _REPLACE_WITH_TRUE 493 +#define _PUSH_NULL_CONDITIONAL 490 +#define _PY_FRAME_GENERAL 491 +#define _PY_FRAME_KW 492 +#define _QUICKEN_RESUME 493 +#define _REPLACE_WITH_TRUE 494 #define _RESUME_CHECK RESUME_CHECK #define _RETURN_GENERATOR RETURN_GENERATOR #define _RETURN_VALUE RETURN_VALUE -#define _SAVE_RETURN_OFFSET 494 -#define _SEND 495 -#define _SEND_GEN_FRAME 496 +#define _SAVE_RETURN_OFFSET 495 +#define _SEND 496 +#define _SEND_GEN_FRAME 497 #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 497 -#define _STORE_ATTR 498 -#define _STORE_ATTR_INSTANCE_VALUE 499 -#define _STORE_ATTR_SLOT 500 -#define _STORE_ATTR_WITH_HINT 501 +#define _START_EXECUTOR 498 +#define _STORE_ATTR 499 +#define _STORE_ATTR_INSTANCE_VALUE 500 +#define _STORE_ATTR_SLOT 501 +#define _STORE_ATTR_WITH_HINT 502 #define _STORE_DEREF STORE_DEREF -#define _STORE_FAST 502 -#define _STORE_FAST_0 503 -#define _STORE_FAST_1 504 -#define _STORE_FAST_2 505 -#define _STORE_FAST_3 506 -#define _STORE_FAST_4 507 -#define _STORE_FAST_5 508 -#define _STORE_FAST_6 509 -#define _STORE_FAST_7 510 +#define _STORE_FAST 503 +#define _STORE_FAST_0 504 +#define _STORE_FAST_1 505 +#define _STORE_FAST_2 506 +#define _STORE_FAST_3 507 +#define _STORE_FAST_4 508 +#define _STORE_FAST_5 509 +#define _STORE_FAST_6 510 +#define _STORE_FAST_7 511 #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 511 -#define _STORE_SUBSCR 512 -#define _STORE_SUBSCR_DICT 513 -#define _STORE_SUBSCR_LIST_INT 514 +#define _STORE_SLICE 512 +#define _STORE_SUBSCR 513 +#define _STORE_SUBSCR_DICT 514 +#define _STORE_SUBSCR_LIST_INT 515 #define _SWAP SWAP -#define _TIER2_RESUME_CHECK 515 -#define _TO_BOOL 516 +#define _TIER2_RESUME_CHECK 516 +#define _TO_BOOL 517 #define _TO_BOOL_BOOL TO_BOOL_BOOL #define _TO_BOOL_INT TO_BOOL_INT -#define _TO_BOOL_LIST 517 +#define _TO_BOOL_LIST 518 #define _TO_BOOL_NONE TO_BOOL_NONE -#define _TO_BOOL_STR 518 +#define _TO_BOOL_STR 519 #define _UNARY_INVERT UNARY_INVERT #define _UNARY_NEGATIVE UNARY_NEGATIVE #define _UNARY_NOT UNARY_NOT #define _UNPACK_EX UNPACK_EX -#define _UNPACK_SEQUENCE 519 -#define _UNPACK_SEQUENCE_LIST 520 -#define _UNPACK_SEQUENCE_TUPLE 521 -#define _UNPACK_SEQUENCE_TWO_TUPLE 522 +#define _UNPACK_SEQUENCE 520 +#define _UNPACK_SEQUENCE_LIST 521 +#define _UNPACK_SEQUENCE_TUPLE 522 +#define _UNPACK_SEQUENCE_TWO_TUPLE 523 #define _WITH_EXCEPT_START WITH_EXCEPT_START #define _YIELD_VALUE YIELD_VALUE -#define MAX_UOP_ID 522 +#define MAX_UOP_ID 523 #ifdef __cplusplus } diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index d90b074b409f88..b334dd840c8fac 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -305,6 +305,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_LOAD_CONST_INLINE_BORROW] = HAS_PURE_FLAG, [_POP_TOP_LOAD_CONST_INLINE_BORROW] = HAS_ESCAPES_FLAG | HAS_PURE_FLAG, [_POP_TWO_LOAD_CONST_INLINE_BORROW] = HAS_ESCAPES_FLAG | HAS_PURE_FLAG, + [_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW] = HAS_ESCAPES_FLAG | HAS_PURE_FLAG, [_CHECK_FUNCTION] = HAS_DEOPT_FLAG, [_START_EXECUTOR] = 0, [_MAKE_WARM] = 0, @@ -553,6 +554,7 @@ const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = { [_MAYBE_EXPAND_METHOD] = "_MAYBE_EXPAND_METHOD", [_MAYBE_EXPAND_METHOD_KW] = "_MAYBE_EXPAND_METHOD_KW", [_NOP] = "_NOP", + [_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW] = "_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW", [_POP_EXCEPT] = "_POP_EXCEPT", [_POP_TOP] = "_POP_TOP", [_POP_TOP_LOAD_CONST_INLINE] = "_POP_TOP_LOAD_CONST_INLINE", @@ -1192,6 +1194,8 @@ int _PyUop_num_popped(int opcode, int oparg) return 1; case _POP_TWO_LOAD_CONST_INLINE_BORROW: return 2; + case _POP_CALL_TWO_LOAD_CONST_INLINE_BORROW: + return 4; case _CHECK_FUNCTION: return 0; case _START_EXECUTOR: diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index 305c53ffd816de..f7a4200e1eec0e 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -1955,9 +1955,10 @@ def testfunc(n): self.assertEqual(res, TIER2_THRESHOLD) self.assertIsNotNone(ex) uops = get_opnames(ex) - self.assertIn("_CALL_ISINSTANCE", uops) + self.assertNotIn("_CALL_ISINSTANCE", uops) self.assertNotIn("_GUARD_THIRD_NULL", uops) self.assertNotIn("_GUARD_CALLABLE_ISINSTANCE", uops) + self.assertIn("_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW", uops) def test_call_list_append(self): def testfunc(n): @@ -1987,9 +1988,10 @@ def testfunc(n): self.assertEqual(res, TIER2_THRESHOLD) self.assertIsNotNone(ex) uops = get_opnames(ex) - self.assertIn("_CALL_ISINSTANCE", uops) + self.assertNotIn("_CALL_ISINSTANCE", uops) self.assertNotIn("_TO_BOOL_BOOL", uops) self.assertNotIn("_GUARD_IS_TRUE_POP", uops) + self.assertIn("_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW", uops) def test_call_isinstance_is_false(self): def testfunc(n): @@ -2004,9 +2006,10 @@ def testfunc(n): self.assertEqual(res, TIER2_THRESHOLD) self.assertIsNotNone(ex) uops = get_opnames(ex) - self.assertIn("_CALL_ISINSTANCE", uops) + self.assertNotIn("_CALL_ISINSTANCE", uops) self.assertNotIn("_TO_BOOL_BOOL", uops) self.assertNotIn("_GUARD_IS_FALSE_POP", uops) + self.assertIn("_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW", uops) def test_call_isinstance_subclass(self): def testfunc(n): @@ -2021,9 +2024,10 @@ def testfunc(n): self.assertEqual(res, TIER2_THRESHOLD) self.assertIsNotNone(ex) uops = get_opnames(ex) - self.assertIn("_CALL_ISINSTANCE", uops) + self.assertNotIn("_CALL_ISINSTANCE", uops) self.assertNotIn("_TO_BOOL_BOOL", uops) self.assertNotIn("_GUARD_IS_TRUE_POP", uops) + self.assertIn("_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW", uops) def test_call_isinstance_unknown_object(self): def testfunc(n): diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-19-20-52-53.gh-issue-134268.HPKX1e.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-19-20-52-53.gh-issue-134268.HPKX1e.rst new file mode 100644 index 00000000000000..98d770cf054926 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-19-20-52-53.gh-issue-134268.HPKX1e.rst @@ -0,0 +1,2 @@ +Add ``_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW`` and use it to further +optimize ``CALL_ISINSTANCE``. diff --git a/Python/bytecodes.c b/Python/bytecodes.c index c10327916fc867..477a84b170ab98 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -5342,6 +5342,15 @@ dummy_func( value = PyStackRef_FromPyObjectImmortal(ptr); } + tier2 pure op(_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW, (ptr/4, callable, null, pop1, pop2 -- value)) { + PyStackRef_CLOSE(pop2); + PyStackRef_CLOSE(pop1); + (void)null; // Silence compiler warnings about unused variables + DEAD(null); + PyStackRef_CLOSE(callable); + value = PyStackRef_FromPyObjectImmortal(ptr); + } + tier2 op(_CHECK_FUNCTION, (func_version/2 -- )) { assert(PyStackRef_FunctionCheck(frame->f_funcobj)); PyFunctionObject *func = (PyFunctionObject *)PyStackRef_AsPyObjectBorrow(frame->f_funcobj); diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index c5e481932e7b29..c3da7a63b9ed86 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -7103,6 +7103,40 @@ break; } + case _POP_CALL_TWO_LOAD_CONST_INLINE_BORROW: { + _PyStackRef pop2; + _PyStackRef pop1; + _PyStackRef null; + _PyStackRef callable; + _PyStackRef value; + pop2 = stack_pointer[-1]; + pop1 = stack_pointer[-2]; + null = stack_pointer[-3]; + callable = stack_pointer[-4]; + PyObject *ptr = (PyObject *)CURRENT_OPERAND0(); + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(pop2); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(pop1); + stack_pointer = _PyFrame_GetStackPointer(frame); + (void)null; + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(callable); + stack_pointer = _PyFrame_GetStackPointer(frame); + value = PyStackRef_FromPyObjectImmortal(ptr); + stack_pointer[0] = value; + stack_pointer += 1; + assert(WITHIN_STACK_BOUNDS()); + break; + } + case _CHECK_FUNCTION: { uint32_t func_version = (uint32_t)CURRENT_OPERAND0(); assert(PyStackRef_FunctionCheck(frame->f_funcobj)); diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index 708b436f6e4ae6..9811e9f348c2a0 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -550,6 +550,10 @@ dummy_func(void) { value = sym_new_const(ctx, ptr); } + op(_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW, (ptr/4, unused, unused, unused, unused -- value)) { + value = sym_new_const(ctx, ptr); + } + op(_COPY, (bottom, unused[oparg-1] -- bottom, unused[oparg-1], top)) { assert(oparg > 0); top = bottom; @@ -901,12 +905,12 @@ dummy_func(void) { // known types, meaning we can deduce either True or False // The below check is equivalent to PyObject_TypeCheck(inst, cls) + PyObject *out = Py_False; if (inst_type == cls_o || PyType_IsSubtype(inst_type, cls_o)) { - sym_set_const(res, Py_True); - } - else { - sym_set_const(res, Py_False); + out = Py_True; } + sym_set_const(res, out); + REPLACE_OP(this_instr, _POP_CALL_TWO_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)out); } } diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 3ac99e5c1b1707..6e19319fbaef4a 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -2143,12 +2143,12 @@ PyTypeObject *inst_type = sym_get_type(instance); PyTypeObject *cls_o = (PyTypeObject *)sym_get_const(ctx, cls); if (inst_type && cls_o && sym_matches_type(cls, &PyType_Type)) { + PyObject *out = Py_False; if (inst_type == cls_o || PyType_IsSubtype(inst_type, cls_o)) { - sym_set_const(res, Py_True); - } - else { - sym_set_const(res, Py_False); + out = Py_True; } + sym_set_const(res, out); + REPLACE_OP(this_instr, _POP_CALL_TWO_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)out); } stack_pointer[-4] = res; stack_pointer += -3; @@ -2556,6 +2556,16 @@ break; } + case _POP_CALL_TWO_LOAD_CONST_INLINE_BORROW: { + JitOptSymbol *value; + PyObject *ptr = (PyObject *)this_instr->operand0; + value = sym_new_const(ctx, ptr); + stack_pointer[-4] = value; + stack_pointer += -3; + assert(WITHIN_STACK_BOUNDS()); + break; + } + case _CHECK_FUNCTION: { break; }
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: