Skip to content

Commit 4e10fa9

Browse files
authored
gh-144007: Eliminate redundant refcounting in the JIT for BINARY_OP (GH-144011)
1 parent 29f1e77 commit 4e10fa9

File tree

11 files changed

+105
-48
lines changed

11 files changed

+105
-48
lines changed

Include/internal/pycore_opcode_metadata.h

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_uop_ids.h

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_uop_metadata.h

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

Lib/test/test_capi/test_opt.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2897,6 +2897,29 @@ def testfunc(n):
28972897
self.assertIn("_POP_TOP_NOP", uops)
28982898
self.assertLessEqual(count_ops(ex, "_POP_TOP"), 2)
28992899

2900+
def test_binary_op_refcount_elimination(self):
2901+
class CustomAdder:
2902+
def __init__(self, val):
2903+
self.val = val
2904+
def __add__(self, other):
2905+
return CustomAdder(self.val + other.val)
2906+
2907+
def testfunc(n):
2908+
a = CustomAdder(1)
2909+
b = CustomAdder(2)
2910+
res = None
2911+
for _ in range(n):
2912+
res = a + b
2913+
return res.val if res else 0
2914+
2915+
res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
2916+
self.assertEqual(res, 3)
2917+
self.assertIsNotNone(ex)
2918+
uops = get_opnames(ex)
2919+
self.assertIn("_BINARY_OP", uops)
2920+
self.assertIn("_POP_TOP_NOP", uops)
2921+
self.assertLessEqual(count_ops(ex, "_POP_TOP"), 2)
2922+
29002923
def test_binary_op_extend_float_long_add_refcount_elimination(self):
29012924
def testfunc(n):
29022925
a = 1.5
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Eliminate redundant refcounting in the JIT for ``BINARY_OP``.

Modules/_testinternalcapi/test_cases.c.h

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@
3232
_PyStackRef lhs;
3333
_PyStackRef rhs;
3434
_PyStackRef res;
35+
_PyStackRef l;
36+
_PyStackRef r;
37+
_PyStackRef value;
3538
// _SPECIALIZE_BINARY_OP
3639
{
3740
rhs = stack_pointer[-1];
@@ -65,18 +68,26 @@
6568
JUMP_TO_LABEL(error);
6669
}
6770
res = PyStackRef_FromPyObjectSteal(res_o);
71+
l = lhs;
72+
r = rhs;
73+
}
74+
// _POP_TOP
75+
{
76+
value = r;
77+
stack_pointer[-2] = res;
78+
stack_pointer[-1] = l;
6879
_PyFrame_SetStackPointer(frame, stack_pointer);
69-
_PyStackRef tmp = lhs;
70-
lhs = res;
71-
stack_pointer[-2] = lhs;
72-
PyStackRef_CLOSE(tmp);
73-
tmp = rhs;
74-
rhs = PyStackRef_NULL;
75-
stack_pointer[-1] = rhs;
76-
PyStackRef_CLOSE(tmp);
80+
PyStackRef_XCLOSE(value);
7781
stack_pointer = _PyFrame_GetStackPointer(frame);
82+
}
83+
// _POP_TOP
84+
{
85+
value = l;
7886
stack_pointer += -1;
7987
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
88+
_PyFrame_SetStackPointer(frame, stack_pointer);
89+
PyStackRef_XCLOSE(value);
90+
stack_pointer = _PyFrame_GetStackPointer(frame);
8091
}
8192
DISPATCH();
8293
}

Python/bytecodes.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5119,7 +5119,7 @@ dummy_func(
51195119
assert(oparg <= NB_OPARG_LAST);
51205120
}
51215121

5122-
op(_BINARY_OP, (lhs, rhs -- res)) {
5122+
op(_BINARY_OP, (lhs, rhs -- res, l, r)) {
51235123
PyObject *lhs_o = PyStackRef_AsPyObjectBorrow(lhs);
51245124
PyObject *rhs_o = PyStackRef_AsPyObjectBorrow(rhs);
51255125

@@ -5129,10 +5129,13 @@ dummy_func(
51295129
ERROR_NO_POP();
51305130
}
51315131
res = PyStackRef_FromPyObjectSteal(res_o);
5132-
DECREF_INPUTS();
5132+
l = lhs;
5133+
r = rhs;
5134+
DEAD(lhs);
5135+
DEAD(rhs);
51335136
}
51345137

5135-
macro(BINARY_OP) = _SPECIALIZE_BINARY_OP + unused/4 + _BINARY_OP;
5138+
macro(BINARY_OP) = _SPECIALIZE_BINARY_OP + unused/4 + _BINARY_OP + POP_TOP + POP_TOP;
51365139

51375140
pure replicate(2:4) inst(SWAP, (bottom, unused[oparg-2], top --
51385141
bottom, unused[oparg-2], top)) {

Python/executor_cases.c.h

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

Python/generated_cases.c.h

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

Python/optimizer_bytecodes.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,9 @@ dummy_func(void) {
211211
sym_set_type(left, &PyFloat_Type);
212212
}
213213

214-
op(_BINARY_OP, (lhs, rhs -- res)) {
214+
op(_BINARY_OP, (lhs, rhs -- res, l, r)) {
215+
l = lhs;
216+
r = rhs;
215217
REPLACE_OPCODE_IF_EVALUATES_PURE(lhs, rhs, res);
216218
bool lhs_int = sym_matches_type(lhs, &PyLong_Type);
217219
bool rhs_int = sym_matches_type(rhs, &PyLong_Type);

0 commit comments

Comments
 (0)