Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
4 changes: 2 additions & 2 deletions Include/internal/pycore_opcode_metadata.h

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

1 change: 0 additions & 1 deletion Include/internal/pycore_tstate.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ typedef struct _PyJitTracerInitialState {

typedef struct _PyJitTracerPreviousState {
bool dependencies_still_valid;
bool instr_is_super;
int code_max_size;
int code_curr_size;
int instr_oparg;
Expand Down
10 changes: 5 additions & 5 deletions Include/internal/pycore_uop_ids.h

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

30 changes: 15 additions & 15 deletions Include/internal/pycore_uop_metadata.h

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

2 changes: 2 additions & 0 deletions Lib/_opcode_metadata.py

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

41 changes: 41 additions & 0 deletions Lib/test/test_capi/test_opt.py
Original file line number Diff line number Diff line change
Expand Up @@ -3133,6 +3133,47 @@ def f1():
"""), PYTHON_JIT="1")
self.assertEqual(result[0].rc, 0, result)

def test_143092(self):
def f1():
a = "a"
for i in range(50):
x = a[i % len(a)]

s = ""
for _ in range(10):
s += ""

class A: ...
class B: ...

match s:
case int(): ...
case str(): ...
case dict(): ...

(
u0,
*u1,
u2,
u4,
u5,
u6,
u7,
u8,
u9, u10, u11,
u12, u13, u14, u15, u16, u17, u18, u19, u20, u21, u22, u23, u24, u25, u26, u27, u28, u29,
) = [None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,
None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,
None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,
None, None, None, None, None, None, None, None,]

s = ""
for _ in range(10):
s += ""
s += ""

for i in range(TIER2_THRESHOLD * 10):
f1()

def global_identity(x):
return x
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix a crash in the JIT when dealing with ``list.append(x)`` style code.
58 changes: 20 additions & 38 deletions Python/bytecodes.c
Original file line number Diff line number Diff line change
Expand Up @@ -588,7 +588,7 @@ dummy_func(
BINARY_OP_SUBSCR_STR_INT,
BINARY_OP_SUBSCR_DICT,
BINARY_OP_SUBSCR_GETITEM,
// BINARY_OP_INPLACE_ADD_UNICODE, // See comments at that opcode.
BINARY_OP_INPLACE_ADD_UNICODE,
BINARY_OP_EXTEND,
};

Expand Down Expand Up @@ -762,13 +762,10 @@ dummy_func(
macro(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
// where the store goes into the left argument.
// So the inputs are the same as for all BINARY_OP
// specializations, but there is no output.
// At the end we just skip over the STORE_FAST.
op(_BINARY_OP_INPLACE_ADD_UNICODE, (left, right --)) {
// This is a subtle one. We write NULL to the local
// of the following STORE_FAST and leave the result for STORE_FAST
// later to store.
op(_BINARY_OP_INPLACE_ADD_UNICODE, (left, right -- res)) {
PyObject *left_o = PyStackRef_AsPyObjectBorrow(left);
assert(PyUnicode_CheckExact(left_o));
assert(PyUnicode_CheckExact(PyStackRef_AsPyObjectBorrow(right)));
Expand Down Expand Up @@ -796,20 +793,16 @@ dummy_func(
* that the string is safe to mutate.
*/
assert(Py_REFCNT(left_o) >= 2 || !PyStackRef_IsHeapSafe(left));
PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc);
DEAD(left);
PyObject *temp = PyStackRef_AsPyObjectSteal(*target_local);
PyObject *right_o = PyStackRef_AsPyObjectSteal(right);
PyObject *right_o = PyStackRef_AsPyObjectBorrow(right);
PyUnicode_Append(&temp, right_o);
*target_local = PyStackRef_FromPyObjectSteal(temp);
Py_DECREF(right_o);
ERROR_IF(PyStackRef_IsNull(*target_local));
#if TIER_ONE
// The STORE_FAST is already done. This is done here in tier one,
// and during trace projection in tier two:
assert(next_instr->op.code == STORE_FAST);
SKIP_OVER(1);
#endif
PyStackRef_CLOSE_SPECIALIZED(right, _PyUnicode_ExactDealloc);
DEAD(right);
PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc);
DEAD(left);
ERROR_IF(temp == NULL);
res = PyStackRef_FromPyObjectSteal(temp);
*target_local = PyStackRef_NULL;
}

op(_GUARD_BINARY_OP_EXTEND, (descr/4, left, right -- left, right)) {
Expand Down Expand Up @@ -4329,8 +4322,7 @@ dummy_func(
DEOPT_IF(callable_o != interp->callable_cache.list_append);
}

// This is secretly a super-instruction
op(_CALL_LIST_APPEND, (callable, self, arg -- c, s)) {
op(_CALL_LIST_APPEND, (callable, self, arg -- none, c, s)) {
assert(oparg == 1);
PyObject *self_o = PyStackRef_AsPyObjectBorrow(self);

Expand All @@ -4343,13 +4335,9 @@ dummy_func(
}
c = callable;
s = self;
INPUTS_DEAD();
#if TIER_ONE
// Skip the following POP_TOP. This is done here in tier one, and
// during trace projection in tier two:
assert(next_instr->op.code == POP_TOP);
SKIP_OVER(1);
#endif
DEAD(callable);
DEAD(self);
none = PyStackRef_None;
}

op(_CALL_METHOD_DESCRIPTOR_O, (callable, self_or_null, args[oparg] -- res)) {
Expand Down Expand Up @@ -5597,15 +5585,9 @@ dummy_func(
// Super instructions. Instruction deopted. There's a mismatch in what the stack expects
// in the optimizer. So we have to reflect in the trace correctly.
_PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
if ((_tstate->jit_tracer_state.prev_state.instr->op.code == CALL_LIST_APPEND &&
opcode == POP_TOP) ||
(_tstate->jit_tracer_state.prev_state.instr->op.code == BINARY_OP_INPLACE_ADD_UNICODE &&
opcode == STORE_FAST)) {
_tstate->jit_tracer_state.prev_state.instr_is_super = true;
}
else {
_tstate->jit_tracer_state.prev_state.instr = next_instr;
}
// JIT should have disabled super instructions, as we can
// do these optimizations ourselves in the JIT.
_tstate->jit_tracer_state.prev_state.instr = next_instr;
PyObject *prev_code = PyStackRef_AsPyObjectBorrow(frame->f_executable);
if (_tstate->jit_tracer_state.prev_state.instr_code != (PyCodeObject *)prev_code) {
Py_SETREF(_tstate->jit_tracer_state.prev_state.instr_code, (PyCodeObject*)Py_NewRef((prev_code)));
Expand Down
Loading
Loading