From 786fa89133e359477b69f985ce942dc469f0b6bf Mon Sep 17 00:00:00 2001 From: Ken Jin Date: Fri, 9 Jan 2026 23:43:27 +0000 Subject: [PATCH] Constant fold LOAD_FAST_BORROW --- Lib/test/test_capi/test_opt.py | 18 ++++++++++++++++++ Python/optimizer_bytecodes.c | 10 ++++++++++ Python/optimizer_cases.c.h | 4 ++++ 3 files changed, 32 insertions(+) diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index 834a3d4b0a4408..d684ddbfa8838b 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -3457,6 +3457,24 @@ def testfunc(n): # _POP_TOP_NOP is a sign the optimizer ran and didn't hit bottom. self.assertGreaterEqual(count_ops(ex, "_POP_TOP_NOP"), 1) + def test_strength_reduce_constant_load_fast_borrow(self): + # If we detect a _LOAD_FAST_BORROW is actually loading a constant, + # reduce that to a _LOAD_CONST_INLINE_BORROW which saves + # the read from locals. + def testfunc(n): + class A: pass + a = A() + for _ in range(n): + x = 0 + a.x = x + + _, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + + self.assertIn("_LOAD_CONST_INLINE_BORROW", uops) + self.assertNotIn("_LOAD_FAST_BORROW_4", uops) + def test_143026(self): # https://github.com/python/cpython/issues/143026 diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index d8868dc020787d..d80d7ba07ce0f0 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -92,6 +92,16 @@ dummy_func(void) { op(_LOAD_FAST_BORROW, (-- value)) { value = PyJitRef_Borrow(GETLOCAL(oparg)); + PyObject *const_val = sym_get_const(ctx, value); + if (const_val != NULL) { + // If we know we're loading a constant, convert + // to a _LOAD_CONST_INLINE_BORROW to save a memory load. + // It's safe to always borrow here, because + // JIT constants only come from _LOAD_CONST + // (which holds a strong reference), or from + // constant-folded _JIT values, which are immortal. + REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)const_val); + } } op(_LOAD_FAST_AND_CLEAR, (-- value)) { diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 845d2f235aeaf8..a66211f655cbfa 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -53,6 +53,10 @@ case _LOAD_FAST_BORROW: { JitOptRef value; value = PyJitRef_Borrow(GETLOCAL(oparg)); + PyObject *const_val = sym_get_const(ctx, value); + if (const_val != NULL) { + REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)const_val); + } CHECK_STACK_BOUNDS(1); stack_pointer[0] = value; stack_pointer += 1;