Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit ccf1b0b

Browse filesBrowse files
authored
GH-132508: Use tagged integers on the evaluation stack for the last instruction offset (GH-132545)
1 parent caee16f commit ccf1b0b
Copy full SHA for ccf1b0b

File tree

Expand file treeCollapse file tree

14 files changed

+129
-60
lines changed
Filter options
Expand file treeCollapse file tree

14 files changed

+129
-60
lines changed

‎Include/internal/pycore_interpframe.h

Copy file name to clipboardExpand all lines: Include/internal/pycore_interpframe.h
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ extern "C" {
1818
((int)((IF)->instr_ptr - _PyFrame_GetBytecode((IF))))
1919

2020
static inline PyCodeObject *_PyFrame_GetCode(_PyInterpreterFrame *f) {
21+
assert(!PyStackRef_IsNull(f->f_executable));
2122
PyObject *executable = PyStackRef_AsPyObjectBorrow(f->f_executable);
2223
assert(PyCode_Check(executable));
2324
return (PyCodeObject *)executable;

‎Include/internal/pycore_stackref.h

Copy file name to clipboardExpand all lines: Include/internal/pycore_stackref.h
+68-10Lines changed: 68 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,13 @@ extern void _Py_stackref_associate(PyInterpreterState *interp, PyObject *obj, _P
6363

6464
static const _PyStackRef PyStackRef_NULL = { .index = 0 };
6565

66-
#define PyStackRef_None ((_PyStackRef){ .index = 1 } )
67-
#define PyStackRef_False ((_PyStackRef){ .index = 2 })
68-
#define PyStackRef_True ((_PyStackRef){ .index = 3 })
66+
// Use the first 3 even numbers for None, True and False.
67+
// Odd numbers are reserved for (tagged) integers
68+
#define PyStackRef_None ((_PyStackRef){ .index = 2 } )
69+
#define PyStackRef_False ((_PyStackRef){ .index = 4 })
70+
#define PyStackRef_True ((_PyStackRef){ .index = 6 })
6971

70-
#define LAST_PREDEFINED_STACKREF_INDEX 3
72+
#define INITIAL_STACKREF_INDEX 8
7173

7274
static inline int
7375
PyStackRef_IsNull(_PyStackRef ref)
@@ -96,6 +98,7 @@ PyStackRef_IsNone(_PyStackRef ref)
9698
static inline PyObject *
9799
_PyStackRef_AsPyObjectBorrow(_PyStackRef ref, const char *filename, int linenumber)
98100
{
101+
assert((ref.index & 1) == 0);
99102
_Py_stackref_record_borrow(ref, filename, linenumber);
100103
return _Py_stackref_get_object(ref);
101104
}
@@ -132,31 +135,45 @@ _PyStackRef_FromPyObjectImmortal(PyObject *obj, const char *filename, int linenu
132135
}
133136
#define PyStackRef_FromPyObjectImmortal(obj) _PyStackRef_FromPyObjectImmortal(_PyObject_CAST(obj), __FILE__, __LINE__)
134137

138+
static inline bool
139+
PyStackRef_IsTaggedInt(_PyStackRef ref)
140+
{
141+
return (ref.index & 1) == 1;
142+
}
143+
135144
static inline void
136145
_PyStackRef_CLOSE(_PyStackRef ref, const char *filename, int linenumber)
137146
{
147+
if (PyStackRef_IsTaggedInt(ref)) {
148+
return;
149+
}
138150
PyObject *obj = _Py_stackref_close(ref, filename, linenumber);
139151
Py_DECREF(obj);
140152
}
141153
#define PyStackRef_CLOSE(REF) _PyStackRef_CLOSE((REF), __FILE__, __LINE__)
142154

155+
143156
static inline void
144157
_PyStackRef_XCLOSE(_PyStackRef ref, const char *filename, int linenumber)
145158
{
146159
if (PyStackRef_IsNull(ref)) {
147160
return;
148161
}
149-
PyObject *obj = _Py_stackref_close(ref, filename, linenumber);
150-
Py_DECREF(obj);
162+
_PyStackRef_CLOSE(ref, filename, linenumber);
151163
}
152164
#define PyStackRef_XCLOSE(REF) _PyStackRef_XCLOSE((REF), __FILE__, __LINE__)
153165

154166
static inline _PyStackRef
155167
_PyStackRef_DUP(_PyStackRef ref, const char *filename, int linenumber)
156168
{
157-
PyObject *obj = _Py_stackref_get_object(ref);
158-
Py_INCREF(obj);
159-
return _Py_stackref_create(obj, filename, linenumber);
169+
if (PyStackRef_IsTaggedInt(ref)) {
170+
return ref;
171+
}
172+
else {
173+
PyObject *obj = _Py_stackref_get_object(ref);
174+
Py_INCREF(obj);
175+
return _Py_stackref_create(obj, filename, linenumber);
176+
}
160177
}
161178
#define PyStackRef_DUP(REF) _PyStackRef_DUP(REF, __FILE__, __LINE__)
162179

@@ -210,8 +227,40 @@ _PyStackRef_FromPyObjectNewMortal(PyObject *obj, const char *filename, int linen
210227

211228
extern int PyStackRef_Is(_PyStackRef a, _PyStackRef b);
212229

230+
extern bool PyStackRef_IsTaggedInt(_PyStackRef ref);
231+
232+
extern intptr_t PyStackRef_UntagInt(_PyStackRef ref);
233+
234+
extern _PyStackRef PyStackRef_TagInt(intptr_t i);
235+
236+
extern bool
237+
PyStackRef_IsNullOrInt(_PyStackRef ref);
238+
213239
#else
214240

241+
#define Py_INT_TAG 3
242+
243+
static inline bool
244+
PyStackRef_IsTaggedInt(_PyStackRef i)
245+
{
246+
return (i.bits & Py_INT_TAG) == Py_INT_TAG;
247+
}
248+
249+
static inline _PyStackRef
250+
PyStackRef_TagInt(intptr_t i)
251+
{
252+
assert(Py_ARITHMETIC_RIGHT_SHIFT(intptr_t, (i << 2), 2) == i);
253+
return (_PyStackRef){ .bits = ((((uintptr_t)i) << 2) | Py_INT_TAG) };
254+
}
255+
256+
static inline intptr_t
257+
PyStackRef_UntagInt(_PyStackRef i)
258+
{
259+
assert((i.bits & Py_INT_TAG) == Py_INT_TAG);
260+
intptr_t val = (intptr_t)i.bits;
261+
return Py_ARITHMETIC_RIGHT_SHIFT(intptr_t, val, 2);
262+
}
263+
215264

216265
#ifdef Py_GIL_DISABLED
217266

@@ -232,6 +281,8 @@ static const _PyStackRef PyStackRef_NULL = { .bits = Py_TAG_DEFERRED};
232281
#define PyStackRef_IsTrue(ref) (PyStackRef_AsPyObjectBorrow(ref) == Py_True)
233282
#define PyStackRef_IsFalse(ref) (PyStackRef_AsPyObjectBorrow(ref) == Py_False)
234283

284+
#define PyStackRef_IsNullOrInt(stackref) (PyStackRef_IsNull(stackref) || PyStackRef_IsTaggedInt(stackref))
285+
235286
static inline PyObject *
236287
PyStackRef_AsPyObjectBorrow(_PyStackRef stackref)
237288
{
@@ -451,6 +502,7 @@ PyStackRef_RefcountOnObject(_PyStackRef ref)
451502
static inline PyObject *
452503
PyStackRef_AsPyObjectBorrow(_PyStackRef ref)
453504
{
505+
assert(!PyStackRef_IsTaggedInt(ref));
454506
return BITS_TO_PTR_MASKED(ref);
455507
}
456508

@@ -587,6 +639,12 @@ PyStackRef_CLOSE(_PyStackRef ref)
587639
}
588640
#endif
589641

642+
static inline bool
643+
PyStackRef_IsNullOrInt(_PyStackRef ref)
644+
{
645+
return PyStackRef_IsNull(ref) || PyStackRef_IsTaggedInt(ref);
646+
}
647+
590648
static inline void
591649
PyStackRef_CLOSE_SPECIALIZED(_PyStackRef ref, destructor destruct)
592650
{
@@ -726,7 +784,7 @@ _Py_TryXGetStackRef(PyObject **src, _PyStackRef *out)
726784
// Like Py_VISIT but for _PyStackRef fields
727785
#define _Py_VISIT_STACKREF(ref) \
728786
do { \
729-
if (!PyStackRef_IsNull(ref)) { \
787+
if (!PyStackRef_IsNullOrInt(ref)) { \
730788
int vret = _PyGC_VisitStackRef(&(ref), visit, arg); \
731789
if (vret) \
732790
return vret; \
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Uses tagged integers on the evaluation stack to represent the instruction
2+
offsets when reraising an exception. This avoids the need to box the integer
3+
which could fail in low memory conditions.

‎Objects/frameobject.c

Copy file name to clipboardExpand all lines: Objects/frameobject.c
+3-2Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1813,15 +1813,16 @@ frame_lineno_set_impl(PyFrameObject *self, PyObject *value)
18131813
start_stack = pop_value(start_stack);
18141814
}
18151815
while (start_stack > best_stack) {
1816+
_PyStackRef popped = _PyFrame_StackPop(self->f_frame);
18161817
if (top_of_stack(start_stack) == Except) {
18171818
/* Pop exception stack as well as the evaluation stack */
1818-
PyObject *exc = PyStackRef_AsPyObjectBorrow(_PyFrame_StackPop(self->f_frame));
1819+
PyObject *exc = PyStackRef_AsPyObjectBorrow(popped);
18191820
assert(PyExceptionInstance_Check(exc) || exc == Py_None);
18201821
PyThreadState *tstate = _PyThreadState_GET();
18211822
Py_XSETREF(tstate->exc_info->exc_value, exc == Py_None ? NULL : exc);
18221823
}
18231824
else {
1824-
PyStackRef_XCLOSE(_PyFrame_StackPop(self->f_frame));
1825+
PyStackRef_XCLOSE(popped);
18251826
}
18261827
start_stack = pop_value(start_stack);
18271828
}

‎Python/bytecodes.c

Copy file name to clipboardExpand all lines: Python/bytecodes.c
+4-16Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1384,16 +1384,7 @@ dummy_func(
13841384

13851385
assert(oparg >= 0 && oparg <= 2);
13861386
if (oparg) {
1387-
PyObject *lasti = PyStackRef_AsPyObjectBorrow(values[0]);
1388-
if (PyLong_Check(lasti)) {
1389-
frame->instr_ptr = _PyFrame_GetBytecode(frame) + PyLong_AsLong(lasti);
1390-
assert(!_PyErr_Occurred(tstate));
1391-
}
1392-
else {
1393-
_PyErr_SetString(tstate, PyExc_SystemError, "lasti is not an int");
1394-
Py_DECREF(exc);
1395-
ERROR_NO_POP();
1396-
}
1387+
frame->instr_ptr = _PyFrame_GetBytecode(frame) + PyStackRef_UntagInt(values[0]);
13971388
}
13981389
assert(exc && PyExceptionInstance_Check(exc));
13991390
_PyErr_SetRaisedException(tstate, exc);
@@ -3472,7 +3463,7 @@ dummy_func(
34723463
if (tb == NULL) {
34733464
tb = Py_None;
34743465
}
3475-
assert(PyStackRef_LongCheck(lasti));
3466+
assert(PyStackRef_IsTaggedInt(lasti));
34763467
(void)lasti; // Shut up compiler warning if asserts are off
34773468
PyObject *stack[5] = {NULL, PyStackRef_AsPyObjectBorrow(exit_self), exc, val_o, tb};
34783469
int has_self = !PyStackRef_IsNull(exit_self);
@@ -5378,11 +5369,8 @@ dummy_func(
53785369
}
53795370
if (lasti) {
53805371
int frame_lasti = _PyInterpreterFrame_LASTI(frame);
5381-
PyObject *lasti = PyLong_FromLong(frame_lasti);
5382-
if (lasti == NULL) {
5383-
goto exception_unwind;
5384-
}
5385-
_PyFrame_StackPush(frame, PyStackRef_FromPyObjectSteal(lasti));
5372+
_PyStackRef lasti = PyStackRef_TagInt(frame_lasti);
5373+
_PyFrame_StackPush(frame, lasti);
53865374
}
53875375

53885376
/* Make the raw exception data

‎Python/ceval.c

Copy file name to clipboardExpand all lines: Python/ceval.c
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,10 @@ dump_item(_PyStackRef item)
146146
printf("<NULL>");
147147
return;
148148
}
149+
if (PyStackRef_IsTaggedInt(item)) {
150+
printf("%" PRId64, (int64_t)PyStackRef_UntagInt(item));
151+
return;
152+
}
149153
PyObject *obj = PyStackRef_AsPyObjectBorrow(item);
150154
if (obj == NULL) {
151155
printf("<nil>");

‎Python/executor_cases.c.h

Copy file name to clipboardExpand all lines: Python/executor_cases.c.h
+1-1Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Python/gc.c

Copy file name to clipboardExpand all lines: Python/gc.c
+8-2Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,7 @@ _PyGC_VisitStackRef(_PyStackRef *ref, visitproc visit, void *arg)
547547
// This is a bit tricky! We want to ignore stackrefs with embedded
548548
// refcounts when computing the incoming references, but otherwise treat
549549
// them like normal.
550+
assert(!PyStackRef_IsTaggedInt(*ref));
550551
if (!PyStackRef_RefcountOnObject(*ref) && (visit == visit_decref)) {
551552
return 0;
552553
}
@@ -560,7 +561,9 @@ _PyGC_VisitFrameStack(_PyInterpreterFrame *frame, visitproc visit, void *arg)
560561
_PyStackRef *ref = _PyFrame_GetLocalsArray(frame);
561562
/* locals and stack */
562563
for (; ref < frame->stackpointer; ref++) {
563-
_Py_VISIT_STACKREF(*ref);
564+
if (!PyStackRef_IsTaggedInt(*ref)) {
565+
_Py_VISIT_STACKREF(*ref);
566+
}
564567
}
565568
return 0;
566569
}
@@ -1495,8 +1498,11 @@ mark_stacks(PyInterpreterState *interp, PyGC_Head *visited, int visited_space, b
14951498
objects_marked += move_to_reachable(func, &reachable, visited_space);
14961499
while (sp > locals) {
14971500
sp--;
1501+
if (PyStackRef_IsNullOrInt(*sp)) {
1502+
continue;
1503+
}
14981504
PyObject *op = PyStackRef_AsPyObjectBorrow(*sp);
1499-
if (op == NULL || _Py_IsImmortal(op)) {
1505+
if (_Py_IsImmortal(op)) {
15001506
continue;
15011507
}
15021508
if (_PyObject_IS_GC(op)) {

‎Python/gc_free_threading.c

Copy file name to clipboardExpand all lines: Python/gc_free_threading.c
+4-3Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ frame_disable_deferred_refcounting(_PyInterpreterFrame *frame)
265265

266266
frame->f_funcobj = PyStackRef_AsStrongReference(frame->f_funcobj);
267267
for (_PyStackRef *ref = frame->localsplus; ref < frame->stackpointer; ref++) {
268-
if (!PyStackRef_IsNull(*ref) && PyStackRef_IsDeferred(*ref)) {
268+
if (!PyStackRef_IsNullOrInt(*ref) && PyStackRef_IsDeferred(*ref)) {
269269
*ref = PyStackRef_AsStrongReference(*ref);
270270
}
271271
}
@@ -420,7 +420,7 @@ gc_visit_heaps(PyInterpreterState *interp, mi_block_visit_fun *visitor,
420420
static inline void
421421
gc_visit_stackref(_PyStackRef stackref)
422422
{
423-
if (PyStackRef_IsDeferred(stackref) && !PyStackRef_IsNull(stackref)) {
423+
if (PyStackRef_IsDeferred(stackref) && !PyStackRef_IsNullOrInt(stackref)) {
424424
PyObject *obj = PyStackRef_AsPyObjectBorrow(stackref);
425425
if (_PyObject_GC_IS_TRACKED(obj) && !gc_is_frozen(obj)) {
426426
gc_add_refs(obj, 1);
@@ -817,7 +817,7 @@ gc_abort_mark_alive(PyInterpreterState *interp,
817817
static int
818818
gc_visit_stackref_mark_alive(gc_mark_args_t *args, _PyStackRef stackref)
819819
{
820-
if (!PyStackRef_IsNull(stackref)) {
820+
if (!PyStackRef_IsNullOrInt(stackref)) {
821821
PyObject *op = PyStackRef_AsPyObjectBorrow(stackref);
822822
if (gc_mark_enqueue(op, args) < 0) {
823823
return -1;
@@ -1706,6 +1706,7 @@ _PyGC_VisitStackRef(_PyStackRef *ref, visitproc visit, void *arg)
17061706
// This is a bit tricky! We want to ignore deferred references when
17071707
// computing the incoming references, but otherwise treat them like
17081708
// regular references.
1709+
assert(!PyStackRef_IsTaggedInt(*ref));
17091710
if (!PyStackRef_IsDeferred(*ref) ||
17101711
(visit != visit_decref && visit != visit_decref_unreachable))
17111712
{

‎Python/generated_cases.c.h

Copy file name to clipboardExpand all lines: Python/generated_cases.c.h
+4-20Lines changed: 4 additions & 20 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Python/pystate.c

Copy file name to clipboardExpand all lines: Python/pystate.c
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -684,7 +684,7 @@ init_interpreter(PyInterpreterState *interp,
684684
interp->dtoa = (struct _dtoa_state)_dtoa_state_INIT(interp);
685685
}
686686
#if !defined(Py_GIL_DISABLED) && defined(Py_STACKREF_DEBUG)
687-
interp->next_stackref = 1;
687+
interp->next_stackref = INITIAL_STACKREF_INDEX;
688688
_Py_hashtable_allocator_t alloc = {
689689
.malloc = malloc,
690690
.free = free,

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.