Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
6 changes: 6 additions & 0 deletions Include/internal/pycore_optimizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,12 @@ PyJitRef_Wrap(JitOptSymbol *sym)
return (JitOptRef){.bits=(uintptr_t)sym};
}

static inline JitOptRef
PyJitRef_StripReferenceInfo(JitOptRef ref)
{
return PyJitRef_Wrap(PyJitRef_Unwrap(ref));
}

static inline JitOptRef
PyJitRef_Borrow(JitOptRef ref)
{
Expand Down
49 changes: 49 additions & 0 deletions Lib/test/test_capi/test_opt.py
Original file line number Diff line number Diff line change
Expand Up @@ -2501,6 +2501,55 @@ def testfunc(n):
# For now... until we constant propagate it away.
self.assertIn("_BINARY_OP", uops)

def test_strip_reference_information_through_str(self):
# This test needs to be in another script due to Python's
# string interning details.
script_helper.assert_python_ok('-c', textwrap.dedent("""
import _testinternalcapi
import _opcode

def get_first_executor(func):
code = func.__code__
co_code = code.co_code
for i in range(0, len(co_code), 2):
try:
return _opcode.get_executor(code, i)
except ValueError:
pass
return None

def iter_opnames(ex):
for item in ex:
yield item[0]

def get_opnames(ex):
return list(iter_opnames(ex))

def testfunc(n):
for _ in range(n):
str("abcde")


testfunc(_testinternalcapi.TIER2_THRESHOLD)
ex = get_first_executor(testfunc)
assert ex is not None
uops = get_opnames(ex)
assert "_POP_TOP_NOP" not in uops
"""))

def test_strip_reference_information_through_tuple(self):
def testfunc(n):
for _ in range(n):
tuple((1,))

testfunc(TIER2_THRESHOLD * 2)

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

self.assertNotIn("_POP_TOP_NOP", uops)


def global_identity(x):
return x
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix a bug in the JIT optimizer when round-tripping strings and tuples.
13 changes: 8 additions & 5 deletions Python/optimizer_bytecodes.c
Original file line number Diff line number Diff line change
Expand Up @@ -762,9 +762,8 @@ dummy_func(void) {
}

op(_RETURN_VALUE, (retval -- res)) {
// We wrap and unwrap the value to mimic PyStackRef_MakeHeapSafe
// in bytecodes.c
JitOptRef temp = PyJitRef_Wrap(PyJitRef_Unwrap(retval));
// Mimics PyStackRef_MakeHeapSafe in the interpreter.
JitOptRef temp = PyJitRef_StripReferenceInfo(retval);
DEAD(retval);
SAVE_STACK();
ctx->frame->stack_pointer = stack_pointer;
Expand Down Expand Up @@ -925,7 +924,9 @@ dummy_func(void) {
op(_CALL_STR_1, (unused, unused, arg -- res)) {
if (sym_matches_type(arg, &PyUnicode_Type)) {
// e.g. str('foo') or str(foo) where foo is known to be a string
res = arg;
// Note: we must strip the reference information because it goes
// through str() which strips the reference information from it.
res = PyJitRef_StripReferenceInfo(arg);
}
else {
res = sym_new_type(ctx, &PyUnicode_Type);
Expand Down Expand Up @@ -1065,7 +1066,9 @@ dummy_func(void) {
op(_CALL_TUPLE_1, (callable, null, arg -- res)) {
if (sym_matches_type(arg, &PyTuple_Type)) {
// e.g. tuple((1, 2)) or tuple(foo) where foo is known to be a tuple
res = arg;
// Note: we must strip the reference information because it goes
// through tuple() which strips the reference information from it.
res = PyJitRef_StripReferenceInfo(arg);
}
else {
res = sym_new_type(ctx, &PyTuple_Type);
Expand Down
6 changes: 3 additions & 3 deletions Python/optimizer_cases.c.h

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

Loading