Skip to content
Merged
20 changes: 10 additions & 10 deletions Include/internal/pycore_opcode_metadata.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 8 additions & 8 deletions Include/internal/pycore_uop_ids.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 7 additions & 7 deletions Include/internal/pycore_uop_metadata.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions Include/opcode_ids.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions Lib/_opcode_metadata.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

40 changes: 40 additions & 0 deletions Lib/test/test_capi/test_opt.py
Original file line number Diff line number Diff line change
Expand Up @@ -1904,6 +1904,26 @@ def testfunc(n):
self.assertNotIn("_GUARD_TOS_UNICODE", uops)
self.assertIn("_BINARY_OP_ADD_UNICODE", uops)

def test_binary_subcsr_ustr_int_narrows_to_str(self):
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought of paramterizing test_binary_subcsr_str_int_narrows_to_str and test_binary_op_subscr_str_int to get rid of duplication, but it resulted in worse readable code, because the input and the expectations have to be varied ...

def testfunc(n):
x = []
s = "바이트코f드_특수화"
for _ in range(n):
y = s[4] # _BINARY_OP_SUBSCR_USTR_INT
z = "bar" + y # (_GUARD_TOS_UNICODE) + _BINARY_OP_ADD_UNICODE
x.append(z)
return x

res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
self.assertEqual(res, ["barf"] * TIER2_THRESHOLD)
self.assertIsNotNone(ex)
uops = get_opnames(ex)
self.assertIn("_BINARY_OP_SUBSCR_USTR_INT", uops)
# _BINARY_OP_SUBSCR_USTR_INT narrows the result to 'str' so
# the unicode guard before _BINARY_OP_ADD_UNICODE is removed.
self.assertNotIn("_GUARD_TOS_UNICODE", uops)
self.assertIn("_BINARY_OP_ADD_UNICODE", uops)

def test_binary_op_subscr_str_int(self):
def testfunc(n):
x = 0
Expand All @@ -1924,6 +1944,26 @@ def testfunc(n):
self.assertLessEqual(count_ops(ex, "_POP_TOP"), 2)
self.assertLessEqual(count_ops(ex, "_POP_TOP_INT"), 1)

def test_binary_op_subscr_ustr_int(self):
def testfunc(n):
x = 0
s = "hello바"
for _ in range(n):
c = s[1] # _BINARY_OP_SUBSCR_USTR_INT
if c == 'e':
x += 1
return x

res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
self.assertEqual(res, TIER2_THRESHOLD)
self.assertIsNotNone(ex)
uops = get_opnames(ex)
self.assertIn("_BINARY_OP_SUBSCR_USTR_INT", uops)
self.assertIn("_COMPARE_OP_STR", uops)
self.assertIn("_POP_TOP_NOP", uops)
self.assertLessEqual(count_ops(ex, "_POP_TOP"), 2)
self.assertLessEqual(count_ops(ex, "_POP_TOP_INT"), 1)

def test_call_type_1_guards_removed(self):
def testfunc(n):
x = 0
Expand Down
1 change: 1 addition & 0 deletions Lib/test/test_opcache.py
Original file line number Diff line number Diff line change
Expand Up @@ -1792,6 +1792,7 @@ def binary_subscr_str_int_non_compact():
self.assertEqual(a[idx], expected)

binary_subscr_str_int_non_compact()
self.assert_specialized(binary_subscr_str_int_non_compact, "BINARY_OP_SUBSCR_USTR_INT")
self.assert_no_opcode(binary_subscr_str_int_non_compact, "BINARY_OP_SUBSCR_STR_INT")

def binary_subscr_getitems():
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Add ``BINARY_OP_SUBSCR_USTR_INT`` to specialize reading an ASCII character
from any string. Patch by Chris Eibl.
14 changes: 7 additions & 7 deletions Python/bytecodes.c
Original file line number Diff line number Diff line change
Expand Up @@ -594,7 +594,7 @@ dummy_func(
BINARY_OP_SUBSCR_LIST_SLICE,
BINARY_OP_SUBSCR_TUPLE_INT,
BINARY_OP_SUBSCR_STR_INT,
BINARY_OP_SUBSCR_NCSTR_INT,
BINARY_OP_SUBSCR_USTR_INT,
BINARY_OP_SUBSCR_DICT,
BINARY_OP_SUBSCR_GETITEM,
BINARY_OP_INPLACE_ADD_UNICODE,
Expand Down Expand Up @@ -943,15 +943,15 @@ dummy_func(
}

macro(BINARY_OP_SUBSCR_STR_INT) =
_GUARD_TOS_INT + _GUARD_NOS_COMPACT_ASCII + unused/5 + _BINARY_OP_SUBSCR_STR_INT + _POP_TOP_INT + POP_TOP;
_GUARD_TOS_INT + _GUARD_NOS_COMPACT_ASCII + unused/5 + _BINARY_OP_SUBSCR_STR_INT + _POP_TOP_INT + _POP_TOP_UNICODE;

op(_BINARY_OP_SUBSCR_STR_INT, (str_st, sub_st -- res, s, i)) {
PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st);
PyObject *str = PyStackRef_AsPyObjectBorrow(str_st);

assert(PyLong_CheckExact(sub));
assert(PyUnicode_CheckExact(str));
DEOPT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub));
assert(_PyLong_IsNonNegativeCompact((PyLongObject *)sub));
Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0];
DEOPT_IF(PyUnicode_GET_LENGTH(str) <= index);
uint8_t c = PyUnicode_1BYTE_DATA(str)[index];
Expand All @@ -964,16 +964,16 @@ dummy_func(
res = PyStackRef_FromPyObjectBorrow(res_o);
}

macro(BINARY_OP_SUBSCR_NCSTR_INT) =
_GUARD_TOS_INT + _GUARD_NOS_UNICODE + unused/5 + _BINARY_OP_SUBSCR_NCSTR_INT + _POP_TOP_INT + POP_TOP;
macro(BINARY_OP_SUBSCR_USTR_INT) =
_GUARD_TOS_INT + _GUARD_NOS_UNICODE + unused/5 + _BINARY_OP_SUBSCR_USTR_INT + _POP_TOP_INT + _POP_TOP_UNICODE;

op(_BINARY_OP_SUBSCR_NCSTR_INT, (str_st, sub_st -- res, s, i)) {
op(_BINARY_OP_SUBSCR_USTR_INT, (str_st, sub_st -- res, s, i)) {
PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st);
PyObject *str = PyStackRef_AsPyObjectBorrow(str_st);

assert(PyLong_CheckExact(sub));
assert(PyUnicode_CheckExact(str));
DEOPT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub));
assert(_PyLong_IsNonNegativeCompact((PyLongObject *)sub));
Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0];
DEOPT_IF(PyUnicode_GET_LENGTH(str) <= index);
// Specialize for reading an ASCII character from any string:
Expand Down
18 changes: 3 additions & 15 deletions Python/executor_cases.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading