Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Include/internal/pycore_opcode_metadata.h

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

90 changes: 47 additions & 43 deletions Include/internal/pycore_uop_ids.h

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

16 changes: 16 additions & 0 deletions Include/internal/pycore_uop_metadata.h

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

19 changes: 19 additions & 0 deletions Lib/test/test_capi/test_opt.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import unittest
import gc
import os
import random
import string

import _opcode

Expand Down Expand Up @@ -2362,6 +2364,23 @@ def testfunc(n):
self.assertNotIn("_GUARD_TOS_INT", uops)
self.assertNotIn("_GUARD_NOS_INT", uops)

def test_store_fast_pop_top_specialize_unicode(self):
def random_str(n):
return ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(n))
def testfunc(n):
y = random_str(32)
for _ in range(n):
x = y + y # _POP_TOP
x = None # _POP_TOP_NOP

testfunc(TIER2_THRESHOLD)

ex = get_first_executor(testfunc)
self.assertIsNotNone(ex)
uops = get_opnames(ex)

self.assertIn("_POP_TOP_NOP", uops)
Copy link
Member

Choose a reason for hiding this comment

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

Maybe also add

self.assertIn("_BINARY_OP_ADD_UNICODE", uops)

to make sure this is the op that's actually generated?

Should we also test for _POP_TOP_UNICODE?

Copy link
Member Author

Choose a reason for hiding this comment

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

In this code, It will be replaced to _POP_TOP_NOP because _POP_TOP_UNICODE will be optimized to _POP_TOP_NOP .

Copy link
Member

Choose a reason for hiding this comment

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

Yup, do you think it makes sense to test _POP_TOP_UNICODE separately? That is, the case when it is not optimized to _POP_TOP_NOP? (not sure how difficult it'll be to come up with a test case though)

Copy link
Member

Choose a reason for hiding this comment

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

I have a test for that in my original PR already.


Copy link
Member

@Fidget-Spinner Fidget-Spinner Jun 22, 2025

Choose a reason for hiding this comment

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

Sorry I thought of a better test: you can use this one but change for str, and assert that the _POP_TOP_UNICODE uop is inside

def test_float_op_refcount_elimination(self):

def test_attr_promotion_failure(self):
# We're not testing for any specific uops here, just
# testing it doesn't crash.
Expand Down
4 changes: 2 additions & 2 deletions Lib/test/test_sys.py
Original file line number Diff line number Diff line change
Expand Up @@ -1671,7 +1671,7 @@ def func():
INTERPRETER_FRAME = '9PihcP'
else:
INTERPRETER_FRAME = '9PhcP'
check(x, size('3PiccPPP' + INTERPRETER_FRAME + 'P'))
check(x, size('3PiccPPP' + INTERPRETER_FRAME + 'PP'))
# function
def func(): pass
check(func, size('16Pi'))
Expand All @@ -1688,7 +1688,7 @@ def bar(cls):
check(bar, size('PP'))
# generator
def get_gen(): yield 1
check(get_gen(), size('6P4c' + INTERPRETER_FRAME + 'P'))
check(get_gen(), size('6P4c' + INTERPRETER_FRAME + 'PP'))
# iterator
check(iter('abc'), size('lP'))
# callable-iterator
Expand Down
2 changes: 1 addition & 1 deletion Programs/test_frozenmain.h

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

30 changes: 26 additions & 4 deletions Python/bytecodes.c
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,28 @@ dummy_func(
PyStackRef_XCLOSE(value);
}


op(_POP_TOP_NOP, (value --)) {
assert(PyStackRef_IsNull(value) || (!PyStackRef_RefcountOnObject(value)) ||
_Py_IsImmortal((PyStackRef_AsPyObjectBorrow(value))));
DEAD(value);
}

op(_POP_TOP_INT, (value --)) {
assert(PyLong_CheckExact(PyStackRef_AsPyObjectBorrow(value)));
PyStackRef_CLOSE_SPECIALIZED(value, _PyLong_ExactDealloc);
}

op(_POP_TOP_FLOAT, (value --)) {
assert(PyFloat_CheckExact(PyStackRef_AsPyObjectBorrow(value)));
PyStackRef_CLOSE_SPECIALIZED(value, _PyFloat_ExactDealloc);
}

op(_POP_TOP_UNICODE, (value --)) {
assert(PyUnicode_CheckExact(PyStackRef_AsPyObjectBorrow(value)));
PyStackRef_CLOSE_SPECIALIZED(value, _PyUnicode_ExactDealloc);
}

tier2 op(_POP_TWO, (nos, tos --)) {
PyStackRef_CLOSE(tos);
PyStackRef_CLOSE(nos);
Expand Down Expand Up @@ -751,23 +773,23 @@ dummy_func(
macro(BINARY_OP_SUBTRACT_FLOAT) =
_GUARD_TOS_FLOAT + _GUARD_NOS_FLOAT + unused/5 + _BINARY_OP_SUBTRACT_FLOAT;

pure op(_BINARY_OP_ADD_UNICODE, (left, right -- res)) {
pure op(_BINARY_OP_ADD_UNICODE, (left, right -- res, l, r)) {
PyObject *left_o = PyStackRef_AsPyObjectBorrow(left);
PyObject *right_o = PyStackRef_AsPyObjectBorrow(right);
assert(PyUnicode_CheckExact(left_o));
assert(PyUnicode_CheckExact(right_o));

STAT_INC(BINARY_OP, hit);
PyObject *res_o = PyUnicode_Concat(left_o, right_o);
PyStackRef_CLOSE_SPECIALIZED(right, _PyUnicode_ExactDealloc);
PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc);
INPUTS_DEAD();
ERROR_IF(res_o == NULL);
l = left;
r = right;
res = PyStackRef_FromPyObjectSteal(res_o);
}

macro(BINARY_OP_ADD_UNICODE) =
_GUARD_TOS_UNICODE + _GUARD_NOS_UNICODE + unused/5 + _BINARY_OP_ADD_UNICODE;
_GUARD_TOS_UNICODE + _GUARD_NOS_UNICODE + unused/5 + _BINARY_OP_ADD_UNICODE + _POP_TOP_UNICODE + _POP_TOP_UNICODE;

// This is a subtle one. It's a super-instruction for
// BINARY_OP_ADD_UNICODE followed by STORE_FAST
Expand Down
4 changes: 4 additions & 0 deletions Python/compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -1431,6 +1431,10 @@ optimize_and_assemble_code_unit(struct compiler_unit *u, PyObject *const_cache,
&optimized_instrs) < 0) {
goto error;
}
/* Reserve an extra word on the stack to ensure there is space for uops to
pass at least one item on the stack to a subsequent uop.
*/
stackdepth++;

/** Assembly **/
co = _PyAssemble_MakeCodeObject(&u->u_metadata, const_cache, consts,
Expand Down
52 changes: 49 additions & 3 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