gh-146393: Optimize float division operations by mutating uniquely-referenced operands in place (JIT only) (GH-146397)

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Pieter Eendebak 2026-04-14 20:08:04 +02:00 committed by GitHub
parent bdb0b36192
commit 95cbd4a232
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 1940 additions and 1263 deletions

View file

@ -895,6 +895,53 @@ dummy_func(
INPUTS_DEAD();
}
// Float true division — not specialized at tier 1, emitted by the
// tier 2 optimizer when both operands are known floats.
tier2 op(_BINARY_OP_TRUEDIV_FLOAT, (left, right -- res, l, r)) {
PyObject *left_o = PyStackRef_AsPyObjectBorrow(left);
PyObject *right_o = PyStackRef_AsPyObjectBorrow(right);
assert(PyFloat_CheckExact(left_o));
assert(PyFloat_CheckExact(right_o));
STAT_INC(BINARY_OP, hit);
double divisor = ((PyFloatObject *)right_o)->ob_fval;
if (divisor == 0.0) {
PyErr_SetString(PyExc_ZeroDivisionError,
"float division by zero");
ERROR_NO_POP();
}
double dres = ((PyFloatObject *)left_o)->ob_fval / divisor;
PyObject *d = PyFloat_FromDouble(dres);
if (d == NULL) {
ERROR_NO_POP();
}
res = PyStackRef_FromPyObjectSteal(d);
l = left;
r = right;
INPUTS_DEAD();
}
tier2 op(_BINARY_OP_TRUEDIV_FLOAT_INPLACE, (left, right -- res, l, r)) {
FLOAT_INPLACE_DIVOP(left, right, left);
if (_divop_err) {
ERROR_NO_POP();
}
res = left;
l = PyStackRef_NULL;
r = right;
INPUTS_DEAD();
}
tier2 op(_BINARY_OP_TRUEDIV_FLOAT_INPLACE_RIGHT, (left, right -- res, l, r)) {
FLOAT_INPLACE_DIVOP(left, right, right);
if (_divop_err) {
ERROR_NO_POP();
}
res = right;
l = left;
r = PyStackRef_NULL;
INPUTS_DEAD();
}
pure op(_BINARY_OP_ADD_UNICODE, (left, right -- res, l, r)) {
PyObject *left_o = PyStackRef_AsPyObjectBorrow(left);
PyObject *right_o = PyStackRef_AsPyObjectBorrow(right);
@ -983,6 +1030,11 @@ dummy_func(
if (res_o == NULL) {
ERROR_NO_POP();
}
// The JIT and tier 2 optimizer assume that float results from
// binary operations are always uniquely referenced (refcount == 1).
// If this assertion fails, update the optimizer to stop marking
// float results as unique in optimizer_bytecodes.c.
assert(!PyFloat_CheckExact(res_o) || Py_REFCNT(res_o) == 1);
res = PyStackRef_FromPyObjectSteal(res_o);
l = left;
r = right;
@ -5673,7 +5725,7 @@ dummy_func(
DEAD(rhs);
}
macro(BINARY_OP) = _SPECIALIZE_BINARY_OP + unused/4 + _BINARY_OP + POP_TOP + POP_TOP;
macro(BINARY_OP) = _SPECIALIZE_BINARY_OP + _RECORD_TOS + _RECORD_NOS + unused/4 + _BINARY_OP + POP_TOP + POP_TOP;
pure replicate(2:4) inst(SWAP, (bottom, unused[oparg-2], top --
bottom, unused[oparg-2], top)) {

View file

@ -563,6 +563,30 @@ gen_try_set_executing(PyGenObject *gen)
->ob_fval = _dres; \
} while (0)
// Inplace float true division. Sets _divop_err to 1 on zero division.
// Caller must check _divop_err and call ERROR_NO_POP() if set.
#define FLOAT_INPLACE_DIVOP(left, right, TARGET) \
int _divop_err = 0; \
do { \
PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); \
PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); \
assert(PyFloat_CheckExact(left_o)); \
assert(PyFloat_CheckExact(right_o)); \
assert(_PyObject_IsUniquelyReferenced( \
PyStackRef_AsPyObjectBorrow(TARGET))); \
STAT_INC(BINARY_OP, hit); \
double _divisor = ((PyFloatObject *)right_o)->ob_fval; \
if (_divisor == 0.0) { \
PyErr_SetString(PyExc_ZeroDivisionError, \
"float division by zero"); \
_divop_err = 1; \
break; \
} \
double _dres = ((PyFloatObject *)left_o)->ob_fval / _divisor; \
((PyFloatObject *)PyStackRef_AsPyObjectBorrow(TARGET)) \
->ob_fval = _dres; \
} while (0)
// Inplace compact int operation. TARGET is expected to be uniquely
// referenced at the optimizer level, but at runtime it may be a
// cached small int singleton. We check _Py_IsImmortal on TARGET
@ -604,4 +628,3 @@ gen_try_set_executing(PyGenObject *gen)
(PyLongObject *)PyStackRef_AsPyObjectBorrow(left), \
(PyLongObject *)PyStackRef_AsPyObjectBorrow(right)); \
}

