GH-119866: Spill the stack around escaping calls. (GH-124392)

* Spill the evaluation around escaping calls in the generated interpreter and JIT. 

* The code generator tracks live, cached values so they can be saved to memory when needed.

* Spills the stack pointer around escaping calls, so that the exact stack is visible to the cycle GC.
This commit is contained in:
Mark Shannon 2024-10-07 14:56:39 +01:00 committed by GitHub
parent cda3b5a576
commit da071fa3e8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 3841 additions and 2034 deletions

View file

@ -93,9 +93,9 @@
}
case _END_SEND: {
_Py_UopsSymbol *value;
value = sym_new_not_null(ctx);
stack_pointer[-2] = value;
_Py_UopsSymbol *val;
val = sym_new_not_null(ctx);
stack_pointer[-2] = val;
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
break;
@ -630,7 +630,6 @@
ctx->frame->stack_pointer = stack_pointer;
frame_pop(ctx);
stack_pointer = ctx->frame->stack_pointer;
res = retval;
/* Stack space handling */
assert(corresponding_check_stack == NULL);
assert(co != NULL);
@ -643,6 +642,7 @@
// might be impossible, but bailing is still safe
ctx->done = true;
}
res = retval;
stack_pointer[0] = res;
stack_pointer += 1;
assert(WITHIN_STACK_BOUNDS());
@ -832,9 +832,7 @@
_Py_UopsSymbol **res;
_Py_UopsSymbol *null = NULL;
res = &stack_pointer[0];
for (int _i = 1; --_i >= 0;) {
res[_i] = sym_new_not_null(ctx);
}
res[0] = sym_new_not_null(ctx);
null = sym_new_null(ctx);
if (oparg & 1) stack_pointer[1] = null;
stack_pointer += 1 + (oparg & 1);
@ -1021,9 +1019,7 @@
owner = stack_pointer[-1];
(void)owner;
attr = sym_new_not_null(ctx);
if (oparg & 1) {
self_or_null = sym_new_unknown(ctx);
}
self_or_null = sym_new_unknown(ctx);
stack_pointer[-1] = attr;
if (oparg & 1) stack_pointer[0] = self_or_null;
stack_pointer += (oparg & 1);
@ -1114,11 +1110,17 @@
PyModuleObject *mod = (PyModuleObject *)sym_get_const(owner);
assert(PyModule_CheckExact(mod));
PyObject *dict = mod->md_dict;
stack_pointer[-1] = attr;
if (oparg & 1) stack_pointer[0] = null;
stack_pointer += (oparg & 1);
assert(WITHIN_STACK_BOUNDS());
PyObject *res = convert_global_to_const(this_instr, dict);
if (res != NULL) {
this_instr[-1].opcode = _POP_TOP;
attr = sym_new_const(ctx, res);
}
stack_pointer += -(oparg & 1);
assert(WITHIN_STACK_BOUNDS());
}
if (attr == NULL) {
/* No conversion made. We don't know what `attr` is. */
@ -1239,7 +1241,11 @@
res = sym_new_type(ctx, &PyBool_Type);
}
else {
stack_pointer += -2;
assert(WITHIN_STACK_BOUNDS());
res = _Py_uop_sym_new_not_null(ctx);
stack_pointer += 2;
assert(WITHIN_STACK_BOUNDS());
}
stack_pointer[-2] = res;
stack_pointer += -1;
@ -1659,12 +1665,13 @@
/* _MONITOR_CALL is not a viable micro-op for tier 2 */
case _PY_FRAME_GENERAL: {
_Py_UopsSymbol **args;
_Py_UopsSymbol *self_or_null;
_Py_UopsSymbol *callable;
_Py_UOpsAbstractFrame *new_frame;
self_or_null = stack_pointer[-1 - oparg];
callable = stack_pointer[-2 - oparg];
stack_pointer += -2 - oparg;
assert(WITHIN_STACK_BOUNDS());
(void)(self_or_null);
(void)(callable);
PyCodeObject *co = NULL;
@ -1675,8 +1682,8 @@
break;
}
new_frame = frame_new(ctx, co, 0, NULL, 0);
stack_pointer[-2 - oparg] = (_Py_UopsSymbol *)new_frame;
stack_pointer += -1 - oparg;
stack_pointer[0] = (_Py_UopsSymbol *)new_frame;
stack_pointer += 1;
assert(WITHIN_STACK_BOUNDS());
break;
}
@ -1690,14 +1697,12 @@
}
case _EXPAND_METHOD: {
_Py_UopsSymbol *method;
_Py_UopsSymbol **method;
_Py_UopsSymbol **self;
method = &stack_pointer[-2 - oparg];
self = &stack_pointer[-1 - oparg];
method = sym_new_not_null(ctx);
for (int _i = 1; --_i >= 0;) {
self[_i] = sym_new_not_null(ctx);
}
stack_pointer[-2 - oparg] = method;
method[0] = sym_new_not_null(ctx);
self[0] = sym_new_not_null(ctx);
break;
}
@ -1774,6 +1779,8 @@
(void)callable;
PyCodeObject *co = NULL;
assert((this_instr + 2)->opcode == _PUSH_FRAME);
stack_pointer += -2 - oparg;
assert(WITHIN_STACK_BOUNDS());
co = get_code_with_logging((this_instr + 2));
if (co == NULL) {
ctx->done = true;
@ -1791,8 +1798,8 @@
} else {
new_frame = frame_new(ctx, co, 0, NULL, 0);
}
stack_pointer[-2 - oparg] = (_Py_UopsSymbol *)new_frame;
stack_pointer += -1 - oparg;
stack_pointer[0] = (_Py_UopsSymbol *)new_frame;
stack_pointer += 1;
assert(WITHIN_STACK_BOUNDS());
break;
}
@ -1825,9 +1832,11 @@
if (first_valid_check_stack == NULL) {
first_valid_check_stack = corresponding_check_stack;
}
else if (corresponding_check_stack) {
// delete all but the first valid _CHECK_STACK_SPACE
corresponding_check_stack->opcode = _NOP;
else {
if (corresponding_check_stack) {
// delete all but the first valid _CHECK_STACK_SPACE
corresponding_check_stack->opcode = _NOP;
}
}
corresponding_check_stack = NULL;
break;
@ -2005,6 +2014,24 @@
/* _INSTRUMENTED_CALL_KW is not a viable micro-op for tier 2 */
case _MAYBE_EXPAND_METHOD_KW: {
_Py_UopsSymbol **func;
_Py_UopsSymbol **maybe_self;
_Py_UopsSymbol **args;
_Py_UopsSymbol *kwnames_out;
func = &stack_pointer[-3 - oparg];
maybe_self = &stack_pointer[-2 - oparg];
args = &stack_pointer[-1 - oparg];
func[0] = sym_new_not_null(ctx);
maybe_self[0] = sym_new_not_null(ctx);
for (int _i = oparg; --_i >= 0;) {
args[_i] = sym_new_not_null(ctx);
}
kwnames_out = sym_new_not_null(ctx);
stack_pointer[-1] = kwnames_out;
break;
}
/* _DO_CALL_KW is not a viable micro-op for tier 2 */
case _PY_FRAME_KW: {
@ -2038,17 +2065,12 @@
}
case _EXPAND_METHOD_KW: {
_Py_UopsSymbol *method;
_Py_UopsSymbol **method;
_Py_UopsSymbol **self;
_Py_UopsSymbol *kwnames;
method = &stack_pointer[-3 - oparg];
self = &stack_pointer[-2 - oparg];
method = sym_new_not_null(ctx);
for (int _i = 1; --_i >= 0;) {
self[_i] = sym_new_not_null(ctx);
}
kwnames = sym_new_not_null(ctx);
stack_pointer[-3 - oparg] = method;
stack_pointer[-1] = kwnames;
method[0] = sym_new_not_null(ctx);
self[0] = sym_new_not_null(ctx);
break;
}
@ -2067,7 +2089,17 @@
/* _INSTRUMENTED_CALL_FUNCTION_EX is not a viable micro-op for tier 2 */
/* __DO_CALL_FUNCTION_EX is not a viable micro-op for tier 2 */
case _MAKE_CALLARGS_A_TUPLE: {
_Py_UopsSymbol *tuple;
_Py_UopsSymbol *kwargs_out = NULL;
tuple = sym_new_not_null(ctx);
kwargs_out = sym_new_not_null(ctx);
stack_pointer[-1 - (oparg & 1)] = tuple;
if (oparg & 1) stack_pointer[-(oparg & 1)] = kwargs_out;
break;
}
/* _DO_CALL_FUNCTION_EX is not a viable micro-op for tier 2 */
case _MAKE_FUNCTION: {
_Py_UopsSymbol *func;
@ -2077,9 +2109,9 @@
}
case _SET_FUNCTION_ATTRIBUTE: {
_Py_UopsSymbol *func_st;
func_st = sym_new_not_null(ctx);
stack_pointer[-2] = func_st;
_Py_UopsSymbol *func_out;
func_out = sym_new_not_null(ctx);
stack_pointer[-2] = func_out;
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
break;
@ -2098,14 +2130,14 @@
assert(framesize > 0);
assert(framesize <= curr_space);
curr_space -= framesize;
stack_pointer[0] = res;
stack_pointer += 1;
assert(WITHIN_STACK_BOUNDS());
co = get_code(this_instr);
if (co == NULL) {
// might be impossible, but bailing is still safe
ctx->done = true;
}
stack_pointer[0] = res;
stack_pointer += 1;
assert(WITHIN_STACK_BOUNDS());
break;
}
@ -2174,7 +2206,9 @@
res = sym_new_type(ctx, &PyFloat_Type);
}
}
res = sym_new_unknown(ctx);
else {
res = sym_new_unknown(ctx);
}
stack_pointer[-2] = res;
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
@ -2182,12 +2216,16 @@
}
case _SWAP: {
_Py_UopsSymbol *top;
_Py_UopsSymbol *bottom;
top = stack_pointer[-1];
bottom = stack_pointer[-2 - (oparg-2)];
stack_pointer[-2 - (oparg-2)] = top;
stack_pointer[-1] = bottom;
_Py_UopsSymbol *top_in;
_Py_UopsSymbol *bottom_in;
_Py_UopsSymbol *top_out;
_Py_UopsSymbol *bottom_out;
top_in = stack_pointer[-1];
bottom_in = stack_pointer[-2 - (oparg-2)];
bottom_out = bottom_in;
top_out = top_in;
stack_pointer[-2 - (oparg-2)] = top_out;
stack_pointer[-1] = bottom_out;
break;
}
@ -2213,7 +2251,11 @@
if (sym_is_const(flag)) {
PyObject *value = sym_get_const(flag);
assert(value != NULL);
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
eliminate_pop_guard(this_instr, value != Py_True);
stack_pointer += 1;
assert(WITHIN_STACK_BOUNDS());
}
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
@ -2226,7 +2268,11 @@
if (sym_is_const(flag)) {
PyObject *value = sym_get_const(flag);
assert(value != NULL);
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
eliminate_pop_guard(this_instr, value != Py_False);
stack_pointer += 1;
assert(WITHIN_STACK_BOUNDS());
}
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
@ -2239,14 +2285,22 @@
if (sym_is_const(flag)) {
PyObject *value = sym_get_const(flag);
assert(value != NULL);
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
eliminate_pop_guard(this_instr, !Py_IsNone(value));
}
else if (sym_has_type(flag)) {
assert(!sym_matches_type(flag, &_PyNone_Type));
eliminate_pop_guard(this_instr, true);
else {
if (sym_has_type(flag)) {
assert(!sym_matches_type(flag, &_PyNone_Type));
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
eliminate_pop_guard(this_instr, true);
stack_pointer += 1;
assert(WITHIN_STACK_BOUNDS());
}
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
}
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
break;
}
@ -2256,14 +2310,22 @@
if (sym_is_const(flag)) {
PyObject *value = sym_get_const(flag);
assert(value != NULL);
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
eliminate_pop_guard(this_instr, Py_IsNone(value));
}
else if (sym_has_type(flag)) {
assert(!sym_matches_type(flag, &_PyNone_Type));
eliminate_pop_guard(this_instr, false);
else {
if (sym_has_type(flag)) {
assert(!sym_matches_type(flag, &_PyNone_Type));
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
eliminate_pop_guard(this_instr, false);
stack_pointer += 1;
assert(WITHIN_STACK_BOUNDS());
}
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
}
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
break;
}