Skip to content

Commit 5b8d455

Browse files
Zheaolireidenong
authored andcommitted
pythongh-134584: Eliminate redundant refcounting from _CONTAINS_{OP|OP_SET|OP_DICT} (pythonGH-143731)
Signed-off-by: Manjusaka <me@manjusaka.me>
1 parent 93d59b1 commit 5b8d455

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
@@ -1997,6 +1997,57 @@ def testfunc(n):
19971997
self.assertIn("_BINARY_OP_SUBSCR_DICT", uops)
19981998
self.assertLessEqual(count_ops(ex, "_POP_TOP"), 2)
19991999

2000+
def test_contains_op(self):
2001+
def testfunc(n):
2002+
x = 0
2003+
items = [1, 2, 3]
2004+
for _ in range(n):
2005+
if 2 in items:
2006+
x += 1
2007+
return x
2008+
2009+
res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
2010+
self.assertEqual(res, TIER2_THRESHOLD)
2011+
self.assertIsNotNone(ex)
2012+
uops = get_opnames(ex)
2013+
self.assertIn("_CONTAINS_OP", uops)
2014+
self.assertIn("_POP_TOP_NOP", uops)
2015+
self.assertLessEqual(count_ops(ex, "_POP_TOP"), 2)
2016+
2017+
def test_contains_op_set(self):
2018+
def testfunc(n):
2019+
x = 0
2020+
s = {1, 2, 3}
2021+
for _ in range(n):
2022+
if 2 in s:
2023+
x += 1
2024+
return x
2025+
2026+
res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
2027+
self.assertEqual(res, TIER2_THRESHOLD)
2028+
self.assertIsNotNone(ex)
2029+
uops = get_opnames(ex)
2030+
self.assertIn("_CONTAINS_OP_SET", uops)
2031+
self.assertIn("_POP_TOP_NOP", uops)
2032+
self.assertLessEqual(count_ops(ex, "_POP_TOP"), 2)
2033+
2034+
def test_contains_op_dict(self):
2035+
def testfunc(n):
2036+
x = 0
2037+
d = {'a': 1, 'b': 2}
2038+
for _ in range(n):
2039+
if 'a' in d:
2040+
x += 1
2041+
return x
2042+
2043+
res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
2044+
self.assertEqual(res, TIER2_THRESHOLD)
2045+
self.assertIsNotNone(ex)
2046+
uops = get_opnames(ex)
2047+
self.assertIn("_CONTAINS_OP_DICT", uops)
2048+
self.assertIn("_POP_TOP_NOP", uops)
2049+
self.assertLessEqual(count_ops(ex, "_POP_TOP"), 2)
2050+
20002051
def test_call_type_1_guards_removed(self):
20012052
def testfunc(n):
20022053
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
@@ -2852,14 +2852,18 @@ dummy_func(
28522852
CONTAINS_OP_DICT,
28532853
};
28542854

2855-
op(_CONTAINS_OP, (left, right -- b)) {
2855+
op(_CONTAINS_OP, (left, right -- b, l, r)) {
28562856
PyObject *left_o = PyStackRef_AsPyObjectBorrow(left);
28572857
PyObject *right_o = PyStackRef_AsPyObjectBorrow(right);
28582858

28592859
int res = PySequence_Contains(right_o, left_o);
2860-
DECREF_INPUTS();
2861-
ERROR_IF(res < 0);
2860+
if (res < 0) {
2861+
ERROR_NO_POP();
2862+
}
28622863
b = (res ^ oparg) ? PyStackRef_True : PyStackRef_False;
2864+
l = left;
2865+
r = right;
2866+
INPUTS_DEAD();
28632867
}
28642868

28652869
specializing op(_SPECIALIZE_CONTAINS_OP, (counter/1, left, right -- left, right)) {
@@ -2874,40 +2878,48 @@ dummy_func(
28742878
#endif /* ENABLE_SPECIALIZATION_FT */
28752879
}
28762880

2877-
macro(CONTAINS_OP) = _SPECIALIZE_CONTAINS_OP + _CONTAINS_OP;
2881+
macro(CONTAINS_OP) = _SPECIALIZE_CONTAINS_OP + _CONTAINS_OP + POP_TOP + POP_TOP;
28782882

28792883
op(_GUARD_TOS_ANY_SET, (tos -- tos)) {
28802884
PyObject *o = PyStackRef_AsPyObjectBorrow(tos);
28812885
DEOPT_IF(!PyAnySet_CheckExact(o));
28822886
}
28832887

2884-
macro(CONTAINS_OP_SET) = _GUARD_TOS_ANY_SET + unused/1 + _CONTAINS_OP_SET;
2888+
macro(CONTAINS_OP_SET) = _GUARD_TOS_ANY_SET + unused/1 + _CONTAINS_OP_SET + POP_TOP + POP_TOP;
28852889

2886-
op(_CONTAINS_OP_SET, (left, right -- b)) {
2890+
op(_CONTAINS_OP_SET, (left, right -- b, l, r)) {
28872891
PyObject *left_o = PyStackRef_AsPyObjectBorrow(left);
28882892
PyObject *right_o = PyStackRef_AsPyObjectBorrow(right);
28892893

28902894
assert(PyAnySet_CheckExact(right_o));
28912895
STAT_INC(CONTAINS_OP, hit);
28922896
// Note: both set and frozenset use the same seq_contains method!
28932897
int res = _PySet_Contains((PySetObject *)right_o, left_o);
2894-
DECREF_INPUTS();
2895-
ERROR_IF(res < 0);
2898+
if (res < 0) {
2899+
ERROR_NO_POP();
2900+
}
28962901
b = (res ^ oparg) ? PyStackRef_True : PyStackRef_False;
2902+
l = left;
2903+
r = right;
2904+
INPUTS_DEAD();
28972905
}
28982906

2899-
macro(CONTAINS_OP_DICT) = _GUARD_TOS_DICT + unused/1 + _CONTAINS_OP_DICT;
2907+
macro(CONTAINS_OP_DICT) = _GUARD_TOS_DICT + unused/1 + _CONTAINS_OP_DICT + POP_TOP + POP_TOP;
29002908

2901-
op(_CONTAINS_OP_DICT, (left, right -- b)) {
2909+
op(_CONTAINS_OP_DICT, (left, right -- b, l, r)) {
29022910
PyObject *left_o = PyStackRef_AsPyObjectBorrow(left);
29032911
PyObject *right_o = PyStackRef_AsPyObjectBorrow(right);
29042912

29052913
assert(PyDict_CheckExact(right_o));
29062914
STAT_INC(CONTAINS_OP, hit);
29072915
int res = PyDict_Contains(right_o, left_o);
2908-
DECREF_INPUTS();
2909-
ERROR_IF(res < 0);
2916+
if (res < 0) {
2917+
ERROR_NO_POP();
2918+
}
29102919
b = (res ^ oparg) ? PyStackRef_True : PyStackRef_False;
2920+
l = left;
2921+
r = right;
2922+
INPUTS_DEAD();
29112923
}
29122924

29132925
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)