gh-146393: Use recorded type instead of instance in BINARY_OP (#148569)

This commit is contained in:
Pieter Eendebak 2026-04-14 22:11:42 +02:00 committed by GitHub
parent 0012686d92
commit 5c3decad66
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 1042 additions and 1013 deletions

View file

@ -1345,7 +1345,7 @@ extern const struct opcode_macro_expansion _PyOpcode_macro_expansion[256];
#ifdef NEED_OPCODE_METADATA
const struct opcode_macro_expansion
_PyOpcode_macro_expansion[256] = {
[BINARY_OP] = { .nuops = 5, .uops = { { _RECORD_TOS, OPARG_SIMPLE, 0 }, { _RECORD_NOS, OPARG_SIMPLE, 0 }, { _BINARY_OP, OPARG_SIMPLE, 4 }, { _POP_TOP, OPARG_SIMPLE, 4 }, { _POP_TOP, OPARG_SIMPLE, 4 } } },
[BINARY_OP] = { .nuops = 5, .uops = { { _RECORD_TOS_TYPE, OPARG_SIMPLE, 0 }, { _RECORD_NOS_TYPE, OPARG_SIMPLE, 0 }, { _BINARY_OP, OPARG_SIMPLE, 4 }, { _POP_TOP, OPARG_SIMPLE, 4 }, { _POP_TOP, OPARG_SIMPLE, 4 } } },
[BINARY_OP_ADD_FLOAT] = { .nuops = 5, .uops = { { _GUARD_TOS_FLOAT, OPARG_SIMPLE, 0 }, { _GUARD_NOS_FLOAT, OPARG_SIMPLE, 0 }, { _BINARY_OP_ADD_FLOAT, OPARG_SIMPLE, 5 }, { _POP_TOP_FLOAT, OPARG_SIMPLE, 5 }, { _POP_TOP_FLOAT, OPARG_SIMPLE, 5 } } },
[BINARY_OP_ADD_INT] = { .nuops = 5, .uops = { { _GUARD_TOS_INT, OPARG_SIMPLE, 0 }, { _GUARD_NOS_INT, OPARG_SIMPLE, 0 }, { _BINARY_OP_ADD_INT, OPARG_SIMPLE, 5 }, { _POP_TOP_INT, OPARG_SIMPLE, 5 }, { _POP_TOP_INT, OPARG_SIMPLE, 5 } } },
[BINARY_OP_ADD_UNICODE] = { .nuops = 5, .uops = { { _GUARD_TOS_UNICODE, OPARG_SIMPLE, 0 }, { _GUARD_NOS_UNICODE, OPARG_SIMPLE, 0 }, { _BINARY_OP_ADD_UNICODE, OPARG_SIMPLE, 5 }, { _POP_TOP_UNICODE, OPARG_SIMPLE, 5 }, { _POP_TOP_UNICODE, OPARG_SIMPLE, 5 } } },

File diff suppressed because it is too large Load diff

View file

@ -410,6 +410,7 @@ const uint32_t _PyUop_Flags[MAX_UOP_ID+1] = {
[_RECORD_TOS] = HAS_RECORDS_VALUE_FLAG,
[_RECORD_TOS_TYPE] = HAS_RECORDS_VALUE_FLAG,
[_RECORD_NOS] = HAS_RECORDS_VALUE_FLAG,
[_RECORD_NOS_TYPE] = HAS_RECORDS_VALUE_FLAG,
[_RECORD_NOS_GEN_FUNC] = HAS_RECORDS_VALUE_FLAG,
[_RECORD_3OS_GEN_FUNC] = HAS_RECORDS_VALUE_FLAG,
[_RECORD_4OS] = HAS_RECORDS_VALUE_FLAG,
@ -5736,6 +5737,7 @@ const char *const _PyOpcode_uop_name[MAX_UOP_REGS_ID+1] = {
[_RECORD_CODE] = "_RECORD_CODE",
[_RECORD_NOS] = "_RECORD_NOS",
[_RECORD_NOS_GEN_FUNC] = "_RECORD_NOS_GEN_FUNC",
[_RECORD_NOS_TYPE] = "_RECORD_NOS_TYPE",
[_RECORD_TOS] = "_RECORD_TOS",
[_RECORD_TOS_TYPE] = "_RECORD_TOS_TYPE",
[_REPLACE_WITH_TRUE] = "_REPLACE_WITH_TRUE",
@ -6694,6 +6696,8 @@ int _PyUop_num_popped(int opcode, int oparg)
return 0;
case _RECORD_NOS:
return 0;
case _RECORD_NOS_TYPE:
return 0;
case _RECORD_NOS_GEN_FUNC:
return 0;
case _RECORD_3OS_GEN_FUNC:

View file

@ -3708,8 +3708,8 @@ def testfunc(args):
self.assertIn("_BINARY_OP_TRUEDIV_FLOAT_INPLACE_RIGHT", uops)
def test_float_truediv_speculative_guards_from_tracing(self):
# a, b are locals with no statically known type. _RECORD_TOS /
# _RECORD_NOS (added to the BINARY_OP macro) capture the observed
# a, b are locals with no statically known type. _RECORD_TOS_TYPE /
# _RECORD_NOS_TYPE (added to the BINARY_OP macro) capture the observed
# operand types during tracing, and the optimizer then speculatively
# emits _GUARD_{TOS,NOS}_FLOAT and specializes the division.
def testfunc(args):

View file

@ -5725,7 +5725,7 @@ dummy_func(
DEAD(rhs);
}
macro(BINARY_OP) = _SPECIALIZE_BINARY_OP + _RECORD_TOS + _RECORD_NOS + unused/4 + _BINARY_OP + POP_TOP + POP_TOP;
macro(BINARY_OP) = _SPECIALIZE_BINARY_OP + _RECORD_TOS_TYPE + _RECORD_NOS_TYPE + unused/4 + _BINARY_OP + POP_TOP + POP_TOP;
pure replicate(2:4) inst(SWAP, (bottom, unused[oparg-2], top --
bottom, unused[oparg-2], top)) {
@ -6178,6 +6178,10 @@ dummy_func(
RECORD_VALUE(PyStackRef_AsPyObjectBorrow(nos));
}
tier2 op(_RECORD_NOS_TYPE, (nos, tos -- nos, tos)) {
RECORD_VALUE(Py_TYPE(PyStackRef_AsPyObjectBorrow(nos)));
}
tier2 op(_RECORD_NOS_GEN_FUNC, (nos, tos -- nos, tos)) {
PyObject *obj = PyStackRef_AsPyObjectBorrow(nos);
if (PyGen_Check(obj)) {

View file

@ -294,10 +294,10 @@ dummy_func(void) {
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:
// guards. _RECORD_TOS_TYPE / _RECORD_NOS_TYPE in the BINARY_OP macro
// record the observed operand type 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
@ -2424,6 +2424,11 @@ dummy_func(void) {
sym_set_recorded_value(nos, (PyObject *)this_instr->operand0);
}
op(_RECORD_NOS_TYPE, (nos, tos -- nos, tos)) {
PyTypeObject *tp = (PyTypeObject *)this_instr->operand0;
sym_set_recorded_type(nos, tp);
}
op(_RECORD_4OS, (value, _3os, nos, tos -- value, _3os, nos, tos)) {
sym_set_recorded_value(value, (PyObject *)this_instr->operand0);
}

View file

@ -5482,6 +5482,14 @@
break;
}
case _RECORD_NOS_TYPE: {
JitOptRef nos;
nos = stack_pointer[-2];
PyTypeObject *tp = (PyTypeObject *)this_instr->operand0;
sym_set_recorded_type(nos, tp);
break;
}
case _RECORD_NOS_GEN_FUNC: {
JitOptRef nos;
nos = stack_pointer[-2];

View file

@ -27,6 +27,13 @@ void _PyOpcode_RecordFunction_NOS(_PyInterpreterFrame *frame, _PyStackRef *stack
Py_INCREF(*recorded_value);
}
void _PyOpcode_RecordFunction_NOS_TYPE(_PyInterpreterFrame *frame, _PyStackRef *stack_pointer, int oparg, PyObject **recorded_value) {
_PyStackRef nos;
nos = stack_pointer[-2];
*recorded_value = (PyObject *)Py_TYPE(PyStackRef_AsPyObjectBorrow(nos));
Py_INCREF(*recorded_value);
}
void _PyOpcode_RecordFunction_NOS_GEN_FUNC(_PyInterpreterFrame *frame, _PyStackRef *stack_pointer, int oparg, PyObject **recorded_value) {
_PyStackRef nos;
nos = stack_pointer[-2];
@ -99,7 +106,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
#define _RECORD_NOS_TYPE_INDEX 9
const _PyOpcodeRecordEntry _PyOpcode_RecordEntries[256] = {
[TO_BOOL_ALWAYS_TRUE] = {1, {_RECORD_TOS_TYPE_INDEX}},
@ -138,7 +145,7 @@ 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}},
[BINARY_OP] = {2, {_RECORD_TOS_TYPE_INDEX, _RECORD_NOS_TYPE_INDEX}},
};
const _Py_RecordFuncPtr _PyOpcode_RecordFunctions[10] = {
@ -151,5 +158,5 @@ const _Py_RecordFuncPtr _PyOpcode_RecordFunctions[10] = {
[_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,
[_RECORD_NOS_TYPE_INDEX] = _PyOpcode_RecordFunction_NOS_TYPE,
};