Skip to content

Commit 7509764

Browse files
committed
TOS caching for the interpreter. Work in progress.
1 parent ff2cb21 commit 7509764

19 files changed

+6493
-886
lines changed

Include/internal/pycore_interpframe.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ static inline PyFunctionObject *_PyFrame_GetFunction(_PyInterpreterFrame *f) {
4343
}
4444

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

4949
static inline _PyStackRef _PyFrame_StackPeek(_PyInterpreterFrame *f) {
@@ -134,7 +134,7 @@ _PyFrame_Initialize(
134134
frame->f_builtins = func_obj->func_builtins;
135135
frame->f_globals = func_obj->func_globals;
136136
frame->f_locals = locals;
137-
frame->stackpointer = frame->localsplus + code->co_nlocalsplus;
137+
frame->stackpointer = frame->localsplus + code->co_nlocalsplus + 1;
138138
frame->frame_obj = NULL;
139139
#ifdef Py_GIL_DISABLED
140140
_PyFrame_InitializeTLBC(tstate, frame, code);
@@ -146,6 +146,7 @@ _PyFrame_Initialize(
146146
frame->owner = FRAME_OWNED_BY_THREAD;
147147
frame->visited = 0;
148148
#ifdef Py_DEBUG
149+
frame->localsplus[code->co_nlocalsplus] = PyStackRef_NULL;
149150
frame->lltrace = 0;
150151
#endif
151152

@@ -317,7 +318,7 @@ _PyFrame_PushTrampolineUnchecked(PyThreadState *tstate, PyCodeObject *code, int
317318
#endif
318319
frame->f_locals = NULL;
319320
assert(stackdepth <= code->co_stacksize);
320-
frame->stackpointer = frame->localsplus + code->co_nlocalsplus + stackdepth;
321+
frame->stackpointer = frame->localsplus + code->co_nlocalsplus + stackdepth + 1;
321322
frame->frame_obj = NULL;
322323
#ifdef Py_GIL_DISABLED
323324
_PyFrame_InitializeTLBC(tstate, frame, code);

Include/internal/pycore_opcode_metadata.h

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_stackref.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,6 @@
44
extern "C" {
55
#endif
66

7-
// Define this to get precise tracking of closed stackrefs.
8-
// This will use unbounded memory, as it can only grow.
9-
// Use this to track double closes in short-lived programs
10-
// #define Py_STACKREF_CLOSE_DEBUG 1
11-
127
#ifndef Py_BUILD_CORE
138
# error "this header requires Py_BUILD_CORE define"
149
#endif

Include/internal/pycore_structs.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@ typedef struct {
5757
// Define this to get precise tracking of stackrefs.
5858
// #define Py_STACKREF_DEBUG 1
5959

60+
// Define this to get precise tracking of closed stackrefs.
61+
// This will use unbounded memory, as it can only grow.
62+
// Use this to track double closes in short-lived programs
63+
// #define Py_STACKREF_CLOSE_DEBUG 1
64+
65+
6066
typedef union _PyStackRef {
6167
#if !defined(Py_GIL_DISABLED) && defined(Py_STACKREF_DEBUG)
6268
uint64_t index;

Lib/test/test_sys.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1661,9 +1661,9 @@ def func():
16611661
return sys._getframe()
16621662
x = func()
16631663
if support.Py_GIL_DISABLED:
1664-
INTERPRETER_FRAME = '9PihcP'
1664+
INTERPRETER_FRAME = '9PihcPP'
16651665
else:
1666-
INTERPRETER_FRAME = '9PhcP'
1666+
INTERPRETER_FRAME = '9PhcPP'
16671667
check(x, size('3PiccPPP' + INTERPRETER_FRAME + 'P'))
16681668
# function
16691669
def func(): pass

Objects/codeobject.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -546,7 +546,7 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con)
546546
/* derived values */
547547
co->co_nlocalsplus = nlocalsplus;
548548
co->co_nlocals = nlocals;
549-
co->co_framesize = nlocalsplus + con->stacksize + FRAME_SPECIALS_SIZE;
549+
co->co_framesize = nlocalsplus + con->stacksize + FRAME_SPECIALS_SIZE + 1;
550550
co->co_ncellvars = ncellvars;
551551
co->co_nfreevars = nfreevars;
552552
#ifdef Py_GIL_DISABLED

Python/bytecodes.c

Lines changed: 32 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ dummy_func(
204204
ptrdiff_t off = this_instr - _PyFrame_GetBytecode(frame);
205205
frame->tlbc_index = ((_PyThreadStateImpl *)tstate)->tlbc_index;
206206
frame->instr_ptr = bytecode + off;
207-
// Make sure this_instr gets reset correctley for any uops that
207+
// Make sure this_instr gets reset correctly for any uops that
208208
// follow
209209
next_instr = frame->instr_ptr;
210210
DISPATCH();
@@ -1111,7 +1111,7 @@ dummy_func(
11111111
tstate->current_frame = frame->previous;
11121112
assert(!_PyErr_Occurred(tstate));
11131113
PyObject *result = PyStackRef_AsPyObjectSteal(retval);
1114-
SYNC_SP(); /* Not strictly necessary, but prevents warnings */
1114+
LLTRACE_RESUME_FRAME();
11151115
return result;
11161116
}
11171117

@@ -1223,8 +1223,9 @@ dummy_func(
12231223
{
12241224
PyGenObject *gen = (PyGenObject *)receiver_o;
12251225
_PyInterpreterFrame *gen_frame = &gen->gi_iframe;
1226-
STACK_SHRINK(1);
12271226
_PyFrame_StackPush(gen_frame, PyStackRef_MakeHeapSafe(v));
1227+
DEAD(v);
1228+
SYNC_SP();
12281229
gen->gi_frame_state = FRAME_EXECUTING;
12291230
gen->gi_exc_state.previous_item = tstate->exc_info;
12301231
tstate->exc_info = &gen->gi_exc_state;
@@ -2436,10 +2437,10 @@ dummy_func(
24362437
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 1);
24372438
_PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(
24382439
tstate, PyStackRef_FromPyObjectNew(f), 2, frame);
2439-
// Manipulate stack directly because we exit with DISPATCH_INLINED().
2440-
STACK_SHRINK(1);
24412440
new_frame->localsplus[0] = owner;
24422441
DEAD(owner);
2442+
// Manipulate stack directly because we exit with DISPATCH_INLINED().
2443+
SYNC_SP();
24432444
new_frame->localsplus[1] = PyStackRef_FromPyObjectNew(name);
24442445
frame->return_offset = INSTRUCTION_SIZE;
24452446
DISPATCH_INLINED(new_frame);
@@ -3083,12 +3084,11 @@ dummy_func(
30833084
macro(FOR_ITER) = _SPECIALIZE_FOR_ITER + _FOR_ITER;
30843085

30853086

3086-
inst(INSTRUMENTED_FOR_ITER, (unused/1 -- )) {
3087-
_PyStackRef iter_stackref = TOP();
3088-
PyObject *iter = PyStackRef_AsPyObjectBorrow(iter_stackref);
3089-
PyObject *next = (*Py_TYPE(iter)->tp_iternext)(iter);
3090-
if (next != NULL) {
3091-
PUSH(PyStackRef_FromPyObjectSteal(next));
3087+
inst(INSTRUMENTED_FOR_ITER, (unused/1, iter -- iter, next)) {
3088+
PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter);
3089+
PyObject *next_o = (*Py_TYPE(iter_o)->tp_iternext)(iter_o);
3090+
if (next_o != NULL) {
3091+
next = PyStackRef_FromPyObjectSteal(next_o);
30923092
INSTRUMENTED_JUMP(this_instr, next_instr, PY_MONITORING_EVENT_BRANCH_LEFT);
30933093
}
30943094
else {
@@ -3105,6 +3105,7 @@ dummy_func(
31053105
next_instr[oparg].op.code == INSTRUMENTED_END_FOR);
31063106
/* Skip END_FOR */
31073107
JUMPBY(oparg + 1);
3108+
DISPATCH();
31083109
}
31093110
}
31103111

@@ -3168,10 +3169,12 @@ dummy_func(
31683169
assert(Py_TYPE(iter_o) == &PyListIter_Type);
31693170
PyListObject *seq = it->it_seq;
31703171
assert(seq);
3172+
// The code generator doesn't understand #ifdef Py_GIL_DISABLED
3173+
// so put in some control flow
31713174
#ifdef Py_GIL_DISABLED
31723175
assert(_PyObject_IsUniquelyReferenced(iter_o));
31733176
assert(_Py_IsOwnedByCurrentThread((PyObject *)seq) ||
3174-
_PyObject_GC_IS_SHARED(seq));
3177+
_PyObject_GC_IS_SHARED(seq));
31753178
STAT_INC(FOR_ITER, hit);
31763179
int result = _PyList_GetItemRefNoLock(seq, it->it_index, &next);
31773180
// A negative result means we lost a race with another thread
@@ -3994,8 +3997,9 @@ dummy_func(
39943997
tstate, (PyCodeObject *)&_Py_InitCleanup, 1, frame);
39953998
assert(_PyFrame_GetBytecode(shim)[0].op.code == EXIT_INIT_CHECK);
39963999
assert(_PyFrame_GetBytecode(shim)[1].op.code == RETURN_VALUE);
4000+
shim->localsplus[0] = PyStackRef_NULL;
39974001
/* Push self onto stack of shim */
3998-
shim->localsplus[0] = PyStackRef_DUP(self[0]);
4002+
shim->localsplus[1] = PyStackRef_DUP(self[0]);
39994003
_PyInterpreterFrame *temp = _PyEvalFramePushAndInit(
40004004
tstate, init[0], NULL, args-1, oparg+1, NULL, shim);
40014005
DEAD(init);
@@ -4022,7 +4026,6 @@ dummy_func(
40224026
_PUSH_FRAME;
40234027

40244028
inst(EXIT_INIT_CHECK, (should_be_none -- )) {
4025-
assert(STACK_LEVEL() == 2);
40264029
if (!PyStackRef_IsNone(should_be_none)) {
40274030
PyErr_Format(PyExc_TypeError,
40284031
"__init__() should return None, not '%.200s'",
@@ -4932,6 +4935,7 @@ dummy_func(
49324935
}
49334936
next_instr = frame->instr_ptr;
49344937
if (next_instr != this_instr) {
4938+
SYNC_SP();
49354939
DISPATCH();
49364940
}
49374941
}
@@ -4976,46 +4980,48 @@ dummy_func(
49764980
_CHECK_PERIODIC +
49774981
_MONITOR_JUMP_BACKWARD;
49784982

4979-
inst(INSTRUMENTED_POP_JUMP_IF_TRUE, (unused/1 -- )) {
4980-
_PyStackRef cond = POP();
4983+
inst(INSTRUMENTED_POP_JUMP_IF_TRUE, (unused/1, cond -- )) {
49814984
assert(PyStackRef_BoolCheck(cond));
49824985
int jump = PyStackRef_IsTrue(cond);
4986+
DEAD(cond);
49834987
RECORD_BRANCH_TAKEN(this_instr[1].cache, jump);
49844988
if (jump) {
49854989
INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT);
49864990
}
49874991
}
49884992

4989-
inst(INSTRUMENTED_POP_JUMP_IF_FALSE, (unused/1 -- )) {
4990-
_PyStackRef cond = POP();
4993+
inst(INSTRUMENTED_POP_JUMP_IF_FALSE, (unused/1, cond -- )) {
49914994
assert(PyStackRef_BoolCheck(cond));
49924995
int jump = PyStackRef_IsFalse(cond);
4996+
DEAD(cond);
49934997
RECORD_BRANCH_TAKEN(this_instr[1].cache, jump);
49944998
if (jump) {
49954999
INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT);
49965000
}
49975001
}
49985002

4999-
inst(INSTRUMENTED_POP_JUMP_IF_NONE, (unused/1 -- )) {
5000-
_PyStackRef value_stackref = POP();
5001-
int jump = PyStackRef_IsNone(value_stackref);
5003+
inst(INSTRUMENTED_POP_JUMP_IF_NONE, (unused/1, value -- )) {
5004+
int jump = PyStackRef_IsNone(value);
50025005
RECORD_BRANCH_TAKEN(this_instr[1].cache, jump);
50035006
if (jump) {
5007+
DEAD(value);
50045008
INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT);
50055009
}
50065010
else {
5007-
PyStackRef_CLOSE(value_stackref);
5011+
PyStackRef_CLOSE(value);
50085012
}
50095013
}
50105014

5011-
inst(INSTRUMENTED_POP_JUMP_IF_NOT_NONE, (unused/1 -- )) {
5012-
_PyStackRef value_stackref = POP();
5013-
int jump = !PyStackRef_IsNone(value_stackref);
5015+
inst(INSTRUMENTED_POP_JUMP_IF_NOT_NONE, (unused/1, value -- )) {
5016+
int jump = !PyStackRef_IsNone(value);
50145017
RECORD_BRANCH_TAKEN(this_instr[1].cache, jump);
50155018
if (jump) {
5016-
PyStackRef_CLOSE(value_stackref);
5019+
PyStackRef_CLOSE(value);
50175020
INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT);
50185021
}
5022+
else {
5023+
DEAD(value);
5024+
}
50195025
}
50205026

50215027
tier1 inst(EXTENDED_ARG, ( -- )) {

0 commit comments

Comments
 (0)