mirror of
https://github.com/python/cpython.git
synced 2026-01-04 14:32:21 +00:00
GH-128375: Better instrument for FOR_ITER (GH-128445)
This commit is contained in:
parent
b9c693dcca
commit
f826beca0c
27 changed files with 827 additions and 544 deletions
|
|
@ -60,6 +60,8 @@
|
|||
#define specializing
|
||||
#define split
|
||||
#define replicate(TIMES)
|
||||
#define tier1
|
||||
#define no_save_ip
|
||||
|
||||
// Dummy variables for stack effects.
|
||||
static PyObject *value, *value1, *value2, *left, *right, *res, *sum, *prod, *sub;
|
||||
|
|
@ -336,9 +338,18 @@ dummy_func(
|
|||
res = PyStackRef_NULL;
|
||||
}
|
||||
|
||||
macro(END_FOR) = POP_TOP;
|
||||
no_save_ip inst(END_FOR, (value -- )) {
|
||||
/* Don't update instr_ptr, so that POP_ITER sees
|
||||
* the FOR_ITER as the previous instruction.
|
||||
* This has the benign side effect that if value is
|
||||
* finalized it will see the location as the FOR_ITER's.
|
||||
*/
|
||||
PyStackRef_CLOSE(value);
|
||||
}
|
||||
|
||||
tier1 inst(INSTRUMENTED_END_FOR, (receiver, value -- receiver)) {
|
||||
macro(POP_ITER) = POP_TOP;
|
||||
|
||||
no_save_ip tier1 inst(INSTRUMENTED_END_FOR, (receiver, value -- receiver)) {
|
||||
/* Need to create a fake StopIteration error here,
|
||||
* to conform to PEP 380 */
|
||||
if (PyStackRef_GenCheck(receiver)) {
|
||||
|
|
@ -350,6 +361,11 @@ dummy_func(
|
|||
DECREF_INPUTS();
|
||||
}
|
||||
|
||||
tier1 inst(INSTRUMENTED_POP_ITER, (iter -- )) {
|
||||
INSTRUMENTED_JUMP(prev_instr, this_instr+1, PY_MONITORING_EVENT_BRANCH_RIGHT);
|
||||
PyStackRef_CLOSE(iter);
|
||||
}
|
||||
|
||||
pure inst(END_SEND, (receiver, value -- val)) {
|
||||
(void)receiver;
|
||||
val = value;
|
||||
|
|
@ -2924,10 +2940,8 @@ dummy_func(
|
|||
/* iterator ended normally */
|
||||
assert(next_instr[oparg].op.code == END_FOR ||
|
||||
next_instr[oparg].op.code == INSTRUMENTED_END_FOR);
|
||||
PyStackRef_CLOSE(iter);
|
||||
STACK_SHRINK(1);
|
||||
/* Jump forward oparg, then skip following END_FOR and POP_TOP instruction */
|
||||
JUMPBY(oparg + 2);
|
||||
/* Jump forward oparg, then skip following END_FOR */
|
||||
JUMPBY(oparg + 1);
|
||||
DISPATCH();
|
||||
}
|
||||
next = PyStackRef_FromPyObjectSteal(next_o);
|
||||
|
|
@ -2957,12 +2971,14 @@ dummy_func(
|
|||
|
||||
macro(FOR_ITER) = _SPECIALIZE_FOR_ITER + _FOR_ITER;
|
||||
|
||||
|
||||
inst(INSTRUMENTED_FOR_ITER, (unused/1 -- )) {
|
||||
_PyStackRef iter_stackref = TOP();
|
||||
PyObject *iter = PyStackRef_AsPyObjectBorrow(iter_stackref);
|
||||
PyObject *next = (*Py_TYPE(iter)->tp_iternext)(iter);
|
||||
if (next != NULL) {
|
||||
PUSH(PyStackRef_FromPyObjectSteal(next));
|
||||
INSTRUMENTED_JUMP(this_instr, next_instr, PY_MONITORING_EVENT_BRANCH_LEFT);
|
||||
}
|
||||
else {
|
||||
if (_PyErr_Occurred(tstate)) {
|
||||
|
|
@ -2976,14 +2992,12 @@ dummy_func(
|
|||
/* iterator ended normally */
|
||||
assert(next_instr[oparg].op.code == END_FOR ||
|
||||
next_instr[oparg].op.code == INSTRUMENTED_END_FOR);
|
||||
STACK_SHRINK(1);
|
||||
PyStackRef_CLOSE(iter_stackref);
|
||||
/* Skip END_FOR and POP_TOP */
|
||||
_Py_CODEUNIT *target = next_instr + oparg + 2;
|
||||
INSTRUMENTED_JUMP(this_instr, target, PY_MONITORING_EVENT_BRANCH_RIGHT);
|
||||
/* Skip END_FOR */
|
||||
JUMPBY(oparg + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
op(_ITER_CHECK_LIST, (iter -- iter)) {
|
||||
EXIT_IF(Py_TYPE(PyStackRef_AsPyObjectBorrow(iter)) != &PyListIter_Type);
|
||||
}
|
||||
|
|
@ -3002,10 +3016,8 @@ dummy_func(
|
|||
Py_DECREF(seq);
|
||||
}
|
||||
#endif
|
||||
PyStackRef_CLOSE(iter);
|
||||
STACK_SHRINK(1);
|
||||
/* Jump forward oparg, then skip following END_FOR and POP_TOP instructions */
|
||||
JUMPBY(oparg + 2);
|
||||
/* Jump forward oparg, then skip following END_FOR instruction */
|
||||
JUMPBY(oparg + 1);
|
||||
DISPATCH();
|
||||
}
|
||||
}
|
||||
|
|
@ -3054,10 +3066,8 @@ dummy_func(
|
|||
it->it_seq = NULL;
|
||||
Py_DECREF(seq);
|
||||
}
|
||||
PyStackRef_CLOSE(iter);
|
||||
STACK_SHRINK(1);
|
||||
/* Jump forward oparg, then skip following END_FOR and POP_TOP instructions */
|
||||
JUMPBY(oparg + 2);
|
||||
/* Jump forward oparg, then skip following END_FOR instruction */
|
||||
JUMPBY(oparg + 1);
|
||||
DISPATCH();
|
||||
}
|
||||
}
|
||||
|
|
@ -3098,10 +3108,8 @@ dummy_func(
|
|||
assert(Py_TYPE(r) == &PyRangeIter_Type);
|
||||
STAT_INC(FOR_ITER, hit);
|
||||
if (r->len <= 0) {
|
||||
STACK_SHRINK(1);
|
||||
PyStackRef_CLOSE(iter);
|
||||
// Jump over END_FOR and POP_TOP instructions.
|
||||
JUMPBY(oparg + 2);
|
||||
// Jump over END_FOR instruction.
|
||||
JUMPBY(oparg + 1);
|
||||
DISPATCH();
|
||||
}
|
||||
}
|
||||
|
|
@ -4779,7 +4787,8 @@ dummy_func(
|
|||
}
|
||||
|
||||
inst(INSTRUMENTED_NOT_TAKEN, ( -- )) {
|
||||
INSTRUMENTED_JUMP(this_instr, next_instr, PY_MONITORING_EVENT_BRANCH_LEFT);
|
||||
(void)this_instr; // INSTRUMENTED_JUMP requires this_instr
|
||||
INSTRUMENTED_JUMP(prev_instr, next_instr, PY_MONITORING_EVENT_BRANCH_LEFT);
|
||||
}
|
||||
|
||||
macro(INSTRUMENTED_JUMP_BACKWARD) =
|
||||
|
|
|
|||
|
|
@ -363,7 +363,7 @@ do { \
|
|||
next_instr = dest; \
|
||||
} else { \
|
||||
_PyFrame_SetStackPointer(frame, stack_pointer); \
|
||||
next_instr = _Py_call_instrumentation_jump(tstate, event, frame, src, dest); \
|
||||
next_instr = _Py_call_instrumentation_jump(this_instr, tstate, event, frame, src, dest); \
|
||||
stack_pointer = _PyFrame_GetStackPointer(frame); \
|
||||
if (next_instr == NULL) { \
|
||||
next_instr = (dest)+1; \
|
||||
|
|
|
|||
|
|
@ -409,7 +409,7 @@ codegen_addop_j(instr_sequence *seq, location loc,
|
|||
if (_PyInstructionSequence_Addop(seq, opcode, target.id, loc) != SUCCESS) {
|
||||
return ERROR;
|
||||
}
|
||||
if (IS_CONDITIONAL_JUMP_OPCODE(opcode) || opcode == FOR_ITER) {
|
||||
if (IS_CONDITIONAL_JUMP_OPCODE(opcode)) {
|
||||
return _PyInstructionSequence_Addop(seq, NOT_TAKEN, 0, NO_LOCATION);
|
||||
}
|
||||
return SUCCESS;
|
||||
|
|
@ -2018,7 +2018,7 @@ codegen_for(compiler *c, stmt_ty s)
|
|||
* but a non-generator will jump to a later instruction.
|
||||
*/
|
||||
ADDOP(c, NO_LOCATION, END_FOR);
|
||||
ADDOP(c, NO_LOCATION, POP_TOP);
|
||||
ADDOP(c, NO_LOCATION, POP_ITER);
|
||||
|
||||
_PyCompile_PopFBlock(c, COMPILE_FBLOCK_FOR_LOOP, start);
|
||||
|
||||
|
|
@ -4283,7 +4283,7 @@ codegen_sync_comprehension_generator(compiler *c, location loc,
|
|||
* but a non-generator will jump to a later instruction.
|
||||
*/
|
||||
ADDOP(c, NO_LOCATION, END_FOR);
|
||||
ADDOP(c, NO_LOCATION, POP_TOP);
|
||||
ADDOP(c, NO_LOCATION, POP_ITER);
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
|
|
|
|||
14
Python/executor_cases.c.h
generated
14
Python/executor_cases.c.h
generated
|
|
@ -411,6 +411,20 @@
|
|||
break;
|
||||
}
|
||||
|
||||
case _END_FOR: {
|
||||
_PyStackRef value;
|
||||
value = stack_pointer[-1];
|
||||
/* Don't update instr_ptr, so that POP_ITER sees
|
||||
* the FOR_ITER as the previous instruction.
|
||||
* This has the benign side effect that if value is
|
||||
* finalized it will see the location as the FOR_ITER's.
|
||||
*/
|
||||
PyStackRef_CLOSE(value);
|
||||
stack_pointer += -1;
|
||||
assert(WITHIN_STACK_BOUNDS());
|
||||
break;
|
||||
}
|
||||
|
||||
case _END_SEND: {
|
||||
_PyStackRef value;
|
||||
_PyStackRef receiver;
|
||||
|
|
|
|||
71
Python/generated_cases.c.h
generated
71
Python/generated_cases.c.h
generated
|
|
@ -3768,11 +3768,15 @@
|
|||
}
|
||||
|
||||
TARGET(END_FOR) {
|
||||
frame->instr_ptr = next_instr;
|
||||
next_instr += 1;
|
||||
INSTRUCTION_STATS(END_FOR);
|
||||
_PyStackRef value;
|
||||
value = stack_pointer[-1];
|
||||
/* Don't update instr_ptr, so that POP_ITER sees
|
||||
* the FOR_ITER as the previous instruction.
|
||||
* This has the benign side effect that if value is
|
||||
* finalized it will see the location as the FOR_ITER's.
|
||||
*/
|
||||
PyStackRef_CLOSE(value);
|
||||
stack_pointer += -1;
|
||||
assert(WITHIN_STACK_BOUNDS());
|
||||
|
|
@ -3957,10 +3961,8 @@
|
|||
/* iterator ended normally */
|
||||
assert(next_instr[oparg].op.code == END_FOR ||
|
||||
next_instr[oparg].op.code == INSTRUMENTED_END_FOR);
|
||||
PyStackRef_CLOSE(iter);
|
||||
STACK_SHRINK(1);
|
||||
/* Jump forward oparg, then skip following END_FOR and POP_TOP instruction */
|
||||
JUMPBY(oparg + 2);
|
||||
/* Jump forward oparg, then skip following END_FOR */
|
||||
JUMPBY(oparg + 1);
|
||||
DISPATCH();
|
||||
}
|
||||
next = PyStackRef_FromPyObjectSteal(next_o);
|
||||
|
|
@ -4048,10 +4050,8 @@
|
|||
Py_DECREF(seq);
|
||||
}
|
||||
#endif
|
||||
PyStackRef_CLOSE(iter);
|
||||
STACK_SHRINK(1);
|
||||
/* Jump forward oparg, then skip following END_FOR and POP_TOP instructions */
|
||||
JUMPBY(oparg + 2);
|
||||
/* Jump forward oparg, then skip following END_FOR instruction */
|
||||
JUMPBY(oparg + 1);
|
||||
DISPATCH();
|
||||
}
|
||||
}
|
||||
|
|
@ -4091,10 +4091,8 @@
|
|||
assert(Py_TYPE(r) == &PyRangeIter_Type);
|
||||
STAT_INC(FOR_ITER, hit);
|
||||
if (r->len <= 0) {
|
||||
STACK_SHRINK(1);
|
||||
PyStackRef_CLOSE(iter);
|
||||
// Jump over END_FOR and POP_TOP instructions.
|
||||
JUMPBY(oparg + 2);
|
||||
// Jump over END_FOR instruction.
|
||||
JUMPBY(oparg + 1);
|
||||
DISPATCH();
|
||||
}
|
||||
}
|
||||
|
|
@ -4141,10 +4139,8 @@
|
|||
it->it_seq = NULL;
|
||||
Py_DECREF(seq);
|
||||
}
|
||||
PyStackRef_CLOSE(iter);
|
||||
STACK_SHRINK(1);
|
||||
/* Jump forward oparg, then skip following END_FOR and POP_TOP instructions */
|
||||
JUMPBY(oparg + 2);
|
||||
/* Jump forward oparg, then skip following END_FOR instruction */
|
||||
JUMPBY(oparg + 1);
|
||||
DISPATCH();
|
||||
}
|
||||
}
|
||||
|
|
@ -4572,7 +4568,7 @@
|
|||
}
|
||||
|
||||
TARGET(INSTRUMENTED_END_FOR) {
|
||||
_Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr;
|
||||
_Py_CODEUNIT* const this_instr = next_instr;
|
||||
(void)this_instr;
|
||||
next_instr += 1;
|
||||
INSTRUCTION_STATS(INSTRUMENTED_END_FOR);
|
||||
|
|
@ -4636,6 +4632,7 @@
|
|||
stack_pointer = _PyFrame_GetStackPointer(frame);
|
||||
if (next != NULL) {
|
||||
PUSH(PyStackRef_FromPyObjectSteal(next));
|
||||
INSTRUMENTED_JUMP(this_instr, next_instr, PY_MONITORING_EVENT_BRANCH_LEFT);
|
||||
}
|
||||
else {
|
||||
if (_PyErr_Occurred(tstate)) {
|
||||
|
|
@ -4653,11 +4650,8 @@
|
|||
/* iterator ended normally */
|
||||
assert(next_instr[oparg].op.code == END_FOR ||
|
||||
next_instr[oparg].op.code == INSTRUMENTED_END_FOR);
|
||||
STACK_SHRINK(1);
|
||||
PyStackRef_CLOSE(iter_stackref);
|
||||
/* Skip END_FOR and POP_TOP */
|
||||
_Py_CODEUNIT *target = next_instr + oparg + 2;
|
||||
INSTRUMENTED_JUMP(this_instr, target, PY_MONITORING_EVENT_BRANCH_RIGHT);
|
||||
/* Skip END_FOR */
|
||||
JUMPBY(oparg + 1);
|
||||
}
|
||||
DISPATCH();
|
||||
}
|
||||
|
|
@ -4764,11 +4758,28 @@
|
|||
}
|
||||
|
||||
TARGET(INSTRUMENTED_NOT_TAKEN) {
|
||||
_Py_CODEUNIT* const prev_instr = frame->instr_ptr;
|
||||
_Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr;
|
||||
(void)this_instr;
|
||||
next_instr += 1;
|
||||
INSTRUCTION_STATS(INSTRUMENTED_NOT_TAKEN);
|
||||
INSTRUMENTED_JUMP(this_instr, next_instr, PY_MONITORING_EVENT_BRANCH_LEFT);
|
||||
(void)this_instr; // INSTRUMENTED_JUMP requires this_instr
|
||||
INSTRUMENTED_JUMP(prev_instr, next_instr, PY_MONITORING_EVENT_BRANCH_LEFT);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
TARGET(INSTRUMENTED_POP_ITER) {
|
||||
_Py_CODEUNIT* const prev_instr = frame->instr_ptr;
|
||||
_Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr;
|
||||
(void)this_instr;
|
||||
next_instr += 1;
|
||||
INSTRUCTION_STATS(INSTRUMENTED_POP_ITER);
|
||||
_PyStackRef iter;
|
||||
iter = stack_pointer[-1];
|
||||
INSTRUMENTED_JUMP(prev_instr, this_instr+1, PY_MONITORING_EVENT_BRANCH_RIGHT);
|
||||
PyStackRef_CLOSE(iter);
|
||||
stack_pointer += -1;
|
||||
assert(WITHIN_STACK_BOUNDS());
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
|
|
@ -6693,6 +6704,18 @@
|
|||
DISPATCH();
|
||||
}
|
||||
|
||||
TARGET(POP_ITER) {
|
||||
frame->instr_ptr = next_instr;
|
||||
next_instr += 1;
|
||||
INSTRUCTION_STATS(POP_ITER);
|
||||
_PyStackRef value;
|
||||
value = stack_pointer[-1];
|
||||
PyStackRef_CLOSE(value);
|
||||
stack_pointer += -1;
|
||||
assert(WITHIN_STACK_BOUNDS());
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
TARGET(POP_JUMP_IF_FALSE) {
|
||||
_Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr;
|
||||
(void)this_instr;
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include "pycore_namespace.h"
|
||||
#include "pycore_object.h"
|
||||
#include "pycore_opcode_metadata.h" // IS_VALID_OPCODE, _PyOpcode_Caches
|
||||
#include "pycore_opcode_utils.h" // IS_CONDITIONAL_JUMP_OPCODE
|
||||
#include "pycore_pyatomic_ft_wrappers.h" // FT_ATOMIC_STORE_UINTPTR_RELEASE
|
||||
#include "pycore_pyerrors.h"
|
||||
#include "pycore_pystate.h" // _PyInterpreterState_GET()
|
||||
|
|
@ -95,8 +96,10 @@ static const int8_t EVENT_FOR_OPCODE[256] = {
|
|||
[INSTRUMENTED_POP_JUMP_IF_TRUE] = PY_MONITORING_EVENT_BRANCH_RIGHT,
|
||||
[INSTRUMENTED_POP_JUMP_IF_NONE] = PY_MONITORING_EVENT_BRANCH_RIGHT,
|
||||
[INSTRUMENTED_POP_JUMP_IF_NOT_NONE] = PY_MONITORING_EVENT_BRANCH_RIGHT,
|
||||
[FOR_ITER] = PY_MONITORING_EVENT_BRANCH_RIGHT,
|
||||
[INSTRUMENTED_FOR_ITER] = PY_MONITORING_EVENT_BRANCH_RIGHT,
|
||||
[FOR_ITER] = PY_MONITORING_EVENT_BRANCH_LEFT,
|
||||
[INSTRUMENTED_FOR_ITER] = PY_MONITORING_EVENT_BRANCH_LEFT,
|
||||
[POP_ITER] = PY_MONITORING_EVENT_BRANCH_RIGHT,
|
||||
[INSTRUMENTED_POP_ITER] = PY_MONITORING_EVENT_BRANCH_RIGHT,
|
||||
[END_FOR] = PY_MONITORING_EVENT_STOP_ITERATION,
|
||||
[INSTRUMENTED_END_FOR] = PY_MONITORING_EVENT_STOP_ITERATION,
|
||||
[END_SEND] = PY_MONITORING_EVENT_STOP_ITERATION,
|
||||
|
|
@ -119,6 +122,7 @@ static const uint8_t DE_INSTRUMENT[256] = {
|
|||
[INSTRUMENTED_POP_JUMP_IF_NONE] = POP_JUMP_IF_NONE,
|
||||
[INSTRUMENTED_POP_JUMP_IF_NOT_NONE] = POP_JUMP_IF_NOT_NONE,
|
||||
[INSTRUMENTED_FOR_ITER] = FOR_ITER,
|
||||
[INSTRUMENTED_POP_ITER] = POP_ITER,
|
||||
[INSTRUMENTED_END_FOR] = END_FOR,
|
||||
[INSTRUMENTED_END_SEND] = END_SEND,
|
||||
[INSTRUMENTED_LOAD_SUPER_ATTR] = LOAD_SUPER_ATTR,
|
||||
|
|
@ -156,6 +160,8 @@ static const uint8_t INSTRUMENTED_OPCODES[256] = {
|
|||
[INSTRUMENTED_END_SEND] = INSTRUMENTED_END_SEND,
|
||||
[FOR_ITER] = INSTRUMENTED_FOR_ITER,
|
||||
[INSTRUMENTED_FOR_ITER] = INSTRUMENTED_FOR_ITER,
|
||||
[POP_ITER] = INSTRUMENTED_POP_ITER,
|
||||
[INSTRUMENTED_POP_ITER] = INSTRUMENTED_POP_ITER,
|
||||
[LOAD_SUPER_ATTR] = INSTRUMENTED_LOAD_SUPER_ATTR,
|
||||
[INSTRUMENTED_LOAD_SUPER_ATTR] = INSTRUMENTED_LOAD_SUPER_ATTR,
|
||||
[NOT_TAKEN] = INSTRUMENTED_NOT_TAKEN,
|
||||
|
|
@ -1077,8 +1083,8 @@ static const char *const event_names [] = {
|
|||
|
||||
static int
|
||||
call_instrumentation_vector(
|
||||
PyThreadState *tstate, int event,
|
||||
_PyInterpreterFrame *frame, _Py_CODEUNIT *instr, Py_ssize_t nargs, PyObject *args[])
|
||||
_Py_CODEUNIT *instr, PyThreadState *tstate, int event,
|
||||
_PyInterpreterFrame *frame, _Py_CODEUNIT *arg2, Py_ssize_t nargs, PyObject *args[])
|
||||
{
|
||||
if (tstate->tracing) {
|
||||
return 0;
|
||||
|
|
@ -1091,17 +1097,13 @@ call_instrumentation_vector(
|
|||
int offset = (int)(instr - _PyFrame_GetBytecode(frame));
|
||||
/* Offset visible to user should be the offset in bytes, as that is the
|
||||
* convention for APIs involving code offsets. */
|
||||
int bytes_offset = offset * (int)sizeof(_Py_CODEUNIT);
|
||||
if (event == PY_MONITORING_EVENT_BRANCH_LEFT) {
|
||||
assert(EVENT_FOR_OPCODE[_Py_GetBaseCodeUnit(code, offset-2).op.code] == PY_MONITORING_EVENT_BRANCH_RIGHT);
|
||||
bytes_offset -= 4;
|
||||
}
|
||||
PyObject *offset_obj = PyLong_FromLong(bytes_offset);
|
||||
if (offset_obj == NULL) {
|
||||
int bytes_arg2 = (int)(arg2 - _PyFrame_GetBytecode(frame)) * (int)sizeof(_Py_CODEUNIT);
|
||||
PyObject *arg2_obj = PyLong_FromLong(bytes_arg2);
|
||||
if (arg2_obj == NULL) {
|
||||
return -1;
|
||||
}
|
||||
assert(args[2] == NULL);
|
||||
args[2] = offset_obj;
|
||||
args[2] = arg2_obj;
|
||||
PyInterpreterState *interp = tstate->interp;
|
||||
uint8_t tools = get_tools_for_instruction(code, interp, offset, event);
|
||||
size_t nargsf = (size_t) nargs | PY_VECTORCALL_ARGUMENTS_OFFSET;
|
||||
|
|
@ -1139,7 +1141,7 @@ call_instrumentation_vector(
|
|||
}
|
||||
}
|
||||
}
|
||||
Py_DECREF(offset_obj);
|
||||
Py_DECREF(arg2_obj);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -1149,7 +1151,7 @@ _Py_call_instrumentation(
|
|||
_PyInterpreterFrame *frame, _Py_CODEUNIT *instr)
|
||||
{
|
||||
PyObject *args[3] = { NULL, NULL, NULL };
|
||||
return call_instrumentation_vector(tstate, event, frame, instr, 2, args);
|
||||
return call_instrumentation_vector(instr, tstate, event, frame, instr, 2, args);
|
||||
}
|
||||
|
||||
int
|
||||
|
|
@ -1158,7 +1160,7 @@ _Py_call_instrumentation_arg(
|
|||
_PyInterpreterFrame *frame, _Py_CODEUNIT *instr, PyObject *arg)
|
||||
{
|
||||
PyObject *args[4] = { NULL, NULL, NULL, arg };
|
||||
return call_instrumentation_vector(tstate, event, frame, instr, 3, args);
|
||||
return call_instrumentation_vector(instr, tstate, event, frame, instr, 3, args);
|
||||
}
|
||||
|
||||
int
|
||||
|
|
@ -1167,34 +1169,34 @@ _Py_call_instrumentation_2args(
|
|||
_PyInterpreterFrame *frame, _Py_CODEUNIT *instr, PyObject *arg0, PyObject *arg1)
|
||||
{
|
||||
PyObject *args[5] = { NULL, NULL, NULL, arg0, arg1 };
|
||||
return call_instrumentation_vector(tstate, event, frame, instr, 4, args);
|
||||
return call_instrumentation_vector(instr, tstate, event, frame, instr, 4, args);
|
||||
}
|
||||
|
||||
_Py_CODEUNIT *
|
||||
_Py_call_instrumentation_jump(
|
||||
PyThreadState *tstate, int event,
|
||||
_PyInterpreterFrame *frame, _Py_CODEUNIT *instr, _Py_CODEUNIT *target)
|
||||
_Py_CODEUNIT *instr, PyThreadState *tstate, int event,
|
||||
_PyInterpreterFrame *frame, _Py_CODEUNIT *src, _Py_CODEUNIT *dest)
|
||||
{
|
||||
assert(event == PY_MONITORING_EVENT_JUMP ||
|
||||
event == PY_MONITORING_EVENT_BRANCH_RIGHT ||
|
||||
event == PY_MONITORING_EVENT_BRANCH_LEFT);
|
||||
assert(frame->instr_ptr == instr);
|
||||
int to = (int)(target - _PyFrame_GetBytecode(frame));
|
||||
int to = (int)(dest - _PyFrame_GetBytecode(frame));
|
||||
PyObject *to_obj = PyLong_FromLong(to * (int)sizeof(_Py_CODEUNIT));
|
||||
if (to_obj == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
PyObject *args[4] = { NULL, NULL, NULL, to_obj };
|
||||
int err = call_instrumentation_vector(tstate, event, frame, instr, 3, args);
|
||||
_Py_CODEUNIT *instr_ptr = frame->instr_ptr;
|
||||
int err = call_instrumentation_vector(instr, tstate, event, frame, src, 3, args);
|
||||
Py_DECREF(to_obj);
|
||||
if (err) {
|
||||
return NULL;
|
||||
}
|
||||
if (frame->instr_ptr != instr) {
|
||||
if (frame->instr_ptr != instr_ptr) {
|
||||
/* The callback has caused a jump (by setting the line number) */
|
||||
return frame->instr_ptr;
|
||||
}
|
||||
return target;
|
||||
return dest;
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -1204,7 +1206,7 @@ call_instrumentation_vector_protected(
|
|||
{
|
||||
assert(_PyErr_Occurred(tstate));
|
||||
PyObject *exc = _PyErr_GetRaisedException(tstate);
|
||||
int err = call_instrumentation_vector(tstate, event, frame, instr, nargs, args);
|
||||
int err = call_instrumentation_vector(instr, tstate, event, frame, instr, nargs, args);
|
||||
if (err) {
|
||||
Py_XDECREF(exc);
|
||||
}
|
||||
|
|
@ -1496,9 +1498,10 @@ initialize_lines(PyCodeObject *code)
|
|||
case END_FOR:
|
||||
case END_SEND:
|
||||
case RESUME:
|
||||
case POP_ITER:
|
||||
/* END_FOR cannot start a line, as it is skipped by FOR_ITER
|
||||
* END_SEND cannot start a line, as it is skipped by SEND
|
||||
* RESUME must not be instrumented with INSTRUMENT_LINE */
|
||||
* RESUME and POP_ITER must not be instrumented with INSTRUMENT_LINE */
|
||||
line_data[i].original_opcode = 0;
|
||||
break;
|
||||
default:
|
||||
|
|
@ -1570,11 +1573,14 @@ initialize_lines(PyCodeObject *code)
|
|||
}
|
||||
assert(target >= 0);
|
||||
if (line_data[target].line_delta != NO_LINE) {
|
||||
line_data[target].original_opcode = _Py_GetBaseCodeUnit(code, target).op.code;
|
||||
if (line_data[target].line_delta == COMPUTED_LINE_LINENO_CHANGE) {
|
||||
// If the line is a jump target, we are not sure if the line
|
||||
// number changes, so we set it to COMPUTED_LINE.
|
||||
line_data[target].line_delta = COMPUTED_LINE;
|
||||
int opcode = _Py_GetBaseCodeUnit(code, target).op.code;
|
||||
if (opcode != POP_ITER) {
|
||||
line_data[target].original_opcode = opcode;
|
||||
if (line_data[target].line_delta == COMPUTED_LINE_LINENO_CHANGE) {
|
||||
// If the line is a jump target, we are not sure if the line
|
||||
// number changes, so we set it to COMPUTED_LINE.
|
||||
line_data[target].line_delta = COMPUTED_LINE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2887,30 +2893,52 @@ branch_handler(
|
|||
_PyLegacyBranchEventHandler *self, PyObject *const *args,
|
||||
size_t nargsf, PyObject *kwnames
|
||||
) {
|
||||
// Find the other instrumented instruction and remove tool
|
||||
// The spec (PEP 669) allows spurious events after a DISABLE,
|
||||
// so a best effort is good enough.
|
||||
assert(PyVectorcall_NARGS(nargsf) >= 3);
|
||||
PyCodeObject *code = (PyCodeObject *)args[0];
|
||||
int src_offset = PyLong_AsLong(args[1]);
|
||||
if (PyErr_Occurred()) {
|
||||
return NULL;
|
||||
}
|
||||
_Py_CODEUNIT instr = _PyCode_CODE(code)[src_offset/2];
|
||||
if (!is_instrumented(instr.op.code)) {
|
||||
/* Already disabled */
|
||||
return &_PyInstrumentation_DISABLE;
|
||||
}
|
||||
PyObject *res = PyObject_Vectorcall(self->handler, args, nargsf, kwnames);
|
||||
if (res == &_PyInstrumentation_DISABLE) {
|
||||
// Find the other instrumented instruction and remove tool
|
||||
assert(PyVectorcall_NARGS(nargsf) >= 2);
|
||||
PyObject *offset_obj = args[1];
|
||||
int bytes_offset = PyLong_AsLong(offset_obj);
|
||||
if (PyErr_Occurred()) {
|
||||
return NULL;
|
||||
}
|
||||
PyCodeObject *code = (PyCodeObject *)args[0];
|
||||
if (!PyCode_Check(code) || (bytes_offset & 1)) {
|
||||
return res;
|
||||
}
|
||||
int offset = bytes_offset / 2;
|
||||
/* We need FOR_ITER and POP_JUMP_ to be the same size */
|
||||
assert(INLINE_CACHE_ENTRIES_FOR_ITER == 1);
|
||||
if (self->right) {
|
||||
offset += 2;
|
||||
int offset;
|
||||
int other_event;
|
||||
if (instr.op.code == FOR_ITER) {
|
||||
if (self->right) {
|
||||
offset = src_offset/2;
|
||||
other_event = PY_MONITORING_EVENT_BRANCH_LEFT;
|
||||
}
|
||||
else {
|
||||
// We don't know where the POP_ITER is, so
|
||||
// we cannot de-instrument it.
|
||||
return res;
|
||||
}
|
||||
}
|
||||
if (offset >= Py_SIZE(code)) {
|
||||
else if (IS_CONDITIONAL_JUMP_OPCODE(instr.op.code)) {
|
||||
if (self->right) {
|
||||
offset = src_offset/2 + 2;
|
||||
other_event = PY_MONITORING_EVENT_BRANCH_LEFT;
|
||||
assert(_Py_GetBaseCodeUnit(code, offset).op.code == NOT_TAKEN);
|
||||
}
|
||||
else {
|
||||
offset = src_offset/2;
|
||||
other_event = PY_MONITORING_EVENT_BRANCH_RIGHT;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Orphaned NOT_TAKEN -- Jump removed by the compiler
|
||||
return res;
|
||||
}
|
||||
int other_event = self->right ?
|
||||
PY_MONITORING_EVENT_BRANCH_LEFT : PY_MONITORING_EVENT_BRANCH_RIGHT;
|
||||
LOCK_CODE(code);
|
||||
remove_tools(code, offset, other_event, 1 << self->tool_id);
|
||||
UNLOCK_CODE();
|
||||
|
|
@ -3013,15 +3041,30 @@ static PyObject *
|
|||
branchesiter_next(branchesiterator *bi)
|
||||
{
|
||||
int offset = bi->bi_offset;
|
||||
int oparg = 0;
|
||||
while (offset < Py_SIZE(bi->bi_code)) {
|
||||
_Py_CODEUNIT inst = _Py_GetBaseCodeUnit(bi->bi_code, offset);
|
||||
int next_offset = offset + _PyInstruction_GetLength(bi->bi_code, offset);
|
||||
int event = EVENT_FOR_OPCODE[inst.op.code];
|
||||
if (event == PY_MONITORING_EVENT_BRANCH_RIGHT) {
|
||||
/* Skip NOT_TAKEN */
|
||||
int not_taken = next_offset + 1;
|
||||
bi->bi_offset = not_taken;
|
||||
return int_triple(offset*2, not_taken*2, (next_offset + inst.op.arg)*2);
|
||||
int next_offset = offset + 1 + _PyOpcode_Caches[inst.op.code];
|
||||
switch(inst.op.code) {
|
||||
case EXTENDED_ARG:
|
||||
oparg = (oparg << 8) | inst.op.arg;
|
||||
break;
|
||||
case FOR_ITER:
|
||||
oparg = (oparg << 8) | inst.op.arg;
|
||||
bi->bi_offset = next_offset;
|
||||
int target = next_offset + oparg+2; // Skips END_FOR and POP_ITER
|
||||
return int_triple(offset*2, next_offset*2, target*2);
|
||||
case POP_JUMP_IF_FALSE:
|
||||
case POP_JUMP_IF_TRUE:
|
||||
case POP_JUMP_IF_NONE:
|
||||
case POP_JUMP_IF_NOT_NONE:
|
||||
oparg = (oparg << 8) | inst.op.arg;
|
||||
/* Skip NOT_TAKEN */
|
||||
int not_taken = next_offset + 1;
|
||||
bi->bi_offset = not_taken;
|
||||
return int_triple(offset*2, not_taken*2, (next_offset + oparg)*2);
|
||||
default:
|
||||
oparg = 0;
|
||||
}
|
||||
offset = next_offset;
|
||||
}
|
||||
|
|
|
|||
4
Python/opcode_targets.h
generated
4
Python/opcode_targets.h
generated
|
|
@ -29,6 +29,7 @@ static void *opcode_targets[256] = {
|
|||
&&TARGET_NOP,
|
||||
&&TARGET_NOT_TAKEN,
|
||||
&&TARGET_POP_EXCEPT,
|
||||
&&TARGET_POP_ITER,
|
||||
&&TARGET_POP_TOP,
|
||||
&&TARGET_PUSH_EXC_INFO,
|
||||
&&TARGET_PUSH_NULL,
|
||||
|
|
@ -147,7 +148,6 @@ static void *opcode_targets[256] = {
|
|||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&TARGET_RESUME,
|
||||
&&TARGET_BINARY_OP_ADD_FLOAT,
|
||||
&&TARGET_BINARY_OP_ADD_INT,
|
||||
|
|
@ -234,8 +234,8 @@ static void *opcode_targets[256] = {
|
|||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&TARGET_INSTRUMENTED_END_FOR,
|
||||
&&TARGET_INSTRUMENTED_POP_ITER,
|
||||
&&TARGET_INSTRUMENTED_END_SEND,
|
||||
&&TARGET_INSTRUMENTED_LOAD_SUPER_ATTR,
|
||||
&&TARGET_INSTRUMENTED_FOR_ITER,
|
||||
|
|
|
|||
|
|
@ -622,8 +622,14 @@ translate_bytecode_to_trace(
|
|||
goto done;
|
||||
}
|
||||
assert(opcode != ENTER_EXECUTOR && opcode != EXTENDED_ARG);
|
||||
RESERVE_RAW(2, "_CHECK_VALIDITY_AND_SET_IP");
|
||||
ADD_TO_TRACE(_CHECK_VALIDITY_AND_SET_IP, 0, (uintptr_t)instr, target);
|
||||
if (OPCODE_HAS_NO_SAVE_IP(opcode)) {
|
||||
RESERVE_RAW(2, "_CHECK_VALIDITY");
|
||||
ADD_TO_TRACE(_CHECK_VALIDITY, 0, 0, target);
|
||||
}
|
||||
else {
|
||||
RESERVE_RAW(2, "_CHECK_VALIDITY_AND_SET_IP");
|
||||
ADD_TO_TRACE(_CHECK_VALIDITY_AND_SET_IP, 0, (uintptr_t)instr, target);
|
||||
}
|
||||
|
||||
/* Special case the first instruction,
|
||||
* so that we can guarantee forward progress */
|
||||
|
|
@ -771,7 +777,7 @@ translate_bytecode_to_trace(
|
|||
uint32_t next_inst = target + 1 + INLINE_CACHE_ENTRIES_FOR_ITER + (oparg > 255);
|
||||
uint32_t jump_target = next_inst + oparg;
|
||||
assert(_Py_GetBaseCodeUnit(code, jump_target).op.code == END_FOR);
|
||||
assert(_Py_GetBaseCodeUnit(code, jump_target+1).op.code == POP_TOP);
|
||||
assert(_Py_GetBaseCodeUnit(code, jump_target+1).op.code == POP_ITER);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
|
|
|
|||
6
Python/optimizer_cases.c.h
generated
6
Python/optimizer_cases.c.h
generated
|
|
@ -115,6 +115,12 @@
|
|||
break;
|
||||
}
|
||||
|
||||
case _END_FOR: {
|
||||
stack_pointer += -1;
|
||||
assert(WITHIN_STACK_BOUNDS());
|
||||
break;
|
||||
}
|
||||
|
||||
case _END_SEND: {
|
||||
_Py_UopsSymbol *val;
|
||||
val = sym_new_not_null(ctx);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue