GH-132508: Use tagged integers on the evaluation stack for the last instruction offset (GH-132545)

This commit is contained in:
Mark Shannon
2025-04-29 18:00:35 +01:00
committed by GitHub
parent caee16f052
commit ccf1b0b1c1
14 changed files with 129 additions and 60 deletions

View File

@@ -1384,16 +1384,7 @@ dummy_func(
assert(oparg >= 0 && oparg <= 2);
if (oparg) {
PyObject *lasti = PyStackRef_AsPyObjectBorrow(values[0]);
if (PyLong_Check(lasti)) {
frame->instr_ptr = _PyFrame_GetBytecode(frame) + PyLong_AsLong(lasti);
assert(!_PyErr_Occurred(tstate));
}
else {
_PyErr_SetString(tstate, PyExc_SystemError, "lasti is not an int");
Py_DECREF(exc);
ERROR_NO_POP();
}
frame->instr_ptr = _PyFrame_GetBytecode(frame) + PyStackRef_UntagInt(values[0]);
}
assert(exc && PyExceptionInstance_Check(exc));
_PyErr_SetRaisedException(tstate, exc);
@@ -3472,7 +3463,7 @@ dummy_func(
if (tb == NULL) {
tb = Py_None;
}
assert(PyStackRef_LongCheck(lasti));
assert(PyStackRef_IsTaggedInt(lasti));
(void)lasti; // Shut up compiler warning if asserts are off
PyObject *stack[5] = {NULL, PyStackRef_AsPyObjectBorrow(exit_self), exc, val_o, tb};
int has_self = !PyStackRef_IsNull(exit_self);
@@ -5378,11 +5369,8 @@ dummy_func(
}
if (lasti) {
int frame_lasti = _PyInterpreterFrame_LASTI(frame);
PyObject *lasti = PyLong_FromLong(frame_lasti);
if (lasti == NULL) {
goto exception_unwind;
}
_PyFrame_StackPush(frame, PyStackRef_FromPyObjectSteal(lasti));
_PyStackRef lasti = PyStackRef_TagInt(frame_lasti);
_PyFrame_StackPush(frame, lasti);
}
/* Make the raw exception data

View File

@@ -146,6 +146,10 @@ dump_item(_PyStackRef item)
printf("<NULL>");
return;
}
if (PyStackRef_IsTaggedInt(item)) {
printf("%" PRId64, (int64_t)PyStackRef_UntagInt(item));
return;
}
PyObject *obj = PyStackRef_AsPyObjectBorrow(item);
if (obj == NULL) {
printf("<nil>");

View File

@@ -4470,7 +4470,7 @@
if (tb == NULL) {
tb = Py_None;
}
assert(PyStackRef_LongCheck(lasti));
assert(PyStackRef_IsTaggedInt(lasti));
(void)lasti;
PyObject *stack[5] = {NULL, PyStackRef_AsPyObjectBorrow(exit_self), exc, val_o, tb};
int has_self = !PyStackRef_IsNull(exit_self);

View File

@@ -547,6 +547,7 @@ _PyGC_VisitStackRef(_PyStackRef *ref, visitproc visit, void *arg)
// This is a bit tricky! We want to ignore stackrefs with embedded
// refcounts when computing the incoming references, but otherwise treat
// them like normal.
assert(!PyStackRef_IsTaggedInt(*ref));
if (!PyStackRef_RefcountOnObject(*ref) && (visit == visit_decref)) {
return 0;
}
@@ -560,7 +561,9 @@ _PyGC_VisitFrameStack(_PyInterpreterFrame *frame, visitproc visit, void *arg)
_PyStackRef *ref = _PyFrame_GetLocalsArray(frame);
/* locals and stack */
for (; ref < frame->stackpointer; ref++) {
_Py_VISIT_STACKREF(*ref);
if (!PyStackRef_IsTaggedInt(*ref)) {
_Py_VISIT_STACKREF(*ref);
}
}
return 0;
}
@@ -1495,8 +1498,11 @@ mark_stacks(PyInterpreterState *interp, PyGC_Head *visited, int visited_space, b
objects_marked += move_to_reachable(func, &reachable, visited_space);
while (sp > locals) {
sp--;
if (PyStackRef_IsNullOrInt(*sp)) {
continue;
}
PyObject *op = PyStackRef_AsPyObjectBorrow(*sp);
if (op == NULL || _Py_IsImmortal(op)) {
if (_Py_IsImmortal(op)) {
continue;
}
if (_PyObject_IS_GC(op)) {

View File

@@ -265,7 +265,7 @@ frame_disable_deferred_refcounting(_PyInterpreterFrame *frame)
frame->f_funcobj = PyStackRef_AsStrongReference(frame->f_funcobj);
for (_PyStackRef *ref = frame->localsplus; ref < frame->stackpointer; ref++) {
if (!PyStackRef_IsNull(*ref) && PyStackRef_IsDeferred(*ref)) {
if (!PyStackRef_IsNullOrInt(*ref) && PyStackRef_IsDeferred(*ref)) {
*ref = PyStackRef_AsStrongReference(*ref);
}
}
@@ -420,7 +420,7 @@ gc_visit_heaps(PyInterpreterState *interp, mi_block_visit_fun *visitor,
static inline void
gc_visit_stackref(_PyStackRef stackref)
{
if (PyStackRef_IsDeferred(stackref) && !PyStackRef_IsNull(stackref)) {
if (PyStackRef_IsDeferred(stackref) && !PyStackRef_IsNullOrInt(stackref)) {
PyObject *obj = PyStackRef_AsPyObjectBorrow(stackref);
if (_PyObject_GC_IS_TRACKED(obj) && !gc_is_frozen(obj)) {
gc_add_refs(obj, 1);
@@ -817,7 +817,7 @@ gc_abort_mark_alive(PyInterpreterState *interp,
static int
gc_visit_stackref_mark_alive(gc_mark_args_t *args, _PyStackRef stackref)
{
if (!PyStackRef_IsNull(stackref)) {
if (!PyStackRef_IsNullOrInt(stackref)) {
PyObject *op = PyStackRef_AsPyObjectBorrow(stackref);
if (gc_mark_enqueue(op, args) < 0) {
return -1;
@@ -1706,6 +1706,7 @@ _PyGC_VisitStackRef(_PyStackRef *ref, visitproc visit, void *arg)
// This is a bit tricky! We want to ignore deferred references when
// computing the incoming references, but otherwise treat them like
// regular references.
assert(!PyStackRef_IsTaggedInt(*ref));
if (!PyStackRef_IsDeferred(*ref) ||
(visit != visit_decref && visit != visit_decref_unreachable))
{

View File

@@ -10191,20 +10191,7 @@
PyObject *exc = PyStackRef_AsPyObjectSteal(exc_st);
assert(oparg >= 0 && oparg <= 2);
if (oparg) {
PyObject *lasti = PyStackRef_AsPyObjectBorrow(values[0]);
if (PyLong_Check(lasti)) {
frame->instr_ptr = _PyFrame_GetBytecode(frame) + PyLong_AsLong(lasti);
assert(!_PyErr_Occurred(tstate));
}
else {
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
_PyFrame_SetStackPointer(frame, stack_pointer);
_PyErr_SetString(tstate, PyExc_SystemError, "lasti is not an int");
Py_DECREF(exc);
stack_pointer = _PyFrame_GetStackPointer(frame);
JUMP_TO_LABEL(error);
}
frame->instr_ptr = _PyFrame_GetBytecode(frame) + PyStackRef_UntagInt(values[0]);
}
assert(exc && PyExceptionInstance_Check(exc));
stack_pointer += -1;
@@ -12059,7 +12046,7 @@
if (tb == NULL) {
tb = Py_None;
}
assert(PyStackRef_LongCheck(lasti));
assert(PyStackRef_IsTaggedInt(lasti));
(void)lasti;
PyObject *stack[5] = {NULL, PyStackRef_AsPyObjectBorrow(exit_self), exc, val_o, tb};
int has_self = !PyStackRef_IsNull(exit_self);
@@ -12231,11 +12218,8 @@ JUMP_TO_LABEL(error);
}
if (lasti) {
int frame_lasti = _PyInterpreterFrame_LASTI(frame);
PyObject *lasti = PyLong_FromLong(frame_lasti);
if (lasti == NULL) {
JUMP_TO_LABEL(exception_unwind);
}
_PyFrame_StackPush(frame, PyStackRef_FromPyObjectSteal(lasti));
_PyStackRef lasti = PyStackRef_TagInt(frame_lasti);
_PyFrame_StackPush(frame, lasti);
}
PyObject *exc = _PyErr_GetRaisedException(tstate);
_PyFrame_StackPush(frame, PyStackRef_FromPyObjectSteal(exc));

View File

@@ -684,7 +684,7 @@ init_interpreter(PyInterpreterState *interp,
interp->dtoa = (struct _dtoa_state)_dtoa_state_INIT(interp);
}
#if !defined(Py_GIL_DISABLED) && defined(Py_STACKREF_DEBUG)
interp->next_stackref = 1;
interp->next_stackref = INITIAL_STACKREF_INDEX;
_Py_hashtable_allocator_t alloc = {
.malloc = malloc,
.free = free,

View File

@@ -70,7 +70,7 @@ _Py_stackref_close(_PyStackRef ref, const char *filename, int linenumber)
}
PyObject *obj;
if (ref.index <= LAST_PREDEFINED_STACKREF_INDEX) {
if (ref.index < INITIAL_STACKREF_INDEX) {
if (ref.index == 0) {
_Py_FatalErrorFormat(__func__, "Passing NULL to PyStackRef_CLOSE at %s:%d\n", filename, linenumber);
}
@@ -113,7 +113,8 @@ _Py_stackref_create(PyObject *obj, const char *filename, int linenumber)
Py_FatalError("Cannot create a stackref for NULL");
}
PyInterpreterState *interp = PyInterpreterState_Get();
uint64_t new_id = interp->next_stackref++;
uint64_t new_id = interp->next_stackref;
interp->next_stackref = new_id + 2;
TableEntry *entry = make_table_entry(obj, filename, linenumber);
if (entry == NULL) {
Py_FatalError("No memory left for stackref debug table");
@@ -127,7 +128,7 @@ _Py_stackref_create(PyObject *obj, const char *filename, int linenumber)
void
_Py_stackref_record_borrow(_PyStackRef ref, const char *filename, int linenumber)
{
if (ref.index <= LAST_PREDEFINED_STACKREF_INDEX) {
if (ref.index < INITIAL_STACKREF_INDEX) {
return;
}
PyInterpreterState *interp = PyInterpreterState_Get();
@@ -151,8 +152,7 @@ _Py_stackref_record_borrow(_PyStackRef ref, const char *filename, int linenumber
void
_Py_stackref_associate(PyInterpreterState *interp, PyObject *obj, _PyStackRef ref)
{
assert(interp->next_stackref >= ref.index);
interp->next_stackref = ref.index+1;
assert(ref.index < INITIAL_STACKREF_INDEX);
TableEntry *entry = make_table_entry(obj, "builtin-object", 0);
if (entry == NULL) {
Py_FatalError("No memory left for stackref debug table");
@@ -197,4 +197,23 @@ _PyStackRef_CLOSE_SPECIALIZED(_PyStackRef ref, destructor destruct, const char *
_Py_DECREF_SPECIALIZED(obj, destruct);
}
_PyStackRef PyStackRef_TagInt(intptr_t i)
{
return (_PyStackRef){ .index = (i << 1) + 1 };
}
intptr_t
PyStackRef_UntagInt(_PyStackRef i)
{
assert(PyStackRef_IsTaggedInt(i));
intptr_t val = (intptr_t)i.index;
return Py_ARITHMETIC_RIGHT_SHIFT(intptr_t, val, 1);
}
bool
PyStackRef_IsNullOrInt(_PyStackRef ref)
{
return PyStackRef_IsNull(ref) || PyStackRef_IsTaggedInt(ref);
}
#endif