Skip to content

Commit 0c48c45

Browse files
Strength reduce _LOAD_FAST{_BORROW} to _LOAD_CONST_INLINE_BORROW
1 parent 450e836 commit 0c48c45

File tree

3 files changed

+54
-0
lines changed

3 files changed

+54
-0
lines changed

Lib/test/test_capi/test_opt.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3114,6 +3114,40 @@ def testfunc(n):
31143114
self.assertNotIn("_POP_TOP_INT", uops)
31153115
self.assertIn("_POP_TOP_NOP", uops)
31163116

3117+
def test_strength_reduce_constant_load_fast(self):
3118+
# If we detect a _LOAD_FAST is actually loading an immortal constant,
3119+
# reduce that to a _LOAD_CONST_INLINE_BORROW which saves
3120+
# the read from locals.
3121+
def testfunc(n):
3122+
for _ in range(n):
3123+
x = 0
3124+
y = x
3125+
3126+
_, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
3127+
self.assertIsNotNone(ex)
3128+
uops = get_opnames(ex)
3129+
3130+
self.assertIn("_LOAD_CONST_INLINE_BORROW", uops)
3131+
self.assertNotIn("_LOAD_FAST_2", uops)
3132+
3133+
def test_strength_reduce_constant_load_fast_borrow(self):
3134+
# If we detect a _LOAD_FAST_BORROW is actually loading a constant,
3135+
# reduce that to a _LOAD_CONST_INLINE_BORROW which saves
3136+
# the read from locals.
3137+
def testfunc(n):
3138+
class A: pass
3139+
a = A()
3140+
for _ in range(n):
3141+
x = 0
3142+
a.x = x
3143+
3144+
_, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
3145+
self.assertIsNotNone(ex)
3146+
uops = get_opnames(ex)
3147+
3148+
self.assertIn("_LOAD_CONST_INLINE_BORROW", uops)
3149+
self.assertNotIn("_LOAD_FAST_BORROW_4", uops)
3150+
31173151
def test_143026(self):
31183152
# https://github.com/python/cpython/issues/143026
31193153

Python/optimizer_bytecodes.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,22 @@ dummy_func(void) {
8787

8888
op(_LOAD_FAST, (-- value)) {
8989
value = GETLOCAL(oparg);
90+
PyObject *const_val = sym_get_const(ctx, value);
91+
if (const_val != NULL && _Py_IsImmortal(const_val)) {
92+
// Note: non-immortal is not safe to replace
93+
// to _LOAD_CONST_INLINE, as it might not be held in co_const.
94+
REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)const_val);
95+
}
9096
}
9197

9298
op(_LOAD_FAST_BORROW, (-- value)) {
9399
value = PyJitRef_Borrow(GETLOCAL(oparg));
100+
PyObject *const_val = sym_get_const(ctx, value);
101+
if (const_val != NULL) {
102+
// It's safe to always borrow here, because
103+
// _LOAD_FAST_BORROW guarantees it.
104+
REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)const_val);
105+
}
94106
}
95107

96108
op(_LOAD_FAST_AND_CLEAR, (-- value)) {

Python/optimizer_cases.c.h

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)