View file

@ -6001,6 +6001,241 @@
break;
}
case _BINARY_OP_TRUEDIV_FLOAT_r23: {
CHECK_CURRENT_CACHED_VALUES(2);
assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE());
_PyStackRef right;
_PyStackRef left;
_PyStackRef res;
_PyStackRef l;
_PyStackRef r;
_PyStackRef _stack_item_0 = _tos_cache0;
_PyStackRef _stack_item_1 = _tos_cache1;
right = _stack_item_1;
left = _stack_item_0;
PyObject *left_o = PyStackRef_AsPyObjectBorrow(left);
PyObject *right_o = PyStackRef_AsPyObjectBorrow(right);
assert(PyFloat_CheckExact(left_o));
assert(PyFloat_CheckExact(right_o));
STAT_INC(BINARY_OP, hit);
double divisor = ((PyFloatObject *)right_o)->ob_fval;
if (divisor == 0.0) {
stack_pointer[0] = left;
stack_pointer[1] = right;
stack_pointer += 2;
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
_PyFrame_SetStackPointer(frame, stack_pointer);
PyErr_SetString(PyExc_ZeroDivisionError,
"float division by zero");
stack_pointer = _PyFrame_GetStackPointer(frame);
SET_CURRENT_CACHED_VALUES(0);
JUMP_TO_ERROR();
}
double dres = ((PyFloatObject *)left_o)->ob_fval / divisor;
PyObject *d = PyFloat_FromDouble(dres);
if (d == NULL) {
stack_pointer[0] = left;
stack_pointer[1] = right;
stack_pointer += 2;
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
SET_CURRENT_CACHED_VALUES(0);
JUMP_TO_ERROR();
}
res = PyStackRef_FromPyObjectSteal(d);
l = left;
r = right;
_tos_cache2 = r;
_tos_cache1 = l;
_tos_cache0 = res;
SET_CURRENT_CACHED_VALUES(3);
assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE());
break;
}
case _BINARY_OP_TRUEDIV_FLOAT_INPLACE_r03: {
CHECK_CURRENT_CACHED_VALUES(0);
assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE());
_PyStackRef right;
_PyStackRef left;
_PyStackRef res;
_PyStackRef l;
_PyStackRef r;
right = stack_pointer[-1];
left = stack_pointer[-2];
FLOAT_INPLACE_DIVOP(left, right, left);
if (_divop_err) {
SET_CURRENT_CACHED_VALUES(0);
JUMP_TO_ERROR();
}
res = left;
l = PyStackRef_NULL;
r = right;
_tos_cache2 = r;
_tos_cache1 = l;
_tos_cache0 = res;
SET_CURRENT_CACHED_VALUES(3);
stack_pointer += -2;
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE());
break;
}
case _BINARY_OP_TRUEDIV_FLOAT_INPLACE_r13: {
CHECK_CURRENT_CACHED_VALUES(1);
assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE());
_PyStackRef right;
_PyStackRef left;
_PyStackRef res;
_PyStackRef l;
_PyStackRef r;
_PyStackRef _stack_item_0 = _tos_cache0;
right = _stack_item_0;
left = stack_pointer[-1];
FLOAT_INPLACE_DIVOP(left, right, left);
if (_divop_err) {
stack_pointer[0] = right;
stack_pointer += 1;
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
SET_CURRENT_CACHED_VALUES(0);
JUMP_TO_ERROR();
}
res = left;
l = PyStackRef_NULL;
r = right;
_tos_cache2 = r;
_tos_cache1 = l;
_tos_cache0 = res;
SET_CURRENT_CACHED_VALUES(3);
stack_pointer += -1;
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE());
break;
}
case _BINARY_OP_TRUEDIV_FLOAT_INPLACE_r23: {
CHECK_CURRENT_CACHED_VALUES(2);
assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE());
_PyStackRef right;
_PyStackRef left;
_PyStackRef res;
_PyStackRef l;
_PyStackRef r;
_PyStackRef _stack_item_0 = _tos_cache0;
_PyStackRef _stack_item_1 = _tos_cache1;
right = _stack_item_1;
left = _stack_item_0;
FLOAT_INPLACE_DIVOP(left, right, left);
if (_divop_err) {
stack_pointer[0] = left;
stack_pointer[1] = right;
stack_pointer += 2;
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
SET_CURRENT_CACHED_VALUES(0);
JUMP_TO_ERROR();
}
res = left;
l = PyStackRef_NULL;
r = right;
_tos_cache2 = r;
_tos_cache1 = l;
_tos_cache0 = res;
SET_CURRENT_CACHED_VALUES(3);
assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE());
break;
}
case _BINARY_OP_TRUEDIV_FLOAT_INPLACE_RIGHT_r03: {
CHECK_CURRENT_CACHED_VALUES(0);
assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE());
_PyStackRef right;
_PyStackRef left;
_PyStackRef res;
_PyStackRef l;
_PyStackRef r;
right = stack_pointer[-1];
left = stack_pointer[-2];
FLOAT_INPLACE_DIVOP(left, right, right);
if (_divop_err) {
SET_CURRENT_CACHED_VALUES(0);
JUMP_TO_ERROR();
}
res = right;
l = left;
r = PyStackRef_NULL;
_tos_cache2 = r;
_tos_cache1 = l;
_tos_cache0 = res;
SET_CURRENT_CACHED_VALUES(3);
stack_pointer += -2;
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE());
break;
}
case _BINARY_OP_TRUEDIV_FLOAT_INPLACE_RIGHT_r13: {
CHECK_CURRENT_CACHED_VALUES(1);
assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE());
_PyStackRef right;
_PyStackRef left;
_PyStackRef res;
_PyStackRef l;
_PyStackRef r;
_PyStackRef _stack_item_0 = _tos_cache0;
right = _stack_item_0;
left = stack_pointer[-1];
FLOAT_INPLACE_DIVOP(left, right, right);
if (_divop_err) {
stack_pointer[0] = right;
stack_pointer += 1;
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
SET_CURRENT_CACHED_VALUES(0);
JUMP_TO_ERROR();
}
res = right;
l = left;
r = PyStackRef_NULL;
_tos_cache2 = r;
_tos_cache1 = l;
_tos_cache0 = res;
SET_CURRENT_CACHED_VALUES(3);
stack_pointer += -1;
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE());
break;
}
case _BINARY_OP_TRUEDIV_FLOAT_INPLACE_RIGHT_r23: {
CHECK_CURRENT_CACHED_VALUES(2);
assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE());
_PyStackRef right;
_PyStackRef left;
_PyStackRef res;
_PyStackRef l;
_PyStackRef r;
_PyStackRef _stack_item_0 = _tos_cache0;
_PyStackRef _stack_item_1 = _tos_cache1;
right = _stack_item_1;
left = _stack_item_0;
FLOAT_INPLACE_DIVOP(left, right, right);
if (_divop_err) {
stack_pointer[0] = left;
stack_pointer[1] = right;
stack_pointer += 2;
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
SET_CURRENT_CACHED_VALUES(0);
JUMP_TO_ERROR();
}
res = right;
l = left;
r = PyStackRef_NULL;
_tos_cache2 = r;
_tos_cache1 = l;
_tos_cache0 = res;
SET_CURRENT_CACHED_VALUES(3);
assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE());
break;
}
case _BINARY_OP_ADD_UNICODE_r03: {
CHECK_CURRENT_CACHED_VALUES(0);
assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE());
@ -6230,6 +6465,7 @@
SET_CURRENT_CACHED_VALUES(0);
JUMP_TO_ERROR();
}
assert(!PyFloat_CheckExact(res_o) || Py_REFCNT(res_o) == 1);
res = PyStackRef_FromPyObjectSteal(res_o);
l = left;
r = right;

