GH-144179: Use recorded values to make optimizer more robust (GH-144437)

* Add three new symbol kinds
* Do not smuggle code object in _PUSH_FRAME operand
* Fix small bug in predicate analysis
This commit is contained in:
Mark Shannon 2026-02-05 08:58:41 +00:00 committed by GitHub
parent b6d8aa436b
commit b53fc7caa6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 2062 additions and 1507 deletions

View file

@ -1115,8 +1115,21 @@
}
case _BINARY_OP_SUBSCR_CHECK_FUNC: {
JitOptRef container;
JitOptRef getitem;
container = stack_pointer[-2];
getitem = sym_new_not_null(ctx);
PyTypeObject *tp = sym_get_type(container);
if (tp == NULL) {
PyObject *c = sym_get_probable_value(container);
if (c != NULL) {
tp = Py_TYPE(c);
}
}
if (tp != NULL) {
PyObject *getitem_o = ((PyHeapTypeObject *)tp)->_spec_cache.getitem;
sym_set_recorded_value(getitem, getitem_o);
}
CHECK_STACK_BOUNDS(1);
stack_pointer[0] = getitem;
stack_pointer += 1;
@ -1125,18 +1138,14 @@
}
case _BINARY_OP_SUBSCR_INIT_CALL: {
JitOptRef getitem;
JitOptRef sub;
JitOptRef container;
JitOptRef new_frame;
getitem = stack_pointer[-1];
sub = stack_pointer[-2];
container = stack_pointer[-3];
assert((this_instr + 1)->opcode == _PUSH_FRAME);
PyCodeObject *co = get_code_with_logging(this_instr + 1);
if (co == NULL) {
ctx->done = true;
break;
}
_Py_UOpsAbstractFrame *f = frame_new(ctx, co, 0, NULL, 0);
_Py_UOpsAbstractFrame *f = frame_new_from_symbol(ctx, getitem, 0, NULL, 0);
if (f == NULL) {
break;
}
@ -1239,7 +1248,9 @@
stack_pointer += -1;
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
ctx->frame->stack_pointer = stack_pointer;
PyCodeObject *returning_code = get_code_with_logging(this_instr);
assert(this_instr[1].opcode == _RECORD_CODE);
PyCodeObject *returning_code = (PyCodeObject *)this_instr[1].operand0;
assert(PyCode_Check(returning_code));
if (returning_code == NULL) {
ctx->done = true;
break;
@ -1248,8 +1259,8 @@
if (ctx->curr_frame_depth >= 2) {
PyCodeObject *expected_code = ctx->frames[ctx->curr_frame_depth - 2].code;
if (expected_code == returning_code) {
assert((this_instr + 1)->opcode == _GUARD_IP_RETURN_VALUE);
REPLACE_OP((this_instr + 1), _NOP, 0, 0);
assert(this_instr[2].opcode == _GUARD_IP_RETURN_VALUE);
REPLACE_OP((this_instr + 2), _NOP, 0, 0);
}
}
if (frame_pop(ctx, returning_code, returning_stacklevel)) {
@ -1292,15 +1303,11 @@
case _SEND_GEN_FRAME: {
JitOptRef v;
JitOptRef receiver;
JitOptRef gen_frame;
v = stack_pointer[-1];
assert((this_instr + 1)->opcode == _PUSH_FRAME);
PyCodeObject *co = get_code_with_logging((this_instr + 1));
if (co == NULL) {
ctx->done = true;
break;
}
_Py_UOpsAbstractFrame *new_frame = frame_new(ctx, co, 1, NULL, 0);
receiver = stack_pointer[-2];
_Py_UOpsAbstractFrame *new_frame = frame_new_from_symbol(ctx, receiver, 1, NULL, 0);
if (new_frame == NULL) {
ctx->done = true;
break;
@ -1320,7 +1327,9 @@
stack_pointer += -1;
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
ctx->frame->stack_pointer = stack_pointer;
PyCodeObject *returning_code = get_code_with_logging(this_instr);
assert(this_instr[1].opcode == _RECORD_CODE);
PyCodeObject *returning_code = (PyCodeObject *)this_instr[1].operand0;
assert(PyCode_Check(returning_code));
if (returning_code == NULL) {
ctx->done = true;
break;
@ -1984,12 +1993,7 @@
JitOptRef new_frame;
owner = stack_pointer[-1];
PyObject *fget = (PyObject *)this_instr->operand0;
assert((this_instr + 2)->opcode == _PUSH_FRAME);
PyCodeObject *co = get_code_with_logging(this_instr + 2);
if (co == NULL) {
ctx->done = true;
break;
}
PyCodeObject *co = (PyCodeObject *)((PyFunctionObject *)fget)->func_code;
_Py_UOpsAbstractFrame *f = frame_new(ctx, co, 0, NULL, 0);
if (f == NULL) {
break;
@ -2267,16 +2271,7 @@
JitOptRef r;
right = stack_pointer[-1];
left = stack_pointer[-2];
int cmp_mask = oparg & (COMPARE_LT_MASK | COMPARE_GT_MASK | COMPARE_EQ_MASK);
if (cmp_mask == COMPARE_EQ_MASK) {
res = sym_new_predicate(ctx, left, right, JIT_PRED_EQ);
}
else if (cmp_mask == (COMPARE_LT_MASK | COMPARE_GT_MASK)) {
res = sym_new_predicate(ctx, left, right, JIT_PRED_NE);
}
else {
res = sym_new_type(ctx, &PyBool_Type);
}
res = sym_new_type(ctx, &PyBool_Type);
l = left;
r = right;
if (
@ -2694,14 +2689,10 @@
}
case _FOR_ITER_GEN_FRAME: {
JitOptRef iter;
JitOptRef gen_frame;
assert((this_instr + 1)->opcode == _PUSH_FRAME);
PyCodeObject *co = get_code_with_logging((this_instr + 1));
if (co == NULL) {
ctx->done = true;
break;
}
_Py_UOpsAbstractFrame *new_frame = frame_new(ctx, co, 1, NULL, 0);
iter = stack_pointer[-2];
_Py_UOpsAbstractFrame *new_frame = frame_new_from_symbol(ctx, iter, 1, NULL, 0);
if (new_frame == NULL) {
ctx->done = true;
break;
@ -2884,14 +2875,10 @@
/* _MONITOR_CALL is not a viable micro-op for tier 2 */
case _PY_FRAME_GENERAL: {
JitOptRef callable;
JitOptRef new_frame;
assert((this_instr + 2)->opcode == _PUSH_FRAME);
PyCodeObject *co = get_code_with_logging((this_instr + 2));
if (co == NULL) {
ctx->done = true;
break;
}
new_frame = PyJitRef_WrapInvalid(frame_new(ctx, co, 0, NULL, 0));
callable = stack_pointer[-2 - oparg];
new_frame = PyJitRef_WrapInvalid(frame_new_from_symbol(ctx, callable, 0, NULL, 0));
CHECK_STACK_BOUNDS(-1 - oparg);
stack_pointer[-2 - oparg] = new_frame;
stack_pointer += -1 - oparg;
@ -2996,8 +2983,9 @@
}
case _CHECK_STACK_SPACE: {
assert((this_instr + 4)->opcode == _PUSH_FRAME);
PyCodeObject *co = get_code_with_logging((this_instr + 4));
JitOptRef callable;
callable = stack_pointer[-2 - oparg];
PyCodeObject *co = sym_get_probable_func_code(callable);
if (co == NULL) {
ctx->done = true;
break;
@ -3013,16 +3001,12 @@
case _INIT_CALL_PY_EXACT_ARGS: {
JitOptRef *args;
JitOptRef self_or_null;
JitOptRef callable;
JitOptRef new_frame;
args = &stack_pointer[-oparg];
self_or_null = stack_pointer[-1 - oparg];
callable = stack_pointer[-2 - oparg];
int argcount = oparg;
assert((this_instr + 2)->opcode == _PUSH_FRAME);
PyCodeObject *co = get_code_with_logging((this_instr + 2));
if (co == NULL) {
ctx->done = true;
break;
}
assert(!PyJitRef_IsNull(self_or_null));
assert(args != NULL);
if (sym_is_not_null(self_or_null)) {
@ -3030,9 +3014,9 @@
argcount++;
}
if (sym_is_null(self_or_null) || sym_is_not_null(self_or_null)) {
new_frame = PyJitRef_WrapInvalid(frame_new(ctx, co, 0, args, argcount));
new_frame = PyJitRef_WrapInvalid(frame_new_from_symbol(ctx, callable, 0, args, argcount));
} else {
new_frame = PyJitRef_WrapInvalid(frame_new(ctx, co, 0, NULL, 0));
new_frame = PyJitRef_WrapInvalid(frame_new_from_symbol(ctx, callable, 0, NULL, 0));
}
CHECK_STACK_BOUNDS(-1 - oparg);
stack_pointer[-2 - oparg] = new_frame;
@ -3053,19 +3037,11 @@
ctx->frame = (_Py_UOpsAbstractFrame *)PyJitRef_Unwrap(new_frame);
ctx->curr_frame_depth++;
stack_pointer = ctx->frame->stack_pointer;
uint64_t operand = this_instr->operand0;
if (operand == 0) {
ctx->done = true;
break;
}
if (!(operand & 1)) {
PyFunctionObject *func = (PyFunctionObject *)operand;
ctx->frame->func = func;
}
if ((this_instr-1)->opcode == _CREATE_INIT_FRAME) {
assert((this_instr+1)->opcode == _GUARD_IP__PUSH_FRAME);
REPLACE_OP(this_instr+1, _NOP, 0, 0);
}
assert(ctx->frame->locals != NULL);
break;
}
@ -3212,9 +3188,11 @@
case _CREATE_INIT_FRAME: {
JitOptRef *args;
JitOptRef self;
JitOptRef init;
JitOptRef init_frame;
args = &stack_pointer[-oparg];
self = stack_pointer[-1 - oparg];
init = stack_pointer[-2 - oparg];
ctx->frame->stack_pointer = stack_pointer - oparg - 2;
_Py_UOpsAbstractFrame *shim = frame_new(ctx, (PyCodeObject *)&_Py_InitCleanup, 0, NULL, 0);
if (shim == NULL) {
@ -3226,8 +3204,7 @@
ctx->frame = shim;
ctx->curr_frame_depth++;
assert((this_instr + 1)->opcode == _PUSH_FRAME);
PyCodeObject *co = get_code_with_logging((this_instr + 1));
init_frame = PyJitRef_WrapInvalid(frame_new(ctx, co, 0, args-1, oparg+1));
init_frame = PyJitRef_WrapInvalid(frame_new_from_symbol(ctx, init, 0, args-1, oparg+1));
CHECK_STACK_BOUNDS(-1 - oparg);
stack_pointer[-2 - oparg] = init_frame;
stack_pointer += -1 - oparg;
@ -3501,14 +3478,10 @@
/* _DO_CALL_KW is not a viable micro-op for tier 2 */
case _PY_FRAME_KW: {
JitOptRef callable;
JitOptRef new_frame;
assert((this_instr + 2)->opcode == _PUSH_FRAME);
PyCodeObject *co = get_code_with_logging((this_instr + 2));
if (co == NULL) {
ctx->done = true;
break;
}
new_frame = PyJitRef_WrapInvalid(frame_new(ctx, co, 0, NULL, 0));
callable = stack_pointer[-3 - oparg];
new_frame = PyJitRef_WrapInvalid(frame_new_from_symbol(ctx, callable, 0, NULL, 0));
CHECK_STACK_BOUNDS(-2 - oparg);
stack_pointer[-3 - oparg] = new_frame;
stack_pointer += -2 - oparg;
@ -3553,14 +3526,10 @@
}
case _PY_FRAME_EX: {
JitOptRef func_st;
JitOptRef ex_frame;
assert((this_instr + 2)->opcode == _PUSH_FRAME);
PyCodeObject *co = get_code_with_logging((this_instr + 2));
if (co == NULL) {
ctx->done = true;
break;
}
ex_frame = PyJitRef_WrapInvalid(frame_new(ctx, co, 0, NULL, 0));
func_st = stack_pointer[-4];
ex_frame = PyJitRef_WrapInvalid(frame_new_from_symbol(ctx, func_st, 0, NULL, 0));
CHECK_STACK_BOUNDS(-3);
stack_pointer[-4] = ex_frame;
stack_pointer += -3;
@ -3602,7 +3571,9 @@
case _RETURN_GENERATOR: {
JitOptRef res;
ctx->frame->stack_pointer = stack_pointer;
PyCodeObject *returning_code = get_code_with_logging(this_instr);
assert(this_instr[1].opcode == _RECORD_CODE);
PyCodeObject *returning_code = (PyCodeObject *)this_instr[1].operand0;
assert(PyCode_Check(returning_code));
if (returning_code == NULL) {
ctx->done = true;
break;
@ -4167,6 +4138,10 @@
break;
}
case _GUARD_CODE: {
break;
}
case _GUARD_IP__PUSH_FRAME: {
break;
}
@ -4184,22 +4159,47 @@
}
case _RECORD_TOS: {
JitOptRef tos;
tos = stack_pointer[-1];
sym_set_recorded_value(tos, (PyObject *)this_instr->operand0);
break;
}
case _RECORD_TOS_TYPE: {
JitOptRef tos;
tos = stack_pointer[-1];
PyTypeObject *tp = (PyTypeObject *)this_instr->operand0;
sym_set_recorded_type(tos, tp);
break;
}
case _RECORD_NOS: {
JitOptRef nos;
nos = stack_pointer[-2];
sym_set_recorded_value(nos, (PyObject *)this_instr->operand0);
break;
}
case _RECORD_NOS_GEN_FUNC: {
JitOptRef nos;
nos = stack_pointer[-2];
PyFunctionObject *func = (PyFunctionObject *)this_instr->operand0;
assert(func == NULL || PyFunction_Check(func));
sym_set_recorded_gen_func(nos, func);
break;
}
case _RECORD_4OS: {
JitOptRef value;
value = stack_pointer[-4];
sym_set_recorded_value(value, (PyObject *)this_instr->operand0);
break;
}
case _RECORD_CALLABLE: {
JitOptRef func;
func = stack_pointer[-2 - oparg];
sym_set_recorded_value(func, (PyObject *)this_instr->operand0);
break;
}
@ -4207,7 +4207,7 @@
break;
}
case _RECORD_CALLER_CODE: {
case _RECORD_CODE: {
break;
}