Skip to content

Commit 3937e04

Browse files
committed
Add support for 'prev_instr' to code generator and refactor soem INSTRUMENTED instructions
1 parent 43709d5 commit 3937e04

14 files changed

+321
-217
lines changed

Include/internal/pycore_opcode_metadata.h

Lines changed: 10 additions & 3 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: 19 additions & 19 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: 19 additions & 19 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: 89 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,38 @@ dummy_func(
146146
RESUME_CHECK,
147147
};
148148

149+
tier1 op(_UPDATE_INSTRUMENTATION, (--)) {
150+
assert(frame == tstate->current_frame);
151+
if (tstate->tracing == 0) {
152+
uintptr_t global_version =
153+
_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) &
154+
~_PY_EVAL_EVENTS_MASK;
155+
PyCodeObject* code = _PyFrame_GetCode(frame);
156+
uintptr_t code_version = FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(code->_co_instrumentation_version);
157+
assert((code_version & 255) == 0);
158+
if (code_version != global_version) {
159+
int err = _Py_Instrument(_PyFrame_GetCode(frame), tstate->interp);
160+
ERROR_IF(err, error);
161+
next_instr = this_instr;
162+
DISPATCH();
163+
}
164+
}
165+
}
166+
167+
tier1 op(_SWAP_TO_RESUME_CHECK) {
168+
if (tstate->tracing == 0) {
169+
assert(this_instr->op.code == RESUME ||
170+
this_instr->op.code == RESUME_CHECK ||
171+
this_instr->op.code == INSTRUMENTED_RESUME ||
172+
this_instr->op.code == ENTER_EXECUTOR);
173+
if (this_instr->op.code == RESUME) {
174+
#if ENABLE_SPECIALIZATION
175+
FT_ATOMIC_STORE_UINT8_RELAXED(this_instr->op.code, RESUME_CHECK);
176+
#endif /* ENABLE_SPECIALIZATION */
177+
}
178+
}
179+
}
180+
149181
tier1 inst(RESUME, (--)) {
150182
assert(frame == tstate->current_frame);
151183
if (tstate->tracing == 0) {
@@ -936,48 +968,25 @@ dummy_func(
936968
LLTRACE_RESUME_FRAME();
937969
}
938970

939-
inst(INSTRUMENTED_RETURN_VALUE, (retval --)) {
971+
tier1 op(_RETURN_VALUE_EVENT, (val -- val)) {
940972
int err = _Py_call_instrumentation_arg(
941973
tstate, PY_MONITORING_EVENT_PY_RETURN,
942-
frame, this_instr, PyStackRef_AsPyObjectBorrow(retval));
974+
frame, this_instr, PyStackRef_AsPyObjectBorrow(val));
943975
if (err) ERROR_NO_POP();
944-
STACK_SHRINK(1);
945-
assert(EMPTY());
946-
_PyFrame_SetStackPointer(frame, stack_pointer);
947-
_Py_LeaveRecursiveCallPy(tstate);
948-
assert(frame != &entry_frame);
949-
// GH-99729: We need to unlink the frame *before* clearing it:
950-
_PyInterpreterFrame *dying = frame;
951-
frame = tstate->current_frame = dying->previous;
952-
_PyEval_FrameClearAndPop(tstate, dying);
953-
_PyFrame_StackPush(frame, retval);
954-
LOAD_IP(frame->return_offset);
955-
goto resume_frame;
956976
}
957977

978+
macro(INSTRUMENTED_RETURN_VALUE) =
979+
_RETURN_VALUE_EVENT +
980+
RETURN_VALUE;
981+
958982
macro(RETURN_CONST) =
959983
LOAD_CONST +
960984
RETURN_VALUE;
961985

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

982991
inst(GET_AITER, (obj -- iter)) {
983992
unaryfunc getter = NULL;
@@ -1171,31 +1180,6 @@ dummy_func(
11711180
DISPATCH_INLINED(gen_frame);
11721181
}
11731182

1174-
inst(INSTRUMENTED_YIELD_VALUE, (retval -- unused)) {
1175-
assert(frame != &entry_frame);
1176-
frame->instr_ptr = next_instr;
1177-
PyGenObject *gen = _PyGen_GetGeneratorFromFrame(frame);
1178-
assert(FRAME_SUSPENDED_YIELD_FROM == FRAME_SUSPENDED + 1);
1179-
assert(oparg == 0 || oparg == 1);
1180-
gen->gi_frame_state = FRAME_SUSPENDED + oparg;
1181-
_PyFrame_SetStackPointer(frame, stack_pointer - 1);
1182-
int err = _Py_call_instrumentation_arg(
1183-
tstate, PY_MONITORING_EVENT_PY_YIELD,
1184-
frame, this_instr, PyStackRef_AsPyObjectBorrow(retval));
1185-
if (err) ERROR_NO_POP();
1186-
tstate->exc_info = gen->gi_exc_state.previous_item;
1187-
gen->gi_exc_state.previous_item = NULL;
1188-
_Py_LeaveRecursiveCallPy(tstate);
1189-
_PyInterpreterFrame *gen_frame = frame;
1190-
frame = tstate->current_frame = frame->previous;
1191-
gen_frame->previous = NULL;
1192-
_PyFrame_StackPush(frame, retval);
1193-
/* We don't know which of these is relevant here, so keep them equal */
1194-
assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER);
1195-
LOAD_IP(1 + INLINE_CACHE_ENTRIES_SEND);
1196-
goto resume_frame;
1197-
}
1198-
11991183
inst(YIELD_VALUE, (retval -- value)) {
12001184
// NOTE: It's important that YIELD_VALUE never raises an exception!
12011185
// The compiler treats any exception raised here as a failed close()
@@ -1232,6 +1216,23 @@ dummy_func(
12321216
LLTRACE_RESUME_FRAME();
12331217
}
12341218

1219+
tier1 op(_YIELD_VALUE_EVENT, (val -- val)) {
1220+
SAVE_SP();
1221+
int err = _Py_call_instrumentation_arg(
1222+
tstate, PY_MONITORING_EVENT_PY_YIELD,
1223+
frame, this_instr, PyStackRef_AsPyObjectBorrow(val));
1224+
LOAD_SP();
1225+
if (err) ERROR_NO_POP();
1226+
if (frame->instr_ptr != this_instr) {
1227+
next_instr = frame->instr_ptr;
1228+
DISPATCH();
1229+
}
1230+
}
1231+
1232+
macro(INSTRUMENTED_YIELD_VALUE) =
1233+
_YIELD_VALUE_EVENT +
1234+
YIELD_VALUE;
1235+
12351236
inst(POP_EXCEPT, (exc_value -- )) {
12361237
_PyErr_StackItem *exc_info = tstate->exc_info;
12371238
Py_XSETREF(exc_info->exc_value,
@@ -4470,6 +4471,36 @@ dummy_func(
44704471
assert(oparg >= 2);
44714472
}
44724473

4474+
inst(INSTRUMENTED_LINE, ( -- )) {
4475+
int original_opcode = 0;
4476+
if (tstate->tracing) {
4477+
PyCodeObject *code = _PyFrame_GetCode(frame);
4478+
original_opcode = code->_co_monitoring->lines[(int)(this_instr - _PyCode_CODE(code))].original_opcode;
4479+
next_instr = this_instr;
4480+
} else {
4481+
_PyFrame_SetStackPointer(frame, stack_pointer);
4482+
original_opcode = _Py_call_instrumentation_line(
4483+
tstate, frame, this_instr, prev_instr);
4484+
stack_pointer = _PyFrame_GetStackPointer(frame);
4485+
if (original_opcode < 0) {
4486+
next_instr = this_instr+1;
4487+
goto error;
4488+
}
4489+
next_instr = frame->instr_ptr;
4490+
if (next_instr != this_instr) {
4491+
DISPATCH();
4492+
}
4493+
}
4494+
if (_PyOpcode_Caches[original_opcode]) {
4495+
_PyBinaryOpCache *cache = (_PyBinaryOpCache *)(next_instr+1);
4496+
/* Prevent the underlying instruction from specializing
4497+
* and overwriting the instrumentation. */
4498+
PAUSE_ADAPTIVE_COUNTER(cache->counter);
4499+
}
4500+
opcode = original_opcode;
4501+
DISPATCH_GOTO();
4502+
}
4503+
44734504
inst(INSTRUMENTED_INSTRUCTION, ( -- )) {
44744505
int next_opcode = _Py_call_instrumentation_instruction(
44754506
tstate, frame, this_instr);

0 commit comments

Comments
 (0)