View file

@ -367,6 +367,7 @@
if (res_o == NULL) {
JUMP_TO_LABEL(error);
}
assert(!PyFloat_CheckExact(res_o) || Py_REFCNT(res_o) == 1);
res = PyStackRef_FromPyObjectSteal(res_o);
l = left;
r = right;

View file

@ -289,7 +289,56 @@ dummy_func(void) {
bool rhs_int = sym_matches_type(rhs, &PyLong_Type);
bool lhs_float = sym_matches_type(lhs, &PyFloat_Type);
bool rhs_float = sym_matches_type(rhs, &PyFloat_Type);
if (!((lhs_int || lhs_float) && (rhs_int || rhs_float))) {
bool is_truediv = (oparg == NB_TRUE_DIVIDE
|| oparg == NB_INPLACE_TRUE_DIVIDE);
bool is_remainder = (oparg == NB_REMAINDER
|| oparg == NB_INPLACE_REMAINDER);
// Promote probable-float operands to known floats via speculative
// guards. _RECORD_TOS / _RECORD_NOS in the BINARY_OP macro record
// the observed operand during tracing, which sym_get_probable_type
// reads here. Applied only to ops where narrowing unlocks a
// meaningful downstream win:
// - NB_TRUE_DIVIDE: enables the specialized float path below.
// - NB_REMAINDER: lets the float result type propagate.
// NB_POWER is excluded — speculative guards there regressed
// test_power_type_depends_on_input_values (GH-127844).
if (is_truediv || is_remainder) {
if (!sym_has_type(rhs)
&& sym_get_probable_type(rhs) == &PyFloat_Type) {
ADD_OP(_GUARD_TOS_FLOAT, 0, 0);
sym_set_type(rhs, &PyFloat_Type);
rhs_float = true;
}
if (!sym_has_type(lhs)
&& sym_get_probable_type(lhs) == &PyFloat_Type) {
ADD_OP(_GUARD_NOS_FLOAT, 0, 0);
sym_set_type(lhs, &PyFloat_Type);
lhs_float = true;
}
}
if (is_truediv && lhs_float && rhs_float) {
if (PyJitRef_IsUnique(lhs)) {
ADD_OP(_BINARY_OP_TRUEDIV_FLOAT_INPLACE, 0, 0);
l = sym_new_null(ctx);
r = rhs;
}
else if (PyJitRef_IsUnique(rhs)) {
ADD_OP(_BINARY_OP_TRUEDIV_FLOAT_INPLACE_RIGHT, 0, 0);
l = lhs;
r = sym_new_null(ctx);
}
else {
ADD_OP(_BINARY_OP_TRUEDIV_FLOAT, 0, 0);
l = lhs;
r = rhs;
}
res = PyJitRef_MakeUnique(sym_new_type(ctx, &PyFloat_Type));
}
else if (is_truediv
&& (lhs_int || lhs_float) && (rhs_int || rhs_float)) {
res = PyJitRef_MakeUnique(sym_new_type(ctx, &PyFloat_Type));
}
else if (!((lhs_int || lhs_float) && (rhs_int || rhs_float))) {
// There's something other than an int or float involved:
res = sym_new_unknown(ctx);
}
@ -312,7 +361,7 @@ dummy_func(void) {
}
else if (lhs_float) {
// Case C:
res = sym_new_type(ctx, &PyFloat_Type);
res = PyJitRef_MakeUnique(sym_new_type(ctx, &PyFloat_Type));
}
else if (!sym_is_const(ctx, rhs)) {
// Case A or B... can't know without the sign of the RHS:
@ -320,21 +369,18 @@ dummy_func(void) {
}
else if (_PyLong_IsNegative((PyLongObject *)sym_get_const(ctx, rhs))) {
// Case B:
res = sym_new_type(ctx, &PyFloat_Type);
res = PyJitRef_MakeUnique(sym_new_type(ctx, &PyFloat_Type));
}
else {
// Case A:
res = sym_new_type(ctx, &PyLong_Type);
}
}
else if (oparg == NB_TRUE_DIVIDE || oparg == NB_INPLACE_TRUE_DIVIDE) {
res = sym_new_type(ctx, &PyFloat_Type);
}
else if (lhs_int && rhs_int) {
res = sym_new_type(ctx, &PyLong_Type);
}
else {
res = sym_new_type(ctx, &PyFloat_Type);
res = PyJitRef_MakeUnique(sym_new_type(ctx, &PyFloat_Type));
}
}

View file

@ -1113,6 +1113,54 @@
break;
}
case _BINARY_OP_TRUEDIV_FLOAT: {
JitOptRef res;
JitOptRef l;
JitOptRef r;
res = sym_new_not_null(ctx);
l = sym_new_not_null(ctx);
r = sym_new_not_null(ctx);
CHECK_STACK_BOUNDS(1);
stack_pointer[-2] = res;
stack_pointer[-1] = l;
stack_pointer[0] = r;
stack_pointer += 1;
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
break;
}
case _BINARY_OP_TRUEDIV_FLOAT_INPLACE: {
JitOptRef res;
JitOptRef l;
JitOptRef r;
res = sym_new_not_null(ctx);
l = sym_new_not_null(ctx);
r = sym_new_not_null(ctx);
CHECK_STACK_BOUNDS(1);
stack_pointer[-2] = res;
stack_pointer[-1] = l;
stack_pointer[0] = r;
stack_pointer += 1;
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
break;
}
case _BINARY_OP_TRUEDIV_FLOAT_INPLACE_RIGHT: {
JitOptRef res;
JitOptRef l;
JitOptRef r;
res = sym_new_not_null(ctx);
l = sym_new_not_null(ctx);
r = sym_new_not_null(ctx);
CHECK_STACK_BOUNDS(1);
stack_pointer[-2] = res;
stack_pointer[-1] = l;
stack_pointer[0] = r;
stack_pointer += 1;
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
break;
}
case _BINARY_OP_ADD_UNICODE: {
JitOptRef right;
JitOptRef left;
@ -5007,7 +5055,47 @@
bool rhs_int = sym_matches_type(rhs, &PyLong_Type);
bool lhs_float = sym_matches_type(lhs, &PyFloat_Type);
bool rhs_float = sym_matches_type(rhs, &PyFloat_Type);
if (!((lhs_int || lhs_float) && (rhs_int || rhs_float))) {
bool is_truediv = (oparg == NB_TRUE_DIVIDE
|| oparg == NB_INPLACE_TRUE_DIVIDE);
bool is_remainder = (oparg == NB_REMAINDER
|| oparg == NB_INPLACE_REMAINDER);
if (is_truediv || is_remainder) {
if (!sym_has_type(rhs)
&& sym_get_probable_type(rhs) == &PyFloat_Type) {
ADD_OP(_GUARD_TOS_FLOAT, 0, 0);
sym_set_type(rhs, &PyFloat_Type);
rhs_float = true;
}
if (!sym_has_type(lhs)
&& sym_get_probable_type(lhs) == &PyFloat_Type) {
ADD_OP(_GUARD_NOS_FLOAT, 0, 0);
sym_set_type(lhs, &PyFloat_Type);
lhs_float = true;
}
}
if (is_truediv && lhs_float && rhs_float) {
if (PyJitRef_IsUnique(lhs)) {
ADD_OP(_BINARY_OP_TRUEDIV_FLOAT_INPLACE, 0, 0);
l = sym_new_null(ctx);
r = rhs;
}
else if (PyJitRef_IsUnique(rhs)) {
ADD_OP(_BINARY_OP_TRUEDIV_FLOAT_INPLACE_RIGHT, 0, 0);
l = lhs;
r = sym_new_null(ctx);
}
else {
ADD_OP(_BINARY_OP_TRUEDIV_FLOAT, 0, 0);
l = lhs;
r = rhs;
}
res = PyJitRef_MakeUnique(sym_new_type(ctx, &PyFloat_Type));
}
else if (is_truediv
&& (lhs_int || lhs_float) && (rhs_int || rhs_float)) {
res = PyJitRef_MakeUnique(sym_new_type(ctx, &PyFloat_Type));
}
else if (!((lhs_int || lhs_float) && (rhs_int || rhs_float))) {
res = sym_new_unknown(ctx);
}
else if (oparg == NB_POWER || oparg == NB_INPLACE_POWER) {
@ -5015,26 +5103,23 @@
res = sym_new_unknown(ctx);
}
else if (lhs_float) {
res = sym_new_type(ctx, &PyFloat_Type);
res = PyJitRef_MakeUnique(sym_new_type(ctx, &PyFloat_Type));
}
else if (!sym_is_const(ctx, rhs)) {
res = sym_new_unknown(ctx);
}
else if (_PyLong_IsNegative((PyLongObject *)sym_get_const(ctx, rhs))) {
res = sym_new_type(ctx, &PyFloat_Type);
res = PyJitRef_MakeUnique(sym_new_type(ctx, &PyFloat_Type));
}
else {
res = sym_new_type(ctx, &PyLong_Type);
}
}
else if (oparg == NB_TRUE_DIVIDE || oparg == NB_INPLACE_TRUE_DIVIDE) {
res = sym_new_type(ctx, &PyFloat_Type);
}
else if (lhs_int && rhs_int) {
res = sym_new_type(ctx, &PyLong_Type);
}
else {
res = sym_new_type(ctx, &PyFloat_Type);
res = PyJitRef_MakeUnique(sym_new_type(ctx, &PyFloat_Type));
}
CHECK_STACK_BOUNDS(1);
stack_pointer[-2] = res;

View file

@ -99,6 +99,7 @@ void _PyOpcode_RecordFunction_CODE(_PyInterpreterFrame *frame, _PyStackRef *stac
#define _RECORD_BOUND_METHOD_INDEX 6
#define _RECORD_CALLABLE_KW_INDEX 7
#define _RECORD_4OS_INDEX 8
#define _RECORD_TOS_INDEX 9
const _PyOpcodeRecordEntry _PyOpcode_RecordEntries[256] = {
[TO_BOOL_ALWAYS_TRUE] = {1, {_RECORD_TOS_TYPE_INDEX}},
@ -137,9 +138,10 @@ const _PyOpcodeRecordEntry _PyOpcode_RecordEntries[256] = {
[CALL_KW_PY] = {1, {_RECORD_CALLABLE_KW_INDEX}},
[CALL_KW_BOUND_METHOD] = {1, {_RECORD_CALLABLE_KW_INDEX}},
[CALL_EX_PY] = {1, {_RECORD_4OS_INDEX}},
[BINARY_OP] = {2, {_RECORD_TOS_INDEX, _RECORD_NOS_INDEX}},
};
const _Py_RecordFuncPtr _PyOpcode_RecordFunctions[9] = {
const _Py_RecordFuncPtr _PyOpcode_RecordFunctions[10] = {
[0] = NULL,
[_RECORD_TOS_TYPE_INDEX] = _PyOpcode_RecordFunction_TOS_TYPE,
[_RECORD_NOS_INDEX] = _PyOpcode_RecordFunction_NOS,
@ -149,4 +151,5 @@ const _Py_RecordFuncPtr _PyOpcode_RecordFunctions[9] = {
[_RECORD_BOUND_METHOD_INDEX] = _PyOpcode_RecordFunction_BOUND_METHOD,
[_RECORD_CALLABLE_KW_INDEX] = _PyOpcode_RecordFunction_CALLABLE_KW,
[_RECORD_4OS_INDEX] = _PyOpcode_RecordFunction_4OS,
[_RECORD_TOS_INDEX] = _PyOpcode_RecordFunction_TOS,
};