Skip to content

Commit afb0aa6

Browse files
authored
GH-121131: Clean up and fix some instrumented instructions. (GH-121132)
* Add support for 'prev_instr' to code generator and refactor some INSTRUMENTED instructions
1 parent d9efa45 commit afb0aa6

15 files changed

+277
-215
lines changed

Include/internal/pycore_opcode_metadata.h

Lines changed: 7 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_uop_ids.h

Lines changed: 1 addition & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/opcode_ids.h

Lines changed: 15 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/_opcode_metadata.py

Lines changed: 15 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/test/test_sys_settrace.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2857,7 +2857,7 @@ def test_no_jump_from_exception_event(output):
28572857
output.append(1)
28582858
1 / 0
28592859

2860-
@jump_test(3, 2, [2, 5], event='return')
2860+
@jump_test(3, 2, [2, 2, 5], event='return')
28612861
def test_jump_from_yield(output):
28622862
def gen():
28632863
output.append(2)

Python/bytecodes.c

Lines changed: 57 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -945,48 +945,25 @@ dummy_func(
945945
LLTRACE_RESUME_FRAME();
946946
}
947947

948-
inst(INSTRUMENTED_RETURN_VALUE, (retval --)) {
948+
tier1 op(_RETURN_VALUE_EVENT, (val -- val)) {
949949
int err = _Py_call_instrumentation_arg(
950950
tstate, PY_MONITORING_EVENT_PY_RETURN,
951-
frame, this_instr, PyStackRef_AsPyObjectBorrow(retval));
951+
frame, this_instr, PyStackRef_AsPyObjectBorrow(val));
952952
if (err) ERROR_NO_POP();
953-
STACK_SHRINK(1);
954-
assert(EMPTY());
955-
_PyFrame_SetStackPointer(frame, stack_pointer);
956-
_Py_LeaveRecursiveCallPy(tstate);
957-
assert(frame != &entry_frame);
958-
// GH-99729: We need to unlink the frame *before* clearing it:
959-
_PyInterpreterFrame *dying = frame;
960-
frame = tstate->current_frame = dying->previous;
961-
_PyEval_FrameClearAndPop(tstate, dying);
962-
_PyFrame_StackPush(frame, retval);
963-
LOAD_IP(frame->return_offset);
964-
goto resume_frame;
965953
}
966954

955+
macro(INSTRUMENTED_RETURN_VALUE) =
956+
_RETURN_VALUE_EVENT +
957+
RETURN_VALUE;
958+
967959
macro(RETURN_CONST) =
968960
LOAD_CONST +
969961
RETURN_VALUE;
970962

971-
inst(INSTRUMENTED_RETURN_CONST, (--)) {
972-
PyObject *retval = GETITEM(FRAME_CO_CONSTS, oparg);
973-
int err = _Py_call_instrumentation_arg(
974-
tstate, PY_MONITORING_EVENT_PY_RETURN,
975-
frame, this_instr, retval);
976-
if (err) ERROR_NO_POP();
977-
Py_INCREF(retval);
978-
assert(EMPTY());
979-
_PyFrame_SetStackPointer(frame, stack_pointer);
980-
_Py_LeaveRecursiveCallPy(tstate);
981-
assert(frame != &entry_frame);
982-
// GH-99729: We need to unlink the frame *before* clearing it:
983-
_PyInterpreterFrame *dying = frame;
984-
frame = tstate->current_frame = dying->previous;
985-
_PyEval_FrameClearAndPop(tstate, dying);
986-
_PyFrame_StackPush(frame, PyStackRef_FromPyObjectSteal(retval));
987-
LOAD_IP(frame->return_offset);
988-
goto resume_frame;
989-
}
963+
macro(INSTRUMENTED_RETURN_CONST) =
964+
LOAD_CONST +
965+
_RETURN_VALUE_EVENT +
966+
RETURN_VALUE;
990967

