Skip to content

Commit 22b0de2

Browse files
gh-117139: Convert the evaluation stack to stack refs (#118450)
This PR sets up tagged pointers for CPython. The general idea is to create a separate struct _PyStackRef for everything on the evaluation stack to store the bits. This forces the C compiler to warn us if we try to cast things or pull things out of the struct directly. Only for free threading: We tag the low bit if something is deferred - that means we skip incref and decref operations on it. This behavior may change in the future if Mark's plans to defer all objects in the interpreter loop pans out. This implies a strict stack reference discipline is required. ALL incref and decref operations on stackrefs must use the stackref variants. It is unsafe to untag something then do normal incref/decref ops on it. The new incref and decref variants are called dup and close. They mimic a "handle" API operating on these stackrefs. Please read Include/internal/pycore_stackref.h for more information! --------- Co-authored-by: Mark Shannon <9448417+markshannon@users.noreply.github.com>
1 parent d611c4c commit 22b0de2

35 files changed

+5215
-3745
lines changed

Include/internal/pycore_ceval.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -261,8 +261,11 @@ PyAPI_FUNC(void) _PyEval_FormatExcUnbound(PyThreadState *tstate, PyCodeObject *c
261261
PyAPI_FUNC(void) _PyEval_FormatKwargsError(PyThreadState *tstate, PyObject *func, PyObject *kwargs);
262262
PyAPI_FUNC(PyObject *)_PyEval_MatchClass(PyThreadState *tstate, PyObject *subject, PyObject *type, Py_ssize_t nargs, PyObject *kwargs);
263263
PyAPI_FUNC(PyObject *)_PyEval_MatchKeys(PyThreadState *tstate, PyObject *map, PyObject *keys);
264-
PyAPI_FUNC(int) _PyEval_UnpackIterable(PyThreadState *tstate, PyObject *v, int argcnt, int argcntafter, PyObject **sp);
264+
PyAPI_FUNC(int) _PyEval_UnpackIterableStackRef(PyThreadState *tstate, _PyStackRef v, int argcnt, int argcntafter, _PyStackRef *sp);
265265
PyAPI_FUNC(void) _PyEval_FrameClearAndPop(PyThreadState *tstate, _PyInterpreterFrame *frame);
266+
PyAPI_FUNC(PyObject **) _PyObjectArray_FromStackRefArray(_PyStackRef *input, Py_ssize_t nargs, PyObject **scratch);
267+
268+
PyAPI_FUNC(void) _PyObjectArray_Free(PyObject **array, PyObject **scratch);
266269

267270

268271
/* Bits that can be set in PyThreadState.eval_breaker */

Include/internal/pycore_code.h

+15-14
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ extern "C" {
88
# error "this header requires Py_BUILD_CORE define"
99
#endif
1010

11+
#include "pycore_stackref.h" // _PyStackRef
1112
#include "pycore_lock.h" // PyMutex
1213
#include "pycore_backoff.h" // _Py_BackoffCounter
1314

@@ -317,30 +318,30 @@ extern void _PyCode_Clear_Executors(PyCodeObject *code);
317318

318319
/* Specialization functions */
319320

320-
extern void _Py_Specialize_LoadSuperAttr(PyObject *global_super, PyObject *cls,
321+
extern void _Py_Specialize_LoadSuperAttr(_PyStackRef global_super, _PyStackRef cls,
321322
_Py_CODEUNIT *instr, int load_method);
322-
extern void _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr,
323+
extern void _Py_Specialize_LoadAttr(_PyStackRef owner, _Py_CODEUNIT *instr,
323324
PyObject *name);
324-
extern void _Py_Specialize_StoreAttr(PyObject *owner, _Py_CODEUNIT *instr,
325+
extern void _Py_Specialize_StoreAttr(_PyStackRef owner, _Py_CODEUNIT *instr,
325326
PyObject *name);
326327
extern void _Py_Specialize_LoadGlobal(PyObject *globals, PyObject *builtins,
327328
_Py_CODEUNIT *instr, PyObject *name);
328-
extern void _Py_Specialize_BinarySubscr(PyObject *sub, PyObject *container,
329+
extern void _Py_Specialize_BinarySubscr(_PyStackRef sub, _PyStackRef container,
329330
_Py_CODEUNIT *instr);
330-
extern void _Py_Specialize_StoreSubscr(PyObject *container, PyObject *sub,
331+
extern void _Py_Specialize_StoreSubscr(_PyStackRef container, _PyStackRef sub,
331332
_Py_CODEUNIT *instr);
332-
extern void _Py_Specialize_Call(PyObject *callable, _Py_CODEUNIT *instr,
333+
extern void _Py_Specialize_Call(_PyStackRef callable, _Py_CODEUNIT *instr,
333334
int nargs);
334-
extern void _Py_Specialize_BinaryOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr,
335-
int oparg, PyObject **locals);
336-
extern void _Py_Specialize_CompareOp(PyObject *lhs, PyObject *rhs,
335+
extern void _Py_Specialize_BinaryOp(_PyStackRef lhs, _PyStackRef rhs, _Py_CODEUNIT *instr,
336+
int oparg, _PyStackRef *locals);
337+
extern void _Py_Specialize_CompareOp(_PyStackRef lhs, _PyStackRef rhs,
337338
_Py_CODEUNIT *instr, int oparg);
338-
extern void _Py_Specialize_UnpackSequence(PyObject *seq, _Py_CODEUNIT *instr,
339+
extern void _Py_Specialize_UnpackSequence(_PyStackRef seq, _Py_CODEUNIT *instr,
339340
int oparg);
340-
extern void _Py_Specialize_ForIter(PyObject *iter, _Py_CODEUNIT *instr, int oparg);
341-
extern void _Py_Specialize_Send(PyObject *receiver, _Py_CODEUNIT *instr);
342-
extern void _Py_Specialize_ToBool(PyObject *value, _Py_CODEUNIT *instr);
343-
extern void _Py_Specialize_ContainsOp(PyObject *value, _Py_CODEUNIT *instr);
341+
extern void _Py_Specialize_ForIter(_PyStackRef iter, _Py_CODEUNIT *instr, int oparg);
342+
extern void _Py_Specialize_Send(_PyStackRef receiver, _Py_CODEUNIT *instr);
343+
extern void _Py_Specialize_ToBool(_PyStackRef value, _Py_CODEUNIT *instr);
344+
extern void _Py_Specialize_ContainsOp(_PyStackRef value, _Py_CODEUNIT *instr);
344345

345346
#ifdef Py_STATS
346347

Include/internal/pycore_frame.h

+14-13
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ extern "C" {
1111
#include <stdbool.h>
1212
#include <stddef.h> // offsetof()
1313
#include "pycore_code.h" // STATS
14+
#include "pycore_stackref.h" // _PyStackRef
1415

1516
/* See Objects/frame_layout.md for an explanation of the frame stack
1617
* including explanation of the PyFrameObject and _PyInterpreterFrame
@@ -67,7 +68,7 @@ typedef struct _PyInterpreterFrame {
6768
uint16_t return_offset; /* Only relevant during a function call */
6869
char owner;
6970
/* Locals and stack */
70-
PyObject *localsplus[1];
71+
_PyStackRef localsplus[1];
7172
} _PyInterpreterFrame;
7273

7374
#define _PyInterpreterFrame_LASTI(IF) \
@@ -78,23 +79,23 @@ static inline PyCodeObject *_PyFrame_GetCode(_PyInterpreterFrame *f) {
7879
return (PyCodeObject *)f->f_executable;
7980
}
8081

81-
static inline PyObject **_PyFrame_Stackbase(_PyInterpreterFrame *f) {
82-
return f->localsplus + _PyFrame_GetCode(f)->co_nlocalsplus;
82+
static inline _PyStackRef *_PyFrame_Stackbase(_PyInterpreterFrame *f) {
83+
return (f->localsplus + _PyFrame_GetCode(f)->co_nlocalsplus);
8384
}
8485

85-
static inline PyObject *_PyFrame_StackPeek(_PyInterpreterFrame *f) {
86+
static inline _PyStackRef _PyFrame_StackPeek(_PyInterpreterFrame *f) {
8687
assert(f->stacktop > _PyFrame_GetCode(f)->co_nlocalsplus);
87-
assert(f->localsplus[f->stacktop-1] != NULL);
88+
assert(!PyStackRef_IsNull(f->localsplus[f->stacktop-1]));
8889
return f->localsplus[f->stacktop-1];
8990
}
9091

91-
static inline PyObject *_PyFrame_StackPop(_PyInterpreterFrame *f) {
92+
static inline _PyStackRef _PyFrame_StackPop(_PyInterpreterFrame *f) {
9293
assert(f->stacktop > _PyFrame_GetCode(f)->co_nlocalsplus);
9394
f->stacktop--;
9495
return f->localsplus[f->stacktop];
9596
}
9697

97-
static inline void _PyFrame_StackPush(_PyInterpreterFrame *f, PyObject *value) {
98+
static inline void _PyFrame_StackPush(_PyInterpreterFrame *f, _PyStackRef value) {
9899
f->localsplus[f->stacktop] = value;
99100
f->stacktop++;
100101
}
@@ -143,14 +144,14 @@ _PyFrame_Initialize(
143144
frame->owner = FRAME_OWNED_BY_THREAD;
144145

145146
for (int i = null_locals_from; i < code->co_nlocalsplus; i++) {
146-
frame->localsplus[i] = NULL;
147+
frame->localsplus[i] = PyStackRef_NULL;
147148
}
148149
}
149150

150151
/* Gets the pointer to the locals array
151152
* that precedes this frame.
152153
*/
153-
static inline PyObject**
154+
static inline _PyStackRef*
154155
_PyFrame_GetLocalsArray(_PyInterpreterFrame *frame)
155156
{
156157
return frame->localsplus;
@@ -160,16 +161,16 @@ _PyFrame_GetLocalsArray(_PyInterpreterFrame *frame)
160161
Having stacktop <= 0 ensures that invalid
161162
values are not visible to the cycle GC.
162163
We choose -1 rather than 0 to assist debugging. */
163-
static inline PyObject**
164+
static inline _PyStackRef*
164165
_PyFrame_GetStackPointer(_PyInterpreterFrame *frame)
165166
{
166-
PyObject **sp = frame->localsplus + frame->stacktop;
167+
_PyStackRef *sp = frame->localsplus + frame->stacktop;
167168
frame->stacktop = -1;
168169
return sp;
169170
}
170171

171172
static inline void
172-
_PyFrame_SetStackPointer(_PyInterpreterFrame *frame, PyObject **stack_pointer)
173+
_PyFrame_SetStackPointer(_PyInterpreterFrame *frame, _PyStackRef *stack_pointer)
173174
{
174175
frame->stacktop = (int)(stack_pointer - frame->localsplus);
175176
}
@@ -309,7 +310,7 @@ _PyFrame_PushTrampolineUnchecked(PyThreadState *tstate, PyCodeObject *code, int
309310

310311
PyAPI_FUNC(_PyInterpreterFrame *)
311312
_PyEvalFramePushAndInit(PyThreadState *tstate, PyFunctionObject *func,
312-
PyObject *locals, PyObject* const* args,
313+
PyObject *locals, _PyStackRef const* args,
313314
size_t argcount, PyObject *kwnames);
314315

315316
#ifdef __cplusplus

Include/internal/pycore_jit.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ extern "C" {
1111

1212
#ifdef _Py_JIT
1313

14-
typedef _Py_CODEUNIT *(*jit_func)(_PyInterpreterFrame *frame, PyObject **stack_pointer, PyThreadState *tstate);
14+
typedef _Py_CODEUNIT *(*jit_func)(_PyInterpreterFrame *frame, _PyStackRef *stack_pointer, PyThreadState *tstate);
1515

1616
int _PyJIT_Compile(_PyExecutorObject *executor, const _PyUOpInstruction *trace, size_t length);
1717
void _PyJIT_Free(_PyExecutorObject *executor);

Include/internal/pycore_object.h

-15
Original file line numberDiff line numberDiff line change
@@ -159,21 +159,6 @@ static inline void _Py_ClearImmortal(PyObject *op)
159159
op = NULL; \
160160
} while (0)
161161

162-
// Mark an object as supporting deferred reference counting. This is a no-op
163-
// in the default (with GIL) build. Objects that use deferred reference
164-
// counting should be tracked by the GC so that they are eventually collected.
165-
extern void _PyObject_SetDeferredRefcount(PyObject *op);
166-
167-
static inline int
168-
_PyObject_HasDeferredRefcount(PyObject *op)
169-
{
170-
#ifdef Py_GIL_DISABLED
171-
return _PyObject_HAS_GC_BITS(op, _PyGC_BITS_DEFERRED);
172-
#else
173-
return 0;
174-
#endif
175-
}
176-
177162
#if !defined(Py_GIL_DISABLED)
178163
static inline void
179164
_Py_DECREF_SPECIALIZED(PyObject *op, const destructor destruct)
+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#ifndef Py_INTERNAL_OBJECT_DEFERRED_H
2+
#define Py_INTERNAL_OBJECT_DEFERRED_H
3+
4+
#ifdef __cplusplus
5+
extern "C" {
6+
#endif
7+
8+
#include "pycore_gc.h"
9+
10+
#ifndef Py_BUILD_CORE
11+
# error "this header requires Py_BUILD_CORE define"
12+
#endif
13+
14+
// Mark an object as supporting deferred reference counting. This is a no-op
15+
// in the default (with GIL) build. Objects that use deferred reference
16+
// counting should be tracked by the GC so that they are eventually collected.
17+
extern void _PyObject_SetDeferredRefcount(PyObject *op);
18+
19+
static inline int
20+
_PyObject_HasDeferredRefcount(PyObject *op)
21+
{
22+
#ifdef Py_GIL_DISABLED
23+
return _PyObject_HAS_GC_BITS(op, _PyGC_BITS_DEFERRED);
24+
#else
25+
return 0;
26+
#endif
27+
}
28+
29+
#ifdef __cplusplus
30+
}
31+
#endif
32+
#endif // !Py_INTERNAL_OBJECT_DEFERRED_H

Include/internal/pycore_optimizer.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ extern int _Py_uop_frame_pop(_Py_UOpsContext *ctx);
271271

272272
PyAPI_FUNC(PyObject *) _Py_uop_symbols_test(PyObject *self, PyObject *ignored);
273273

274-
PyAPI_FUNC(int) _PyOptimizer_Optimize(struct _PyInterpreterFrame *frame, _Py_CODEUNIT *start, PyObject **stack_pointer, _PyExecutorObject **exec_ptr);
274+
PyAPI_FUNC(int) _PyOptimizer_Optimize(struct _PyInterpreterFrame *frame, _Py_CODEUNIT *start, _PyStackRef *stack_pointer, _PyExecutorObject **exec_ptr);
275275

276276
#ifdef __cplusplus
277277
}

0 commit comments

Comments
 (0)