10000 GH-131498: manage stacks automatically by markshannon · Pull Request #132074 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

GH-131498: manage stacks automatically #132074

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Apr 4, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
TOS caching for the interpreter. Work in progress.
  • Loading branch information
markshannon committed Apr 4, 2025
commit 7509764d7da1e0a9fbdde5672c9476c3668180f7
7 changes: 4 additions & 3 deletions Include/internal/pycore_interpframe.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ static inline PyFunctionObject *_PyFrame_GetFunction(_PyInterpreterFrame *f) {
}

static inline _PyStackRef *_PyFrame_Stackbase(_PyInterpreterFrame *f) {
return (f->localsplus + _PyFrame_GetCode(f)->co_nlocalsplus);
return (f->localsplus + _PyFrame_GetCode(f)->co_nlocalsplus + 1);
}

static inline _PyStackRef _PyFrame_StackPeek(_PyInterpreterFrame *f) {
Expand Down Expand Up @@ -134,7 +134,7 @@ _PyFrame_Initialize(
frame->f_builtins = func_obj->func_builtins;
frame->f_globals = func_obj->func_globals;
frame->f_locals = locals;
frame->stackpointer = frame->localsplus + code->co_nlocalsplus;
frame->stackpointer = frame->localsplus + code->co_nlocalsplus + 1;
frame->frame_obj = NULL;
#ifdef Py_GIL_DISABLED
_PyFrame_InitializeTLBC(tstate, frame, code);
Expand All @@ -146,6 +146,7 @@ _PyFrame_Initialize(
frame->owner = FRAME_OWNED_BY_THREAD;
frame->visited = 0;
#ifdef Py_DEBUG
frame->localsplus[code->co_nlocalsplus] = PyStackRef_NULL;
frame->lltrace = 0;
#endif

Expand Down Expand Up @@ -317,7 +318,7 @@ _PyFrame_PushTrampolineUnchecked(PyThreadState *tstate, PyCodeObject *code, int
#endif
frame->f_locals = NULL;
assert(stackdepth <= code->co_stacksize);
frame->stackpointer = frame->localsplus + code->co_nlocalsplus + stackdepth;
frame->stackpointer = frame->localsplus + code->co_nlocalsplus + stackdepth + 1;
frame->frame_obj = NULL;
#ifdef Py_GIL_DISABLED
_PyFrame_InitializeTLBC(tstate, frame, code);
Expand Down
12 changes: 6 additions & 6 deletions Include/internal/pycore_opcode_metadata.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 0 additions & 5 deletions Include/internal/pycore_stackref.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,6 @@
extern "C" {
#endif

// Define this to get precise tracking of closed stackrefs.
// This will use unbounded memory, as it can only grow.
// Use this to track double closes in short-lived programs
// #define Py_STACKREF_CLOSE_DEBUG 1

#ifndef Py_BUILD_CORE
# error "this header requires Py_BUILD_CORE define"
#endif
Expand Down
6 changes: 6 additions & 0 deletions Include/internal/pycore_structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ typedef struct {
// Define this to get precise tracking of stackrefs.
// #define Py_STACKREF_DEBUG 1

// Define this to get precise tracking of closed stackrefs.
// This will use unbounded memory, as it can only grow.
// Use this to track double closes in short-lived programs
// #define Py_STACKREF_CLOSE_DEBUG 1


typedef union _PyStackRef {
#if !defined(Py_GIL_DISABLED) && defined(Py_STACKREF_DEBUG)
uint64_t index;
Expand Down
4 changes: 2 additions & 2 deletions Lib/test/test_sys.py
Original file line number Diff line number Diff line change
Expand Up @@ -1661,9 +1661,9 @@ def func():
return sys._getframe()
x = func()
if support.Py_GIL_DISABLED:
INTERPRETER_FRAME = '9PihcP'
INTERPRETER_FRAME = '9PihcPP'
else:
INTERPRETER_FRAME = '9PhcP'
INTERPRETER_FRAME = '9PhcPP'
check(x, size('3PiccPPP' + INTERPRETER_FRAME + 'P'))
# function
def func(): pass
Expand Down
2 changes: 1 addition & 1 deletion Objects/codeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -546,7 +546,7 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con)
/* derived values */
co->co_nlocalsplus = nlocalsplus;
co->co_nlocals = nlocals;
co->co_framesize = nlocalsplus + con->stacksize + FRAME_SPECIALS_SIZE;
co->co_framesize = nlocalsplus + con->stacksize + FRAME_SPECIALS_SIZE + 1;
co->co_ncellvars = ncellvars;
co->co_nfreevars = nfreevars;
#ifdef Py_GIL_DISABLED
Expand Down
58 changes: 32 additions & 26 deletions Python/bytecodes.c
341A
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ dummy_func(
ptrdiff_t off = this_instr - _PyFrame_GetBytecode(frame);
frame->tlbc_index = ((_PyThreadStateImpl *)tstate)->tlbc_index;
frame->instr_ptr = bytecode + off;
// Make sure this_instr gets reset correctley for any uops that
// Make sure this_instr gets reset correctly for any uops that
// follow
next_instr = frame->instr_ptr;
DISPATCH();
Expand Down Expand Up @@ -1111,7 +1111,7 @@ dummy_func(
tstate->current_frame = frame->previous;
assert(!_PyErr_Occurred(tstate));
PyObject *result = PyStackRef_AsPyObjectSteal(retval);
SYNC_SP(); /* Not strictly necessary, but prevents warnings */
LLTRACE_RESUME_FRAME();
return result;
}

Expand Down Expand Up @@ -1223,8 +1223,9 @@ dummy_func(
{
PyGenObject *gen = (PyGenObject *)receiver_o;
_PyInterpreterFrame *gen_frame = &gen->gi_iframe;
STACK_SHRINK(1);
_PyFrame_StackPush(gen_frame, PyStackRef_MakeHeapSafe(v));
DEAD(v);
SYNC_SP();
gen->gi_frame_state = FRAME_EXECUTING;
gen->gi_exc_state.previous_item = tstate->exc_info;
tstate->exc_info = &gen->gi_exc_state;
Expand Down Expand Up @@ -2436,10 +2437,10 @@ dummy_func(
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 1);
_PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(
tstate, PyStackRef_FromPyObjectNew(f), 2, frame);
// Manipulate stack directly because we exit with DISPATCH_INLINED().
STACK_SHRINK(1);
new_frame->localsplus[0] = owner;
DEAD(owner);
// Manipulate stack directly because we exit with DISPATCH_INLINED().
SYNC_SP();
new_frame->localsplus[1] = PyStackRef_FromPyObjectNew(name);
frame->return_offset = INSTRUCTION_SIZE;
DISPATCH_INLINED(new_frame);
Expand Down Expand Up @@ -3083,12 +3084,11 @@ dummy_func(
macro(FOR_ITER) = _SPECIALIZE_FOR_ITER + _FOR_ITER;


inst(INSTRUMENTED_FOR_ITER, (unused/1 -- )) {
_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));
inst(INSTRUMENTED_FOR_ITER, (unused/1, iter -- iter, next)) {
PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter);
PyObject *next_o = (*Py_TYPE(iter_o)->tp_iternext)(iter_o);
if (next_o != NULL) {
next = PyStackRef_FromPyObjectSteal(next_o);
INSTRUMENTED_JUMP(this_instr, next_instr, PY_MONITORING_EVENT_BRANCH_LEFT);
}
else {
Expand All @@ -3105,6 +3105,7 @@ dummy_func(
next_instr[oparg].op.code == INSTRUMENTED_END_FOR);
/* Skip END_FOR */
JUMPBY(oparg + 1);
DISPATCH();
}
}

Expand Down Expand Up @@ -3168,10 +3169,12 @@ dummy_func(
assert(Py_TYPE(iter_o) == &PyListIter_Type);
PyListObject *seq = it->it_seq;
assert(seq);
// The code generator doesn't understand #ifdef Py_GIL_DISABLED
// so put in some control flow
#ifdef Py_GIL_DISABLED
assert(_PyObject_IsUniquelyReferenced(iter_o));
assert(_Py_IsOwnedByCurrentThread((PyObject *)seq) ||
_PyObject_GC_IS_SHARED(seq));
_PyObject_GC_IS_SHARED(seq));
STAT_INC(FOR_ITER, hit);
int result = _PyList_GetItemRefNoLock(seq, it->it_index, &next);
// A negative result means we lost a race with another thread
Expand Down Expand Up @@ -3994,8 +3997,9 @@ dummy_func(
tstate, (PyCodeObject *)&_Py_InitCleanup, 1, frame);
assert(_PyFrame_GetBytecode(shim)[0].op.code == EXIT_INIT_CHECK);
assert(_PyFrame_GetBytecode(shim)[1].op.code == RETURN_VALUE);
shim->localsplus[0] = PyStackRef_NULL;
/* Push self onto stack of shim */
shim->localsplus[0] = PyStackRef_DUP(self[0]);
shim->localsplus[1] = PyStackRef_DUP(self[0]);
_PyInterpreterFrame *temp = _PyEvalFramePushAndInit(
tstate, init[0], NULL, args-1, oparg+1, NULL, shim);
DEAD(init);
Expand All @@ -4022,7 +4026,6 @@ dummy_func(
_PUSH_FRAME;

inst(EXIT_INIT_CHECK, (should_be_none -- )) {
assert(STACK_LEVEL() == 2);
if (!PyStackRef_IsNone(should_be_none)) {
PyErr_Format(PyExc_TypeError,
"__init__() should return None, not '%.200s'",
Expand Down Expand Up @@ -4932,6 +4935,7 @@ dummy_func(
}
next_instr = frame->instr_ptr;
if (next_instr != this_instr) {
SYNC_SP();
DISPATCH();
}
}
Expand Down Expand Up @@ -4976,46 +4980,48 @@ dummy_func(
_CHECK_PERIODIC +
_MONITOR_JUMP_BACKWARD;

inst(INSTRUMENTED_POP_JUMP_IF_TRUE, (unused/1 -- )) {
_PyStackRef cond = POP();
inst(INSTRUMENTED_POP_JUMP_IF_TRUE, (unused/1, cond -- )) {
assert(PyStackRef_BoolCheck(cond));
int jump = PyStackRef_IsTrue(cond);
DEAD(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();
inst(INSTRUMENTED_POP_JUMP_IF_FALSE, (unused/1, cond -- )) {
assert(PyStackRef_BoolCheck(cond));
int jump = PyStackRef_IsFalse(cond);
DEAD(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 jump = PyStackRef_IsNone(value_stackref);
inst(INSTRUMENTED_POP_JUMP_IF_NONE, (unused/1, value -- )) {
int jump = PyStackRef_IsNone(value);
RECORD_BRANCH_TAKEN(this_instr[1].cache, jump);
if (jump) {
DEAD(value);
INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT);
}
else {
PyStackRef_CLOSE(value_stackref);
PyStackRef_CLOSE(value);
}
}

inst(INSTRUMENTED_POP_JUMP_IF_NOT_NONE, (unused/1 -- )) {
_PyStackRef value_stackref = POP();
int jump = !PyStackRef_IsNone(value_stackref);
inst(INSTRUMENTED_POP_JUMP_IF_NOT_NONE, (unused/1, value -- )) {
int jump = !PyStackRef_IsNone(value);
RECORD_BRANCH_TAKEN(this_instr[1].cache, jump);
if (jump) {
PyStackRef_CLOSE(value_stackref);
PyStackRef_CLOSE(value);
INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT);
}
else {
DEAD(value);
}
}

tier1 inst(EXTENDED_ARG, ( -- )) {
Expand Down
Loading
0