Skip to content

Commit e535bdb

Browse files
authored
gh-134584: Eliminate redundant refcounting from _CONTAINS_{OP|OP_SET|OP_DICT} (GH-143731)
Signed-off-by: Manjusaka <[email protected]>
1 parent c315748 commit e535bdb

File tree

10 files changed

+253
-130
lines changed

10 files changed

+253
-130
lines changed

Include/internal/pycore_opcode_metadata.h

Lines changed: 6 additions & 6 deletions
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: 3 additions & 3 deletions
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: 12 additions & 12 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: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1992,6 +1992,57 @@ def testfunc(n):
19921992
self.assertIn("_BINARY_OP_SUBSCR_DICT", uops)
19931993
self.assertLessEqual(count_ops(ex, "_POP_TOP"), 2)
19941994

1995+
def test_contains_op(self):
1996+
def testfunc(n):
1997+
x = 0
1998+
items = [1, 2, 3]
1999+
for _ in range(n):
2000+
if 2 in items:
2001+
x += 1
2002+
return x
2003+
2004+
res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
2005+
self.assertEqual(res, TIER2_THRESHOLD)
2006+
self.assertIsNotNone(ex)
2007+
uops = get_opnames(ex)
2008+
self.assertIn("_CONTAINS_OP", uops)
2009+
self.assertIn("_POP_TOP_NOP", uops)
2010+
self.assertLessEqual(count_ops(ex, "_POP_TOP"), 2)
2011+
2012+
def test_contains_op_set(self):
2013+
def testfunc(n):
2014+
x = 0
2015+
s = {1, 2, 3}
2016+
for _ in range(n):
2017+
if 2 in s:
2018+
x += 1
2019+
return x
2020+
2021+
res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
2022+
self.assertEqual(res, TIER2_THRESHOLD)
2023+
self.assertIsNotNone(ex)
2024+
uops = get_opnames(ex)
2025+
self.assertIn("_CONTAINS_OP_SET", uops)
2026+
self.assertIn("_POP_TOP_NOP", uops)
2027+
self.assertLessEqual(count_ops(ex, "_POP_TOP"), 2)
2028+
2029+
def test_contains_op_dict(self):
2030+
def testfunc(n):
2031+
x = 0
2032+
d = {'a': 1, 'b': 2}
2033+
for _ in range(n):
2034+
if 'a' in d:
2035+
x += 1
2036+
return x
2037+
2038+
res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
2039+
self.assertEqual(res, TIER2_THRESHOLD)
2040+
self.assertIsNotNone(ex)
2041+
uops = get_opnames(ex)
2042+
self.assertIn("_CONTAINS_OP_DICT", uops)
2043+
self.assertIn("_POP_TOP_NOP", uops)
2044+
self.assertLessEqual(count_ops(ex, "_POP_TOP"), 2)
2045+
19952046
def test_call_type_1_guards_removed(self):
19962047
def testfunc(n):
19972048
x = 0
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Eliminate redundant refcounting from ``_CONTAINS_OP``, ``_CONTAINS_OP_SET``
2+
and ``_CONTAINS_OP_DICT``.

Python/bytecodes.c

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2855,14 +2855,18 @@ dummy_func(
28552855
CONTAINS_OP_DICT,
28562856
};
28572857

2858-
op(_CONTAINS_OP, (left, right -- b)) {
2858+
op(_CONTAINS_OP, (left, right -- b, l, r)) {
28592859
PyObject *left_o = PyStackRef_AsPyObjectBorrow(left);
28602860
PyObject *right_o = PyStackRef_AsPyObjectBorrow(right);
28612861

28622862
int res = PySequence_Contains(right_o, left_o);
2863-
DECREF_INPUTS();
2864-
ERROR_IF(res < 0);
2863+
if (res < 0) {
2864+
ERROR_NO_POP();
2865+
}
28652866
b = (res ^ oparg) ? PyStackRef_True : PyStackRef_False;
2867+
l = left;
2868+
r = right;
2869+
INPUTS_DEAD();
28662870
}
28672871

28682872
specializing op(_SPECIALIZE_CONTAINS_OP, (counter/1, left, right -- left, right)) {
@@ -2877,40 +2881,48 @@ dummy_func(
28772881
#endif /* ENABLE_SPECIALIZATION_FT */
28782882
}
28792883

2880-
macro(CONTAINS_OP) = _SPECIALIZE_CONTAINS_OP + _CONTAINS_OP;
2884+
macro(CONTAINS_OP) = _SPECIALIZE_CONTAINS_OP + _CONTAINS_OP + POP_TOP + POP_TOP;
28812885

28822886
op(_GUARD_TOS_ANY_SET, (tos -- tos)) {
28832887
PyObject *o = PyStackRef_AsPyObjectBorrow(tos);
28842888
DEOPT_IF(!PyAnySet_CheckExact(o));
28852889
}
28862890

2887-
macro(CONTAINS_OP_SET) = _GUARD_TOS_ANY_SET + unused/1 + _CONTAINS_OP_SET;
2891+
macro(CONTAINS_OP_SET) = _GUARD_TOS_ANY_SET + unused/1 + _CONTAINS_OP_SET + POP_TOP + POP_TOP;
28882892

2889-
op(_CONTAINS_OP_SET, (left, right -- b)) {
2893+
op(_CONTAINS_OP_SET, (left, right -- b, l, r)) {
28902894
PyObject *left_o = PyStackRef_AsPyObjectBorrow(left);
28912895
PyObject *right_o = PyStackRef_AsPyObjectBorrow(right);
28922896

28932897
assert(PyAnySet_CheckExact(right_o));
28942898
STAT_INC(CONTAINS_OP, hit);
28952899
// Note: both set and frozenset use the same seq_contains method!
28962900
int res = _PySet_Contains((PySetObject *)right_o, left_o);
2897-
DECREF_INPUTS();
2898-
ERROR_IF(res < 0);
2901+
if (res < 0) {
2902+
ERROR_NO_POP();
2903+
}
28992904
b = (res ^ oparg) ? PyStackRef_True : PyStackRef_False;
2905+
l = left;
2906+
r = right;
2907+
INPUTS_DEAD();
29002908
}
29012909

2902-
macro(CONTAINS_OP_DICT) = _GUARD_TOS_DICT + unused/1 + _CONTAINS_OP_DICT;
2910+
macro(CONTAINS_OP_DICT) = _GUARD_TOS_DICT + unused/1 + _CONTAINS_OP_DICT + POP_TOP + POP_TOP;
29032911

2904-
op(_CONTAINS_OP_DICT, (left, right -- b)) {
2912+
op(_CONTAINS_OP_DICT, (left, right -- b, l, r)) {
29052913
PyObject *left_o = PyStackRef_AsPyObjectBorrow(left);
29062914
PyObject *right_o = PyStackRef_AsPyObjectBorrow(right);
29072915

29082916
assert(PyDict_CheckExact(right_o));
29092917
STAT_INC(CONTAINS_OP, hit);
29102918
int res = PyDict_Contains(right_o, left_o);
2911-
DECREF_INPUTS();
2912-
ERROR_IF(res < 0);
2919+
if (res < 0) {
2920+
ERROR_NO_POP();
2921+
}
29132922
b = (res ^ oparg) ? PyStackRef_True : PyStackRef_False;
2923+
l = left;
2924+
r = right;
2925+
INPUTS_DEAD();
29142926
}
29152927

29162928
inst(CHECK_EG_MATCH, (exc_value_st, match_type_st -- rest, match)) {

Python/executor_cases.c.h

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

0 commit comments

Comments
 (0)