summaryrefslogtreecommitdiffstats
path: root/Python
diff options
context:
space:
mode:
authorMark Shannon <mark@hotpy.org>2024-12-19 16:59:51 (GMT)
committerGitHub <noreply@github.com>2024-12-19 16:59:51 (GMT)
commitd2f1d917e8b3d2dd8f35495c7632a32688883332 (patch)
tree334558f483e0f41d80924973d16b68354bf95b82 /Python
parent7b811d0562a0bf7433165785f1549ac199610f8b (diff)
downloadcpython-d2f1d917e8b3d2dd8f35495c7632a32688883332.zip
cpython-d2f1d917e8b3d2dd8f35495c7632a32688883332.tar.gz
cpython-d2f1d917e8b3d2dd8f35495c7632a32688883332.tar.bz2
GH-122548: Implement branch taken and not taken events for sys.monitoring (GH-122564)
Diffstat (limited to 'Python')
-rw-r--r--Python/bytecodes.c60
-rw-r--r--Python/codegen.c9
-rw-r--r--Python/executor_cases.c.h2
-rw-r--r--Python/flowgraph.c6
-rw-r--r--Python/generated_cases.c.h74
-rw-r--r--Python/instrumentation.c321
-rw-r--r--Python/opcode_targets.h4
-rw-r--r--Python/optimizer_cases.c.h2
8 files changed, 353 insertions, 125 deletions
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index b67264f..cf089c3 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -148,6 +148,8 @@ dummy_func(
RESUME_CHECK,
};
+ macro(NOT_TAKEN) = NOP;
+
op(_CHECK_PERIODIC, (--)) {
_Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY();
QSBR_QUIESCENT_STATE(tstate);
@@ -2723,7 +2725,7 @@ dummy_func(
int flag = PyStackRef_IsFalse(cond);
DEAD(cond);
RECORD_BRANCH_TAKEN(this_instr[1].cache, flag);
- JUMPBY(oparg * flag);
+ JUMPBY(flag ? oparg : next_instr->op.code == NOT_TAKEN);
}
replaced op(_POP_JUMP_IF_TRUE, (cond -- )) {
@@ -2731,7 +2733,7 @@ dummy_func(
int flag = PyStackRef_IsTrue(cond);
DEAD(cond);
RECORD_BRANCH_TAKEN(this_instr[1].cache, flag);
- JUMPBY(oparg * flag);
+ JUMPBY(flag ? oparg : next_instr->op.code == NOT_TAKEN);
}
op(_IS_NONE, (value -- b)) {
@@ -2923,13 +2925,11 @@ dummy_func(
macro(FOR_ITER) = _SPECIALIZE_FOR_ITER + _FOR_ITER;
inst(INSTRUMENTED_FOR_ITER, (unused/1 -- )) {
- _Py_CODEUNIT *target;
_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));
- target = next_instr;
}
else {
if (_PyErr_Occurred(tstate)) {
@@ -2946,9 +2946,9 @@ dummy_func(
STACK_SHRINK(1);
PyStackRef_CLOSE(iter_stackref);
/* Skip END_FOR and POP_TOP */
- target = next_instr + oparg + 2;
+ _Py_CODEUNIT *target = next_instr + oparg + 2;
+ INSTRUMENTED_JUMP(this_instr, target, PY_MONITORING_EVENT_BRANCH_RIGHT);
}
- INSTRUMENTED_JUMP(this_instr, target, PY_MONITORING_EVENT_BRANCH);
}
op(_ITER_CHECK_LIST, (iter -- iter)) {
@@ -4736,6 +4736,10 @@ dummy_func(
INSTRUMENTED_JUMP(this_instr, next_instr - oparg, PY_MONITORING_EVENT_JUMP);
}
+ inst(INSTRUMENTED_NOT_TAKEN, ( -- )) {
+ INSTRUMENTED_JUMP(this_instr, next_instr, PY_MONITORING_EVENT_BRANCH_LEFT);
+ }
+
macro(INSTRUMENTED_JUMP_BACKWARD) =
unused/1 +
_CHECK_PERIODIC +
@@ -4744,51 +4748,43 @@ dummy_func(
inst(INSTRUMENTED_POP_JUMP_IF_TRUE, (unused/1 -- )) {
_PyStackRef cond = POP();
assert(PyStackRef_BoolCheck(cond));
- int flag = PyStackRef_IsTrue(cond);
- int offset = flag * oparg;
- RECORD_BRANCH_TAKEN(this_instr[1].cache, flag);
- INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH);
+ int jump = PyStackRef_IsTrue(cond);
+ RECORD_BRANCH_TAKEN(this_instr[1].cache, jump);
+ if (jump) {
+ INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT);
+ }
}
inst(INSTRUMENTED_POP_JUMP_IF_FALSE, (unused/1 -- )) {
_PyStackRef cond = POP();
assert(PyStackRef_BoolCheck(cond));
- int flag = PyStackRef_IsFalse(cond);
- int offset = flag * oparg;
- RECORD_BRANCH_TAKEN(this_instr[1].cache, flag);
- INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH);
+ int jump = PyStackRef_IsFalse(cond);
+ RECORD_BRANCH_TAKEN(this_instr[1].cache, jump);
+ if (jump) {
+ INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT);
+ }
}
inst(INSTRUMENTED_POP_JUMP_IF_NONE, (unused/1 -- )) {
_PyStackRef value_stackref = POP();
- int flag = PyStackRef_IsNone(value_stackref);
- int offset;
- if (flag) {
- offset = oparg;
+ int jump = PyStackRef_IsNone(value_stackref);
+ RECORD_BRANCH_TAKEN(this_instr[1].cache, jump);
+ if (jump) {
+ INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT);
}
else {
PyStackRef_CLOSE(value_stackref);
- offset = 0;
}
- RECORD_BRANCH_TAKEN(this_instr[1].cache, flag);
- INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH);
}
inst(INSTRUMENTED_POP_JUMP_IF_NOT_NONE, (unused/1 -- )) {
_PyStackRef value_stackref = POP();
- int offset;
- int nflag = PyStackRef_IsNone(value_stackref);
- if (nflag) {
- offset = 0;
- }
- else {
+ int jump = !PyStackRef_IsNone(value_stackref);
+ RECORD_BRANCH_TAKEN(this_instr[1].cache, jump);
+ if (jump) {
PyStackRef_CLOSE(value_stackref);
- offset = oparg;
+ INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT);
}
- #if ENABLE_SPECIALIZATION
- this_instr[1].cache = (this_instr[1].cache << 1) | !nflag;
- #endif
- INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH);
}
tier1 inst(EXTENDED_ARG, ( -- )) {
diff --git a/Python/codegen.c b/Python/codegen.c
index a5e550c..6d3272e 100644
--- a/Python/codegen.c
+++ b/Python/codegen.c
@@ -406,7 +406,13 @@ codegen_addop_j(instr_sequence *seq, location loc,
assert(IS_JUMP_TARGET_LABEL(target));
assert(OPCODE_HAS_JUMP(opcode) || IS_BLOCK_PUSH_OPCODE(opcode));
assert(!IS_ASSEMBLER_OPCODE(opcode));
- return _PyInstructionSequence_Addop(seq, opcode, target.id, loc);
+ if (_PyInstructionSequence_Addop(seq, opcode, target.id, loc) != SUCCESS) {
+ return ERROR;
+ }
+ if (IS_CONDITIONAL_JUMP_OPCODE(opcode) || opcode == FOR_ITER) {
+ return _PyInstructionSequence_Addop(seq, NOT_TAKEN, 0, NO_LOCATION);
+ }
+ return SUCCESS;
}
#define ADDOP_JUMP(C, LOC, OP, O) \
@@ -682,7 +688,6 @@ codegen_setup_annotations_scope(compiler *c, location loc,
ADDOP_I(c, loc, COMPARE_OP, (Py_GT << 5) | compare_masks[Py_GT]);
NEW_JUMP_TARGET_LABEL(c, body);
ADDOP_JUMP(c, loc, POP_JUMP_IF_FALSE, body);
-
ADDOP_I(c, loc, LOAD_COMMON_CONSTANT, CONSTANT_NOTIMPLEMENTEDERROR);
ADDOP_I(c, loc, RAISE_VARARGS, 1);
USE_LABEL(c, body);
diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h
index de61a64..9bfc3e7 100644
--- a/Python/executor_cases.c.h
+++ b/Python/executor_cases.c.h
@@ -5668,6 +5668,8 @@
/* _MONITOR_JUMP_BACKWARD is not a viable micro-op for tier 2 because it uses the 'this_instr' variable */
+ /* _INSTRUMENTED_NOT_TAKEN is not a viable micro-op for tier 2 because it is instrumented */
+
/* _INSTRUMENTED_POP_JUMP_IF_TRUE is not a viable micro-op for tier 2 because it is instrumented */
/* _INSTRUMENTED_POP_JUMP_IF_FALSE is not a viable micro-op for tier 2 because it is instrumented */
diff --git a/Python/flowgraph.c b/Python/flowgraph.c
index b1097b6..64df629 100644
--- a/Python/flowgraph.c
+++ b/Python/flowgraph.c
@@ -557,6 +557,12 @@ normalize_jumps_in_block(cfg_builder *g, basicblock *b) {
if (backwards_jump == NULL) {
return ERROR;
}
+ assert(b->b_next->b_iused > 0);
+ assert(b->b_next->b_instr[0].i_opcode == NOT_TAKEN);
+ b->b_next->b_instr[0].i_opcode = NOP;
+ b->b_next->b_instr[0].i_loc = NO_LOCATION;
+ RETURN_IF_ERROR(
+ basicblock_addop(backwards_jump, NOT_TAKEN, 0, last->i_loc));
RETURN_IF_ERROR(
basicblock_add_jump(backwards_jump, JUMP, target, last->i_loc));
last->i_opcode = reversed_opcode;
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 8a89ba8..ac89891 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -4618,7 +4618,6 @@
next_instr += 2;
INSTRUCTION_STATS(INSTRUMENTED_FOR_ITER);
/* Skip 1 cache entry */
- _Py_CODEUNIT *target;
_PyStackRef iter_stackref = TOP();
PyObject *iter = PyStackRef_AsPyObjectBorrow(iter_stackref);
_PyFrame_SetStackPointer(frame, stack_pointer);
@@ -4626,7 +4625,6 @@
stack_pointer = _PyFrame_GetStackPointer(frame);
if (next != NULL) {
PUSH(PyStackRef_FromPyObjectSteal(next));
- target = next_instr;
}
else {
if (_PyErr_Occurred(tstate)) {
@@ -4647,9 +4645,9 @@
STACK_SHRINK(1);
PyStackRef_CLOSE(iter_stackref);
/* Skip END_FOR and POP_TOP */
- target = next_instr + oparg + 2;
+ _Py_CODEUNIT *target = next_instr + oparg + 2;
+ INSTRUMENTED_JUMP(this_instr, target, PY_MONITORING_EVENT_BRANCH_RIGHT);
}
- INSTRUMENTED_JUMP(this_instr, target, PY_MONITORING_EVENT_BRANCH);
DISPATCH();
}
@@ -4754,6 +4752,15 @@
GO_TO_INSTRUCTION(LOAD_SUPER_ATTR);
}
+ TARGET(INSTRUMENTED_NOT_TAKEN) {
+ _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);
+ DISPATCH();
+ }
+
TARGET(INSTRUMENTED_POP_JUMP_IF_FALSE) {
_Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr;
(void)this_instr;
@@ -4762,10 +4769,11 @@
/* Skip 1 cache entry */
_PyStackRef cond = POP();
assert(PyStackRef_BoolCheck(cond));
- int flag = PyStackRef_IsFalse(cond);
- int offset = flag * oparg;
- RECORD_BRANCH_TAKEN(this_instr[1].cache, flag);
- INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH);
+ int jump = PyStackRef_IsFalse(cond);
+ RECORD_BRANCH_TAKEN(this_instr[1].cache, jump);
+ if (jump) {
+ INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT);
+ }
DISPATCH();
}
@@ -4776,17 +4784,14 @@
INSTRUCTION_STATS(INSTRUMENTED_POP_JUMP_IF_NONE);
/* Skip 1 cache entry */
_PyStackRef value_stackref = POP();
- int flag = PyStackRef_IsNone(value_stackref);
- int offset;
- if (flag) {
- offset = oparg;
+ int jump = PyStackRef_IsNone(value_stackref);
+ RECORD_BRANCH_TAKEN(this_instr[1].cache, jump);
+ if (jump) {
+ INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT);
}
else {
PyStackRef_CLOSE(value_stackref);
- offset = 0;
}
- RECORD_BRANCH_TAKEN(this_instr[1].cache, flag);
- INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH);
DISPATCH();
}
@@ -4797,19 +4802,12 @@
INSTRUCTION_STATS(INSTRUMENTED_POP_JUMP_IF_NOT_NONE);
/* Skip 1 cache entry */
_PyStackRef value_stackref = POP();
- int offset;
- int nflag = PyStackRef_IsNone(value_stackref);
- if (nflag) {
- offset = 0;
- }
- else {
+ int jump = !PyStackRef_IsNone(value_stackref);
+ RECORD_BRANCH_TAKEN(this_instr[1].cache, jump);
+ if (jump) {
PyStackRef_CLOSE(value_stackref);
- offset = oparg;
+ INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT);
}
- #if ENABLE_SPECIALIZATION
- this_instr[1].cache = (this_instr[1].cache << 1) | !nflag;
- #endif
- INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH);
DISPATCH();
}
@@ -4821,10 +4819,11 @@
/* Skip 1 cache entry */
_PyStackRef cond = POP();
assert(PyStackRef_BoolCheck(cond));
- int flag = PyStackRef_IsTrue(cond);
- int offset = flag * oparg;
- RECORD_BRANCH_TAKEN(this_instr[1].cache, flag);
- INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH);
+ int jump = PyStackRef_IsTrue(cond);
+ RECORD_BRANCH_TAKEN(this_instr[1].cache, jump);
+ if (jump) {
+ INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT);
+ }
DISPATCH();
}
@@ -6659,6 +6658,13 @@
DISPATCH();
}
+ TARGET(NOT_TAKEN) {
+ frame->instr_ptr = next_instr;
+ next_instr += 1;
+ INSTRUCTION_STATS(NOT_TAKEN);
+ DISPATCH();
+ }
+
TARGET(POP_EXCEPT) {
frame->instr_ptr = next_instr;
next_instr += 1;
@@ -6687,7 +6693,7 @@
assert(PyStackRef_BoolCheck(cond));
int flag = PyStackRef_IsFalse(cond);
RECORD_BRANCH_TAKEN(this_instr[1].cache, flag);
- JUMPBY(oparg * flag);
+ JUMPBY(flag ? oparg : next_instr->op.code == NOT_TAKEN);
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
DISPATCH();
@@ -6719,7 +6725,7 @@
assert(PyStackRef_BoolCheck(cond));
int flag = PyStackRef_IsTrue(cond);
RECORD_BRANCH_TAKEN(this_instr[1].cache, flag);
- JUMPBY(oparg * flag);
+ JUMPBY(flag ? oparg : next_instr->op.code == NOT_TAKEN);
}
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
@@ -6752,7 +6758,7 @@
assert(PyStackRef_BoolCheck(cond));
int flag = PyStackRef_IsFalse(cond);
RECORD_BRANCH_TAKEN(this_instr[1].cache, flag);
- JUMPBY(oparg * flag);
+ JUMPBY(flag ? oparg : next_instr->op.code == NOT_TAKEN);
}
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
@@ -6770,7 +6776,7 @@
assert(PyStackRef_BoolCheck(cond));
int flag = PyStackRef_IsTrue(cond);
RECORD_BRANCH_TAKEN(this_instr[1].cache, flag);
- JUMPBY(oparg * flag);
+ JUMPBY(flag ? oparg : next_instr->op.code == NOT_TAKEN);
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
DISPATCH();
diff --git a/Python/instrumentation.c b/Python/instrumentation.c
index 3503809..e4255bf 100644
--- a/Python/instrumentation.c
+++ b/Python/instrumentation.c
@@ -85,22 +85,24 @@ static const int8_t EVENT_FOR_OPCODE[256] = {
[INSTRUMENTED_YIELD_VALUE] = PY_MONITORING_EVENT_PY_YIELD,
[JUMP_FORWARD] = PY_MONITORING_EVENT_JUMP,
[JUMP_BACKWARD] = PY_MONITORING_EVENT_JUMP,
- [POP_JUMP_IF_FALSE] = PY_MONITORING_EVENT_BRANCH,
- [POP_JUMP_IF_TRUE] = PY_MONITORING_EVENT_BRANCH,
- [POP_JUMP_IF_NONE] = PY_MONITORING_EVENT_BRANCH,
- [POP_JUMP_IF_NOT_NONE] = PY_MONITORING_EVENT_BRANCH,
+ [POP_JUMP_IF_FALSE] = PY_MONITORING_EVENT_BRANCH_RIGHT,
+ [POP_JUMP_IF_TRUE] = PY_MONITORING_EVENT_BRANCH_RIGHT,
+ [POP_JUMP_IF_NONE] = PY_MONITORING_EVENT_BRANCH_RIGHT,
+ [POP_JUMP_IF_NOT_NONE] = PY_MONITORING_EVENT_BRANCH_RIGHT,
[INSTRUMENTED_JUMP_FORWARD] = PY_MONITORING_EVENT_JUMP,
[INSTRUMENTED_JUMP_BACKWARD] = PY_MONITORING_EVENT_JUMP,
- [INSTRUMENTED_POP_JUMP_IF_FALSE] = PY_MONITORING_EVENT_BRANCH,
- [INSTRUMENTED_POP_JUMP_IF_TRUE] = PY_MONITORING_EVENT_BRANCH,
- [INSTRUMENTED_POP_JUMP_IF_NONE] = PY_MONITORING_EVENT_BRANCH,
- [INSTRUMENTED_POP_JUMP_IF_NOT_NONE] = PY_MONITORING_EVENT_BRANCH,
- [FOR_ITER] = PY_MONITORING_EVENT_BRANCH,
- [INSTRUMENTED_FOR_ITER] = PY_MONITORING_EVENT_BRANCH,
+ [INSTRUMENTED_POP_JUMP_IF_FALSE] = PY_MONITORING_EVENT_BRANCH_RIGHT,
+ [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,
[END_FOR] = PY_MONITORING_EVENT_STOP_ITERATION,
[INSTRUMENTED_END_FOR] = PY_MONITORING_EVENT_STOP_ITERATION,
[END_SEND] = PY_MONITORING_EVENT_STOP_ITERATION,
[INSTRUMENTED_END_SEND] = PY_MONITORING_EVENT_STOP_ITERATION,
+ [NOT_TAKEN] = PY_MONITORING_EVENT_BRANCH_LEFT,
+ [INSTRUMENTED_NOT_TAKEN] = PY_MONITORING_EVENT_BRANCH_LEFT,
};
static const uint8_t DE_INSTRUMENT[256] = {
@@ -120,6 +122,7 @@ static const uint8_t DE_INSTRUMENT[256] = {
[INSTRUMENTED_END_FOR] = END_FOR,
[INSTRUMENTED_END_SEND] = END_SEND,
[INSTRUMENTED_LOAD_SUPER_ATTR] = LOAD_SUPER_ATTR,
+ [INSTRUMENTED_NOT_TAKEN] = NOT_TAKEN,
};
static const uint8_t INSTRUMENTED_OPCODES[256] = {
@@ -155,6 +158,8 @@ static const uint8_t INSTRUMENTED_OPCODES[256] = {
[INSTRUMENTED_FOR_ITER] = INSTRUMENTED_FOR_ITER,
[LOAD_SUPER_ATTR] = INSTRUMENTED_LOAD_SUPER_ATTR,
[INSTRUMENTED_LOAD_SUPER_ATTR] = INSTRUMENTED_LOAD_SUPER_ATTR,
+ [NOT_TAKEN] = INSTRUMENTED_NOT_TAKEN,
+ [INSTRUMENTED_NOT_TAKEN] = INSTRUMENTED_NOT_TAKEN,
[INSTRUMENTED_LINE] = INSTRUMENTED_LINE,
[INSTRUMENTED_INSTRUCTION] = INSTRUMENTED_INSTRUCTION,
@@ -323,33 +328,8 @@ _PyInstruction_GetLength(PyCodeObject *code, int offset)
{
ASSERT_WORLD_STOPPED_OR_LOCKED(code);
- int opcode =
- FT_ATOMIC_LOAD_UINT8_RELAXED(_PyCode_CODE(code)[offset].op.code);
- assert(opcode != 0);
- assert(opcode != RESERVED);
- if (opcode == INSTRUMENTED_LINE) {
- opcode = code->_co_monitoring->lines[offset].original_opcode;
- }
- if (opcode == INSTRUMENTED_INSTRUCTION) {
- opcode = code->_co_monitoring->per_instruction_opcodes[offset];
- }
- int deinstrumented = DE_INSTRUMENT[opcode];
- if (deinstrumented) {
- opcode = deinstrumented;
- }
- else {
- opcode = _PyOpcode_Deopt[opcode];
- }
- assert(opcode != 0);
- if (opcode == ENTER_EXECUTOR) {
- int exec_index = _PyCode_CODE(code)[offset].op.arg;
- _PyExecutorObject *exec = code->co_executors->executors[exec_index];
- opcode = _PyOpcode_Deopt[exec->vm_data.opcode];
- }
- assert(!is_instrumented(opcode));
- assert(opcode != ENTER_EXECUTOR);
- assert(opcode == _PyOpcode_Deopt[opcode]);
- return 1 + _PyOpcode_Caches[opcode];
+ _Py_CODEUNIT inst = _Py_GetBaseCodeUnit(code, offset);
+ return 1 + _PyOpcode_Caches[inst.op.code];
}
#ifdef INSTRUMENT_DEBUG
@@ -599,16 +579,15 @@ _Py_GetBaseCodeUnit(PyCodeObject *code, int i)
int opcode = inst.op.code;
if (opcode < MIN_INSTRUMENTED_OPCODE) {
inst.op.code = _PyOpcode_Deopt[opcode];
- assert(inst.op.code <= RESUME);
+ assert(inst.op.code < MIN_SPECIALIZED_OPCODE);
return inst;
}
if (opcode == ENTER_EXECUTOR) {
_PyExecutorObject *exec = code->co_executors->executors[inst.op.arg];
opcode = _PyOpcode_Deopt[exec->vm_data.opcode];
inst.op.code = opcode;
- assert(opcode <= RESUME);
inst.op.arg = exec->vm_data.oparg;
- assert(inst.op.code <= RESUME);
+ assert(inst.op.code < MIN_SPECIALIZED_OPCODE);
return inst;
}
if (opcode == INSTRUMENTED_LINE) {
@@ -1084,6 +1063,8 @@ static const char *const event_names [] = {
[PY_MONITORING_EVENT_INSTRUCTION] = "INSTRUCTION",
[PY_MONITORING_EVENT_JUMP] = "JUMP",
[PY_MONITORING_EVENT_BRANCH] = "BRANCH",
+ [PY_MONITORING_EVENT_BRANCH_LEFT] = "BRANCH_LEFT",
+ [PY_MONITORING_EVENT_BRANCH_RIGHT] = "BRANCH_RIGHT",
[PY_MONITORING_EVENT_C_RETURN] = "C_RETURN",
[PY_MONITORING_EVENT_PY_THROW] = "PY_THROW",
[PY_MONITORING_EVENT_RAISE] = "RAISE",
@@ -1111,6 +1092,10 @@ call_instrumentation_vector(
/* 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) {
return -1;
@@ -1191,7 +1176,8 @@ _Py_call_instrumentation_jump(
_PyInterpreterFrame *frame, _Py_CODEUNIT *instr, _Py_CODEUNIT *target)
{
assert(event == PY_MONITORING_EVENT_JUMP ||
- event == PY_MONITORING_EVENT_BRANCH);
+ event == PY_MONITORING_EVENT_BRANCH_RIGHT ||
+ event == PY_MONITORING_EVENT_BRANCH_LEFT);
assert(frame->instr_ptr == instr);
int to = (int)(target - _PyFrame_GetBytecode(frame));
PyObject *to_obj = PyLong_FromLong(to * (int)sizeof(_Py_CODEUNIT));
@@ -1427,19 +1413,6 @@ _Py_call_instrumentation_instruction(PyThreadState *tstate, _PyInterpreterFrame*
return next_opcode;
}
-
-PyObject *
-_PyMonitoring_RegisterCallback(int tool_id, int event_id, PyObject *obj)
-{
- PyInterpreterState *is = _PyInterpreterState_GET();
- assert(0 <= tool_id && tool_id < PY_MONITORING_TOOL_IDS);
- assert(0 <= event_id && event_id < _PY_MONITORING_EVENTS);
- PyObject *callback = _Py_atomic_exchange_ptr(&is->monitoring_callables[tool_id][event_id],
- Py_XNewRef(obj));
-
- return callback;
-}
-
static void
initialize_tools(PyCodeObject *code)
{
@@ -2312,6 +2285,10 @@ monitoring_set_events_impl(PyObject *module, int tool_id, int event_set)
return NULL;
}
event_set &= ~C_RETURN_EVENTS;
+ if (event_set & (1 << PY_MONITORING_EVENT_BRANCH)) {
+ event_set &= ~(1 << PY_MONITORING_EVENT_BRANCH);
+ event_set |= (1 << PY_MONITORING_EVENT_BRANCH_RIGHT) | (1 << PY_MONITORING_EVENT_BRANCH_LEFT);
+ }
if (_PyMonitoring_SetEvents(tool_id, event_set)) {
return NULL;
}
@@ -2384,6 +2361,10 @@ monitoring_set_local_events_impl(PyObject *module, int tool_id,
return NULL;
}
event_set &= ~C_RETURN_EVENTS;
+ if (event_set & (1 << PY_MONITORING_EVENT_BRANCH)) {
+ event_set &= ~(1 << PY_MONITORING_EVENT_BRANCH);
+ event_set |= (1 << PY_MONITORING_EVENT_BRANCH_RIGHT) | (1 << PY_MONITORING_EVENT_BRANCH_LEFT);
+ }
if (event_set < 0 || event_set >= (1 << _PY_MONITORING_LOCAL_EVENTS)) {
PyErr_Format(PyExc_ValueError, "invalid local event set 0x%x", event_set);
return NULL;
@@ -2711,7 +2692,27 @@ _PyMonitoring_FireBranchEvent(PyMonitoringState *state, PyObject *codelike, int3
assert(state->active);
PyObject *args[4] = { NULL, NULL, NULL, target_offset };
return capi_call_instrumentation(state, codelike, offset, args, 3,
- PY_MONITORING_EVENT_BRANCH);
+ PY_MONITORING_EVENT_BRANCH_RIGHT);
+}
+
+int
+_PyMonitoring_FireBranchRightEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset,
+ PyObject *target_offset)
+{
+ assert(state->active);
+ PyObject *args[4] = { NULL, NULL, NULL, target_offset };
+ return capi_call_instrumentation(state, codelike, offset, args, 3,
+ PY_MONITORING_EVENT_BRANCH_RIGHT);
+}
+
+int
+_PyMonitoring_FireBranchLeftEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset,
+ PyObject *target_offset)
+{
+ assert(state->active);
+ PyObject *args[4] = { NULL, NULL, NULL, target_offset };
+ return capi_call_instrumentation(state, codelike, offset, args, 3,
+ PY_MONITORING_EVENT_BRANCH_LEFT);
}
int
@@ -2849,3 +2850,213 @@ _PyMonitoring_FireStopIterationEvent(PyMonitoringState *state, PyObject *codelik
Py_DECREF(exc);
return exception_event_teardown(err, NULL);
}
+
+
+
+/* Handle legacy BRANCH event */
+
+typedef struct _PyLegacyBranchEventHandler {
+ PyObject_HEAD
+ vectorcallfunc vectorcall;
+ PyObject *handler;
+ bool right;
+ int tool_id;
+} _PyLegacyBranchEventHandler;
+
+static void
+dealloc_branch_handler(_PyLegacyBranchEventHandler *self)
+{
+ Py_CLEAR(self->handler);
+ PyObject_Free((PyObject *)self);
+}
+
+static PyTypeObject _PyLegacyBranchEventHandler_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ "sys.monitoring.branch_event_handler",
+ sizeof(_PyLegacyBranchEventHandler),
+ .tp_dealloc = (destructor)dealloc_branch_handler,
+ .tp_vectorcall_offset = offsetof(_PyLegacyBranchEventHandler, vectorcall),
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
+ Py_TPFLAGS_HAVE_VECTORCALL | Py_TPFLAGS_DISALLOW_INSTANTIATION,
+ .tp_call = PyVectorcall_Call,
+};
+
+
+static PyObject *
+branch_handler(
+ _PyLegacyBranchEventHandler *self, PyObject *const *args,
+ size_t nargsf, PyObject *kwnames
+) {
+ 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;
+ }
+ if (offset >= Py_SIZE(code)) {
+ 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();
+ }
+ return res;
+}
+
+static PyObject *make_branch_handler(int tool_id, PyObject *handler, bool right)
+{
+ _PyLegacyBranchEventHandler *callback =
+ PyObject_NEW(_PyLegacyBranchEventHandler, &_PyLegacyBranchEventHandler_Type);
+ if (callback == NULL) {
+ return NULL;
+ }
+ callback->vectorcall = (vectorcallfunc)branch_handler;
+ callback->handler = Py_NewRef(handler);
+ callback->right = right;
+ callback->tool_id = tool_id;
+ return (PyObject *)callback;
+}
+
+/* Consumes a reference to obj */
+static PyObject *exchange_callables(int tool_id, int event_id, PyObject *obj)
+{
+ PyInterpreterState *is = _PyInterpreterState_GET();
+ return _Py_atomic_exchange_ptr(&is->monitoring_callables[tool_id][event_id], obj);
+}
+
+PyObject *
+_PyMonitoring_RegisterCallback(int tool_id, int event_id, PyObject *obj)
+{
+ assert(0 <= tool_id && tool_id < PY_MONITORING_TOOL_IDS);
+ assert(0 <= event_id && event_id < _PY_MONITORING_EVENTS);
+ PyObject *res;
+ if (event_id == PY_MONITORING_EVENT_BRANCH) {
+ PyObject *left, *right;
+ if (obj == NULL) {
+ left = NULL;
+ right = NULL;
+ }
+ else {
+ right = make_branch_handler(tool_id, obj, true);
+ if (right == NULL) {
+ return NULL;
+ }
+ left = make_branch_handler(tool_id, obj, false);
+ if (left == NULL) {
+ Py_DECREF(right);
+ return NULL;
+ }
+ }
+ Py_XDECREF(exchange_callables(tool_id, PY_MONITORING_EVENT_BRANCH_RIGHT, right));
+ res = exchange_callables(tool_id, PY_MONITORING_EVENT_BRANCH_LEFT, left);
+ }
+ else {
+ res = exchange_callables(tool_id, event_id, Py_XNewRef(obj));
+ }
+ if (res != NULL && Py_TYPE(res) == &_PyLegacyBranchEventHandler_Type) {
+ _PyLegacyBranchEventHandler *wrapper = (_PyLegacyBranchEventHandler *)res;
+ res = Py_NewRef(wrapper->handler);
+ Py_DECREF(wrapper);
+ }
+ return res;
+}
+
+/* Branch Iterator */
+
+typedef struct {
+ PyObject_HEAD
+ PyCodeObject *bi_code;
+ int bi_offset;
+} branchesiterator;
+
+static PyObject *
+int_triple(int a, int b, int c) {
+ PyObject *obja = PyLong_FromLong(a);
+ PyObject *objb = NULL;
+ PyObject *objc = NULL;
+ if (obja == NULL) {
+ goto error;
+ }
+ objb = PyLong_FromLong(b);
+ if (objb == NULL) {
+ goto error;
+ }
+ objc = PyLong_FromLong(c);
+ if (objc == NULL) {
+ goto error;
+ }
+ PyObject *array[3] = { obja, objb, objc };
+ return _PyTuple_FromArraySteal(array, 3);
+error:
+ Py_XDECREF(obja);
+ Py_XDECREF(objb);
+ Py_XDECREF(objc);
+ return NULL;
+}
+
+static PyObject *
+branchesiter_next(branchesiterator *bi)
+{
+ int offset = bi->bi_offset;
+ 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);
+ }
+ offset = next_offset;
+ }
+ return NULL;
+}
+
+static void
+branchesiter_dealloc(branchesiterator *bi)
+{
+ Py_DECREF(bi->bi_code);
+ PyObject_Free(bi);
+}
+
+static PyTypeObject _PyBranchesIterator = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ "line_iterator", /* tp_name */
+ sizeof(branchesiterator), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ .tp_dealloc = (destructor)branchesiter_dealloc,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .tp_iter = PyObject_SelfIter,
+ .tp_iternext = (iternextfunc)branchesiter_next,
+ .tp_free = PyObject_Del,
+};
+
+PyObject *
+_PyInstrumentation_BranchesIterator(PyCodeObject *code)
+{
+
+ branchesiterator *bi = (branchesiterator *)PyType_GenericAlloc(&_PyBranchesIterator, 0);
+ if (bi == NULL) {
+ return NULL;
+ }
+ bi->bi_code = (PyCodeObject*)Py_NewRef(code);
+ bi->bi_offset = 0;
+ return (PyObject *)bi;
+}
diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h
index c93941d..7f3fb9c 100644
--- a/Python/opcode_targets.h
+++ b/Python/opcode_targets.h
@@ -27,6 +27,7 @@ static void *opcode_targets[256] = {
&&TARGET_MATCH_MAPPING,
&&TARGET_MATCH_SEQUENCE,
&&TARGET_NOP,
+ &&TARGET_NOT_TAKEN,
&&TARGET_POP_EXCEPT,
&&TARGET_POP_TOP,
&&TARGET_PUSH_EXC_INFO,
@@ -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,
@@ -235,7 +235,6 @@ static void *opcode_targets[256] = {
&&_unknown_opcode,
&&_unknown_opcode,
&&_unknown_opcode,
- &&_unknown_opcode,
&&TARGET_INSTRUMENTED_END_FOR,
&&TARGET_INSTRUMENTED_END_SEND,
&&TARGET_INSTRUMENTED_LOAD_SUPER_ATTR,
@@ -244,6 +243,7 @@ static void *opcode_targets[256] = {
&&TARGET_INSTRUMENTED_CALL_FUNCTION_EX,
&&TARGET_INSTRUMENTED_INSTRUCTION,
&&TARGET_INSTRUMENTED_JUMP_FORWARD,
+ &&TARGET_INSTRUMENTED_NOT_TAKEN,
&&TARGET_INSTRUMENTED_POP_JUMP_IF_TRUE,
&&TARGET_INSTRUMENTED_POP_JUMP_IF_FALSE,
&&TARGET_INSTRUMENTED_POP_JUMP_IF_NONE,
diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h
index 33b34d6..2c3133d 100644
--- a/Python/optimizer_cases.c.h
+++ b/Python/optimizer_cases.c.h
@@ -2343,6 +2343,8 @@
/* _MONITOR_JUMP_BACKWARD is not a viable micro-op for tier 2 */
+ /* _INSTRUMENTED_NOT_TAKEN is not a viable micro-op for tier 2 */
+
/* _INSTRUMENTED_POP_JUMP_IF_TRUE is not a viable micro-op for tier 2 */
/* _INSTRUMENTED_POP_JUMP_IF_FALSE is not a viable micro-op for tier 2 */