Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/main' into binary_subscr_to_op
Browse files Browse the repository at this point in the history
  • Loading branch information
iritkatriel committed Feb 7, 2025
2 parents 99173b9 + 49bd47d commit cfbd6ca
Show file tree
Hide file tree
Showing 14 changed files with 104 additions and 353 deletions.
2 changes: 1 addition & 1 deletion Doc/whatsnew/3.14.rst
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ For further information on how to build Python, see
__ https://en.wikipedia.org/wiki/Tail_call

(Contributed by Ken Jin in :gh:`128718`, with ideas on how to implement this
in CPython by Mark Shannon, Garret Gu, Haoran Xu, and Josh Haberman.)
in CPython by Mark Shannon, Garrett Gu, Haoran Xu, and Josh Haberman.)


Other language changes
Expand Down
2 changes: 1 addition & 1 deletion Include/internal/pycore_uop_metadata.h

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

10 changes: 6 additions & 4 deletions Lib/test/test_asyncio/test_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -2254,7 +2254,6 @@ def test_wait_invalid_args(self):
asyncio.wait([]))

def test_log_destroyed_pending_task(self):
Task = self.__class__.Task

async def kill_me(loop):
future = self.new_future(loop)
Expand All @@ -2269,7 +2268,7 @@ async def kill_me(loop):

# schedule the task
coro = kill_me(self.loop)
task = asyncio.ensure_future(coro, loop=self.loop)
task = self.new_task(self.loop, coro)

self.assertEqual(self.all_tasks(loop=self.loop), {task})

Expand All @@ -2286,14 +2285,17 @@ async def kill_me(loop):
# no more reference to kill_me() task: the task is destroyed by the GC
support.gc_collect()

self.assertEqual(self.all_tasks(loop=self.loop), set())

mock_handler.assert_called_with(self.loop, {
'message': 'Task was destroyed but it is pending!',
'task': mock.ANY,
'source_traceback': source_traceback,
})
mock_handler.reset_mock()
# task got resurrected by the exception handler
support.gc_collect()

self.assertEqual(self.all_tasks(loop=self.loop), set())


@mock.patch('asyncio.base_events.logger')
def test_tb_logger_not_called_after_cancel(self, m_log):
Expand Down
40 changes: 16 additions & 24 deletions Python/bytecodes.c
Original file line number Diff line number Diff line change
Expand Up @@ -3498,6 +3498,7 @@ dummy_func(
}

op(_MAYBE_EXPAND_METHOD, (callable[1], self_or_null[1], args[oparg] -- func[1], maybe_self[1], args[oparg])) {
(void)args;
if (PyStackRef_TYPE(callable[0]) == &PyMethod_Type && PyStackRef_IsNull(self_or_null[0])) {
PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]);
PyObject *self = ((PyMethodObject *)callable_o)->im_self;
Expand Down Expand Up @@ -3864,6 +3865,7 @@ dummy_func(
_CHECK_PERIODIC;

op(_CHECK_AND_ALLOCATE_OBJECT, (type_version/2, callable[1], null[1], args[oparg] -- init[1], self[1], args[oparg])) {
(void)args;
PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]);
DEOPT_IF(!PyStackRef_IsNull(null[0]));
DEOPT_IF(!PyType_Check(callable_o));
Expand Down Expand Up @@ -4091,7 +4093,7 @@ dummy_func(
PyObject *res_o = PyLong_FromSsize_t(len_i);
assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
if (res_o == NULL) {
GOTO_ERROR(error);
ERROR_NO_POP();
}
PyStackRef_CLOSE(arg_stackref);
DEAD(args);
Expand Down Expand Up @@ -4336,6 +4338,7 @@ dummy_func(
}

op(_MAYBE_EXPAND_METHOD_KW, (callable[1], self_or_null[1], args[oparg], kwnames_in -- func[1], maybe_self[1], args[oparg], kwnames_out)) {
(void)args;
if (PyStackRef_TYPE(callable[0]) == &PyMethod_Type && PyStackRef_IsNull(self_or_null[0])) {
PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]);
PyObject *self = ((PyMethodObject *)callable_o)->im_self;
Expand Down Expand Up @@ -4994,7 +4997,7 @@ dummy_func(
if (frame->lltrace >= 2) {
printf("SIDE EXIT: [UOp ");
_PyUOpPrint(&next_uop[-1]);
printf(", exit %u, temp %d, target %d -> %s]\n",
printf(", exit %lu, temp %d, target %d -> %s]\n",
exit - current_executor->exits, exit->temperature.value_and_backoff,
(int)(target - _PyFrame_GetBytecode(frame)),
_PyOpcode_OpName[target->op.code]);
Expand All @@ -5004,11 +5007,11 @@ dummy_func(
exit->temperature = initial_temperature_backoff_counter();
Py_CLEAR(exit->executor);
}
tstate->previous_executor = (PyObject *)current_executor;
if (exit->executor == NULL) {
_Py_BackoffCounter temperature = exit->temperature;
if (!backoff_counter_triggers(temperature)) {
exit->temperature = advance_backoff_counter(temperature);
tstate->previous_executor = (PyObject *)current_executor;
GOTO_TIER_ONE(target);
}
_PyExecutorObject *executor;
Expand All @@ -5021,20 +5024,13 @@ dummy_func(
int optimized = _PyOptimizer_Optimize(frame, target, &executor, chain_depth);
if (optimized <= 0) {
exit->temperature = restart_backoff_counter(temperature);
if (optimized < 0) {
GOTO_UNWIND();
}
tstate->previous_executor = (PyObject *)current_executor;
GOTO_TIER_ONE(target);
}
else {
exit->temperature = initial_temperature_backoff_counter();
GOTO_TIER_ONE(optimized < 0 ? NULL : target);
}
exit->temperature = initial_temperature_backoff_counter();
}
exit->executor = executor;
}
Py_INCREF(exit->executor);
tstate->previous_executor = (PyObject *)current_executor;
GOTO_TIER_TWO(exit->executor);
}

Expand Down Expand Up @@ -5102,7 +5098,7 @@ dummy_func(
if (frame->lltrace >= 2) {
printf("DYNAMIC EXIT: [UOp ");
_PyUOpPrint(&next_uop[-1]);
printf(", exit %u, temp %d, target %d -> %s]\n",
printf(", exit %lu, temp %d, target %d -> %s]\n",
exit - current_executor->exits, exit->temperature.value_and_backoff,
(int)(target - _PyFrame_GetBytecode(frame)),
_PyOpcode_OpName[target->op.code]);
Expand All @@ -5122,21 +5118,15 @@ dummy_func(
int optimized = _PyOptimizer_Optimize(frame, target, &executor, 0);
if (optimized <= 0) {
exit->temperature = restart_backoff_counter(exit->temperature);
if (optimized < 0) {
GOTO_UNWIND();
}
GOTO_TIER_ONE(target);
}
else {
exit->temperature = initial_temperature_backoff_counter();
GOTO_TIER_ONE(optimized < 0 ? NULL : target);
}
exit->temperature = initial_temperature_backoff_counter();
}
GOTO_TIER_TWO(executor);
}

tier2 op(_START_EXECUTOR, (executor/4 --)) {
Py_DECREF(tstate->previous_executor);
tstate->previous_executor = NULL;
Py_CLEAR(tstate->previous_executor);
#ifndef _Py_JIT
current_executor = (_PyExecutorObject*)executor;
#endif
Expand All @@ -5162,14 +5152,16 @@ dummy_func(
}

tier2 op(_DEOPT, (--)) {
EXIT_TO_TIER1();
tstate->previous_executor = (PyObject *)current_executor;
GOTO_TIER_ONE(_PyFrame_GetBytecode(frame) + CURRENT_TARGET());
}

tier2 op(_ERROR_POP_N, (target/2 --)) {
tstate->previous_executor = (PyObject *)current_executor;
assert(oparg == 0);
frame->instr_ptr = _PyFrame_GetBytecode(frame) + target;
SYNC_SP();
GOTO_UNWIND();
GOTO_TIER_ONE(NULL);
}

/* Progress is guaranteed if we DEOPT on the eval breaker, because
Expand Down
34 changes: 1 addition & 33 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -880,9 +880,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
#undef LOAD_IP
#define LOAD_IP(UNUSED) (void)0

#undef GOTO_ERROR
#define GOTO_ERROR(LABEL) goto LABEL ## _tier_two

#ifdef Py_STATS
// Disable these macros that apply to Tier 1 stats when we are in Tier 2
#undef STAT_INC
Expand Down Expand Up @@ -958,46 +955,17 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
_PyOpcode_OpName[frame->instr_ptr->op.code]);
}
#endif
assert (next_uop[-1].format == UOP_FORMAT_JUMP);
assert(next_uop[-1].format == UOP_FORMAT_JUMP);
uint16_t target = uop_get_error_target(&next_uop[-1]);
next_uop = current_executor->trace + target;
goto tier2_dispatch;

error_tier_two:
OPT_HIST(trace_uop_execution_counter, trace_run_length_hist);
assert(next_uop[-1].format == UOP_FORMAT_TARGET);
frame->return_offset = 0; // Don't leave this random
Py_DECREF(current_executor);
tstate->previous_executor = NULL;
next_instr = frame->instr_ptr;
goto error;

jump_to_jump_target:
assert(next_uop[-1].format == UOP_FORMAT_JUMP);
target = uop_get_jump_target(&next_uop[-1]);
next_uop = current_executor->trace + target;
goto tier2_dispatch;

exit_to_tier1_dynamic:
next_instr = frame->instr_ptr;
goto goto_to_tier1;
exit_to_tier1:
assert(next_uop[-1].format == UOP_FORMAT_TARGET);
next_instr = next_uop[-1].target + _PyFrame_GetBytecode(frame);
goto_to_tier1:
#ifdef Py_DEBUG
if (frame->lltrace >= 2) {
printf("DEOPT: [UOp ");
_PyUOpPrint(&next_uop[-1]);
printf(" -> %s]\n",
_PyOpcode_OpName[next_instr->op.code]);
}
#endif
OPT_HIST(trace_uop_execution_counter, trace_run_length_hist);
Py_DECREF(current_executor);
tstate->previous_executor = NULL;
DISPATCH();

#endif // _Py_JIT

#endif // _Py_TIER2
Expand Down
38 changes: 19 additions & 19 deletions Python/ceval_macros.h
Original file line number Diff line number Diff line change
Expand Up @@ -161,9 +161,6 @@ do { \
JUMP_TO_LABEL(start_frame); \
} while (0)

// Use this instead of 'goto error' so Tier 2 can go to a different label
#define GOTO_ERROR(LABEL) JUMP_TO_LABEL(LABEL)

/* Tuple access macros */

#ifndef Py_DEBUG
Expand Down Expand Up @@ -387,17 +384,19 @@ _PyFrame_SetStackPointer(frame, stack_pointer)
#define GOTO_TIER_TWO(EXECUTOR) \
do { \
OPT_STAT_INC(traces_executed); \
jit_func jitted = (EXECUTOR)->jit_code; \
_PyExecutorObject *_executor = (EXECUTOR); \
jit_func jitted = _executor->jit_code; \
/* Keep the shim frame alive via the executor: */ \
Py_INCREF(_executor); \
next_instr = jitted(frame, stack_pointer, tstate); \
Py_DECREF(tstate->previous_executor); \
tstate->previous_executor = NULL; \
Py_DECREF(_executor); \
Py_CLEAR(tstate->previous_executor); \
frame = tstate->current_frame; \
stack_pointer = _PyFrame_GetStackPointer(frame); \
if (next_instr == NULL) { \
next_instr = frame->instr_ptr; \
stack_pointer = _PyFrame_GetStackPointer(frame); \
goto error; \
} \
stack_pointer = _PyFrame_GetStackPointer(frame); \
DISPATCH(); \
} while (0)
#else
Expand All @@ -410,24 +409,25 @@ do { \
} while (0)
#endif

#define GOTO_TIER_ONE(TARGET) \
do { \
Py_DECREF(tstate->previous_executor); \
tstate->previous_executor = NULL; \
next_instr = target; \
DISPATCH(); \
#define GOTO_TIER_ONE(TARGET) \
do { \
next_instr = (TARGET); \
OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); \
Py_CLEAR(tstate->previous_executor); \
if (next_instr == NULL) { \
next_instr = frame->instr_ptr; \
goto error; \
} \
DISPATCH(); \
} while (0)

#define CURRENT_OPARG() (next_uop[-1].oparg)

#define CURRENT_OPARG() (next_uop[-1].oparg)
#define CURRENT_OPERAND0() (next_uop[-1].operand0)
#define CURRENT_OPERAND1() (next_uop[-1].operand1)
#define CURRENT_TARGET() (next_uop[-1].target)

#define JUMP_TO_JUMP_TARGET() goto jump_to_jump_target
#define JUMP_TO_ERROR() goto jump_to_error_target
#define GOTO_UNWIND() goto error_tier_two
#define EXIT_TO_TIER1() goto exit_to_tier1
#define EXIT_TO_TIER1_DYNAMIC() goto exit_to_tier1_dynamic;

/* Stackref macros */

Expand Down
Loading

0 comments on commit cfbd6ca

Please sign in to comment.