991968
inst(GET_AITER, (obj -- iter)) {
992969
unaryfunc getter = NULL;
@@ -1183,31 +1160,6 @@ dummy_func(
11831160
_SEND_GEN_FRAME +
11841161
_PUSH_FRAME;
11851162

1186-
inst(INSTRUMENTED_YIELD_VALUE, (retval -- unused)) {
1187-
assert(frame != &entry_frame);
1188-
frame->instr_ptr = next_instr;
1189-
PyGenObject *gen = _PyGen_GetGeneratorFromFrame(frame);
1190-
assert(FRAME_SUSPENDED_YIELD_FROM == FRAME_SUSPENDED + 1);
1191-
assert(oparg == 0 || oparg == 1);
1192-
gen->gi_frame_state = FRAME_SUSPENDED + oparg;
1193-
_PyFrame_SetStackPointer(frame, stack_pointer - 1);
1194-
int err = _Py_call_instrumentation_arg(
1195-
tstate, PY_MONITORING_EVENT_PY_YIELD,
1196-
frame, this_instr, PyStackRef_AsPyObjectBorrow(retval));
1197-
if (err) ERROR_NO_POP();
1198-
tstate->exc_info = gen->gi_exc_state.previous_item;
1199-
gen->gi_exc_state.previous_item = NULL;
1200-
_Py_LeaveRecursiveCallPy(tstate);
1201-
_PyInterpreterFrame *gen_frame = frame;
1202-
frame = tstate->current_frame = frame->previous;
1203-
gen_frame->previous = NULL;
1204-
_PyFrame_StackPush(frame, retval);
1205-
/* We don't know which of these is relevant here, so keep them equal */
1206-
assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER);
1207-
LOAD_IP(1 + INLINE_CACHE_ENTRIES_SEND);
1208-
goto resume_frame;
1209-
}
1210-
12111163
inst(YIELD_VALUE, (retval -- value)) {
12121164
// NOTE: It's important that YIELD_VALUE never raises an exception!
12131165
// The compiler treats any exception raised here as a failed close()
@@ -1244,6 +1196,23 @@ dummy_func(
12441196
LLTRACE_RESUME_FRAME();
12451197
}
12461198

1199+
tier1 op(_YIELD_VALUE_EVENT, (val -- val)) {
1200+
SAVE_SP();
1201+
int err = _Py_call_instrumentation_arg(
1202+
tstate, PY_MONITORING_EVENT_PY_YIELD,
1203+
frame, this_instr, PyStackRef_AsPyObjectBorrow(val));
1204+
LOAD_SP();
1205+
if (err) ERROR_NO_POP();
1206+
if (frame->instr_ptr != this_instr) {
1207+
next_instr = frame->instr_ptr;
1208+
DISPATCH();
1209+
}
1210+
}
1211+
1212+
macro(INSTRUMENTED_YIELD_VALUE) =
1213+
_YIELD_VALUE_EVENT +
1214+
YIELD_VALUE;
1215+
12471216
inst(POP_EXCEPT, (exc_value -- )) {
12481217
_PyErr_StackItem *exc_info = tstate->exc_info;
12491218
Py_XSETREF(exc_info->exc_value,
@@ -4450,6 +4419,36 @@ dummy_func(
44504419
assert(oparg >= 2);
44514420
}
44524421

4422+
inst(INSTRUMENTED_LINE, ( -- )) {
4423+
int original_opcode = 0;
4424+
if (tstate->tracing) {
4425+
PyCodeObject *code = _PyFrame_GetCode(frame);
4426+
original_opcode = code->_co_monitoring->lines[(int)(this_instr - _PyCode_CODE(code))].original_opcode;
4427+
next_instr = this_instr;
4428+
} else {
4429+
_PyFrame_SetStackPointer(frame, stack_pointer);
4430+
original_opcode = _Py_call_instrumentation_line(
4431+
tstate, frame, this_instr, prev_instr);
4432+
stack_pointer = _PyFrame_GetStackPointer(frame);
4433+
if (original_opcode < 0) {
4434+
next_instr = this_instr+1;
4435+
goto error;
4436+
}
4437+
next_instr = frame->instr_ptr;
4438+
if (next_instr != this_instr) {
4439+
DISPATCH();
4440+
}
4441+
}
4442+
if (_PyOpcode_Caches[original_opcode]) {
4443+
_PyBinaryOpCache *cache = (_PyBinaryOpCache *)(next_instr+1);
4444+
/* Prevent the underlying instruction from specializing
4445+
* and overwriting the instrumentation. */
4446+
PAUSE_ADAPTIVE_COUNTER(cache->counter);
4447+
}
4448+
opcode = original_opcode;
4449+
DISPATCH_GOTO();
4450+
}
4451+
44534452
inst(INSTRUMENTED_INSTRUCTION, ( -- )) {
44544453
int next_opcode = _Py_call_instrumentation_instruction(
44554454
tstate, frame, this_instr);

Python/ceval.c

Lines changed: 0 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -835,46 +835,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
835835

836836
#include "generated_cases.c.h"
837837

838-
/* INSTRUMENTED_LINE has to be here, rather than in bytecodes.c,
839-
* because it needs to capture frame->instr_ptr before it is updated,
840-
* as happens in the standard instruction prologue.
841-
*/
842-
#if USE_COMPUTED_GOTOS
843-
TARGET_INSTRUMENTED_LINE:
844-
#else
845-
case INSTRUMENTED_LINE:
846-
#endif
847-
{
848-
_Py_CODEUNIT *prev = frame->instr_ptr;
849-
_Py_CODEUNIT *here = frame->instr_ptr = next_instr;
850-
int original_opcode = 0;
851-
if (tstate->tracing) {
852-
PyCodeObject *code = _PyFrame_GetCode(frame);
853-
original_opcode = code->_co_monitoring->lines[(int)(here - _PyCode_CODE(code))].original_opcode;
854-
} else {
855-
_PyFrame_SetStackPointer(frame, stack_pointer);
856-
original_opcode = _Py_call_instrumentation_line(
857-
tstate, frame, here, prev);
858-
stack_pointer = _PyFrame_GetStackPointer(frame);
859-
if (original_opcode < 0) {
860-
next_instr = here+1;
861-
goto error;
862-
}
863-
next_instr = frame->instr_ptr;
864-
if (next_instr != here) {
865-
DISPATCH();
866-
}
867-
}
868-
if (_PyOpcode_Caches[original_opcode]) {
869-
_PyBinaryOpCache *cache = (_PyBinaryOpCache *)(next_instr+1);
870-
/* Prevent the underlying instruction from specializing
871-
* and overwriting the instrumentation. */
872-
PAUSE_ADAPTIVE_COUNTER(cache->counter);
873-
}
874-
opcode = original_opcode;
875-
DISPATCH_GOTO();
876-
}
877-
878838

879839
#if USE_COMPUTED_GOTOS
880840
_unknown_opcode:

Python/ceval_macros.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,10 @@ static inline void _Py_LeaveRecursiveCallPy(PyThreadState *tstate) {
402402
/* There's no STORE_IP(), it's inlined by the code generator. */
403403

404404
#define LOAD_SP() \
405-
stack_pointer = _PyFrame_GetStackPointer(frame);
405+
stack_pointer = _PyFrame_GetStackPointer(frame)
406+
407+
#define SAVE_SP() \
408+
_PyFrame_SetStackPointer(frame, stack_pointer)
406409

407410
/* Tier-switching macros. */
408411

Python/executor_cases.c.h

Lines changed: 2 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)