GH-131498: Cases generator: manage stacks automatically (GH-132074)

This commit is contained in:
Mark Shannon 2025-04-04 17:59:36 +01:00 committed by GitHub
parent 305be5fb1a
commit 7099c75550
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 216 additions and 251 deletions

View file

@ -234,7 +234,7 @@ int _PyOpcode_num_popped(int opcode, int oparg) {
case INSTRUMENTED_END_SEND:
return 2;
case INSTRUMENTED_FOR_ITER:
return 0;
return 1;
case INSTRUMENTED_INSTRUCTION:
return 0;
case INSTRUMENTED_JUMP_BACKWARD:
@ -250,13 +250,13 @@ int _PyOpcode_num_popped(int opcode, int oparg) {
case INSTRUMENTED_POP_ITER:
return 1;
case INSTRUMENTED_POP_JUMP_IF_FALSE:
return 0;
return 1;
case INSTRUMENTED_POP_JUMP_IF_NONE:
return 0;
return 1;
case INSTRUMENTED_POP_JUMP_IF_NOT_NONE:
return 0;
return 1;
case INSTRUMENTED_POP_JUMP_IF_TRUE:
return 0;
return 1;
case INSTRUMENTED_RESUME:
return 0;
case INSTRUMENTED_RETURN_VALUE:
@ -713,7 +713,7 @@ int _PyOpcode_num_pushed(int opcode, int oparg) {
case INSTRUMENTED_END_SEND:
return 1;
case INSTRUMENTED_FOR_ITER:
return 0;
return 2;
case INSTRUMENTED_INSTRUCTION:
return 0;
case INSTRUMENTED_JUMP_BACKWARD:

View file

@ -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

View file

@ -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;

View file

@ -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();
@ -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;
}
@ -1123,7 +1123,7 @@ dummy_func(
_PyStackRef temp = PyStackRef_MakeHeapSafe(retval);
DEAD(retval);
SAVE_STACK();
assert(EMPTY());
assert(STACK_LEVEL() == 0);
_Py_LeaveRecursiveCallPy(tstate);
// GH-99729: We need to unlink the frame *before* clearing it:
_PyInterpreterFrame *dying = frame;
@ -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;
@ -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);
@ -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 {
@ -3105,6 +3105,7 @@ dummy_func(
next_instr[oparg].op.code == INSTRUMENTED_END_FOR);
/* Skip END_FOR */
JUMPBY(oparg + 1);
DISPATCH();
}
}
@ -4022,7 +4023,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'",
@ -4813,7 +4813,7 @@ dummy_func(
PyFunctionObject *func = (PyFunctionObject *)PyStackRef_AsPyObjectBorrow(frame->f_funcobj);
PyGenObject *gen = (PyGenObject *)_Py_MakeCoro(func);
ERROR_IF(gen == NULL, error);
assert(EMPTY());
assert(STACK_LEVEL() == 0);
SAVE_STACK();
_PyInterpreterFrame *gen_frame = &gen->gi_iframe;
frame->instr_ptr++;
@ -4932,6 +4932,7 @@ dummy_func(
}
next_instr = frame->instr_ptr;
if (next_instr != this_instr) {
SYNC_SP();
DISPATCH();
}
}
@ -4976,46 +4977,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, ( -- )) {
@ -5219,22 +5222,26 @@ dummy_func(
}
label(pop_4_error) {
STACK_SHRINK(4);
stack_pointer -= 4;
assert(WITHIN_STACK_BOUNDS());
goto error;
}
label(pop_3_error) {
STACK_SHRINK(3);
stack_pointer -= 3;
assert(WITHIN_STACK_BOUNDS());
goto error;
}
label(pop_2_error) {
STACK_SHRINK(2);
stack_pointer -= 2;
assert(WITHIN_STACK_BOUNDS());
goto error;
}
label(pop_1_error) {
STACK_SHRINK(1);
stack_pointer -= 1;
assert(WITHIN_STACK_BOUNDS());
goto error;
}

View file

@ -151,18 +151,6 @@ dump_item(_PyStackRef item)
printf("<nil>");
return;
}
if (
obj == Py_None
|| PyBool_Check(obj)
|| PyLong_CheckExact(obj)
|| PyFloat_CheckExact(obj)
|| PyUnicode_CheckExact(obj)
) {
if (PyObject_Print(obj, stdout, 0) == 0) {
return;
}
PyErr_Clear();
}
// Don't call __repr__(), it might recurse into the interpreter.
printf("<%s at %p>", Py_TYPE(obj)->tp_name, (void *)obj);
}
@ -182,14 +170,19 @@ dump_stack(_PyInterpreterFrame *frame, _PyStackRef *stack_pointer)
dump_item(*ptr);
}
printf("]\n");
printf(" stack=[");
for (_PyStackRef *ptr = stack_base; ptr < stack_pointer; ptr++) {
if (ptr != stack_base) {
printf(", ");
}
dump_item(*ptr);
if (stack_pointer < stack_base) {
printf(" stack=%d\n", (int)(stack_pointer-stack_base));
}
else {
printf(" stack=[");
for (_PyStackRef *ptr = stack_base; ptr < stack_pointer; ptr++) {
if (ptr != stack_base) {
printf(", ");
}
dump_item(*ptr);
}
printf("]\n");
}
printf("]\n");
fflush(stdout);
PyErr_SetRaisedException(exc);
_PyFrame_GetStackPointer(frame);
@ -202,13 +195,13 @@ lltrace_instruction(_PyInterpreterFrame *frame,
int opcode,
int oparg)
{
if (frame->owner >= FRAME_OWNED_BY_INTERPRETER) {
return;
int offset = 0;
if (frame->owner < FRAME_OWNED_BY_INTERPRETER) {
dump_stack(frame, stack_pointer);
offset = (int)(next_instr - _PyFrame_GetBytecode(frame));
}
dump_stack(frame, stack_pointer);
const char *opname = _PyOpcode_OpName[opcode];
assert(opname != NULL);
int offset = (int)(next_instr - _PyFrame_GetBytecode(frame));
if (OPCODE_HAS_ARG((int)_PyOpcode_Deopt[opcode])) {
printf("%d: %s %d\n", offset * 2, opname, oparg);
}
@ -986,8 +979,10 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
* These are cached values from the frame and code object. */
_Py_CODEUNIT *next_instr;
_PyStackRef *stack_pointer;
#if defined(Py_DEBUG) && !defined(Py_STACKREF_DEBUG)
entry_frame.localsplus[0] = PyStackRef_NULL;
#ifdef Py_STACKREF_DEBUG
entry_frame.f_funcobj = PyStackRef_None;
#elif defined(Py_DEBUG)
/* Set these to invalid but identifiable values for debugging. */
entry_frame.f_funcobj = (_PyStackRef){.bits = 0xaaa0};
entry_frame.f_locals = (PyObject*)0xaaa1;
@ -1044,7 +1039,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
_PyExecutorObject *current_executor = NULL;
const _PyUOpInstruction *next_uop = NULL;
#endif
#if Py_TAIL_CALL_INTERP
return _TAIL_CALL_start_frame(frame, NULL, tstate, NULL, 0);
#else

View file

@ -194,48 +194,8 @@ GETITEM(PyObject *v, Py_ssize_t i) {
#define JUMPBY(x) (next_instr += (x))
#define SKIP_OVER(x) (next_instr += (x))
/* Stack manipulation macros */
/* The stack can grow at most MAXINT deep, as co_nlocals and
co_stacksize are ints. */
#define STACK_LEVEL() ((int)(stack_pointer - _PyFrame_Stackbase(frame)))
#define STACK_SIZE() (_PyFrame_GetCode(frame)->co_stacksize)
#define EMPTY() (STACK_LEVEL() == 0)
#define TOP() (stack_pointer[-1])
#define SECOND() (stack_pointer[-2])
#define THIRD() (stack_pointer[-3])
#define FOURTH() (stack_pointer[-4])
#define PEEK(n) (stack_pointer[-(n)])
#define POKE(n, v) (stack_pointer[-(n)] = (v))
#define SET_TOP(v) (stack_pointer[-1] = (v))
#define SET_SECOND(v) (stack_pointer[-2] = (v))
#define BASIC_STACKADJ(n) (stack_pointer += n)
#define BASIC_PUSH(v) (*stack_pointer++ = (v))
#define BASIC_POP() (*--stack_pointer)
#ifdef Py_DEBUG
#define PUSH(v) do { \
BASIC_PUSH(v); \
assert(STACK_LEVEL() <= STACK_SIZE()); \
} while (0)
#define POP() (assert(STACK_LEVEL() > 0), BASIC_POP())
#define STACK_GROW(n) do { \
assert(n >= 0); \
BASIC_STACKADJ(n); \
assert(STACK_LEVEL() <= STACK_SIZE()); \
} while (0)
#define STACK_SHRINK(n) do { \
assert(n >= 0); \
assert(STACK_LEVEL() >= n); \
BASIC_STACKADJ(-(n)); \
} while (0)
#else
#define PUSH(v) BASIC_PUSH(v)
#define POP() BASIC_POP()
#define STACK_GROW(n) BASIC_STACKADJ(n)
#define STACK_SHRINK(n) BASIC_STACKADJ(-(n))
#endif
#define WITHIN_STACK_BOUNDS() \
(frame->owner == FRAME_OWNED_BY_INTERPRETER || (STACK_LEVEL() >= 0 && STACK_LEVEL() <= STACK_SIZE()))

View file

@ -714,7 +714,6 @@
stack_pointer = _PyFrame_GetStackPointer(frame);
res = PyStackRef_True;
stack_pointer += 1;
assert(WITHIN_STACK_BOUNDS());
}
stack_pointer[-1] = res;
break;
@ -798,7 +797,6 @@
stack_pointer = _PyFrame_GetStackPointer(frame);
res = PyStackRef_True;
stack_pointer += 1;
assert(WITHIN_STACK_BOUNDS());
}
stack_pointer[-1] = res;
break;
@ -1206,7 +1204,6 @@
Py_DECREF(slice);
stack_pointer = _PyFrame_GetStackPointer(frame);
stack_pointer += 2;
assert(WITHIN_STACK_BOUNDS());
}
stack_pointer += -3;
assert(WITHIN_STACK_BOUNDS());
@ -1248,21 +1245,18 @@
Py_DECREF(slice);
stack_pointer = _PyFrame_GetStackPointer(frame);
stack_pointer += 2;
assert(WITHIN_STACK_BOUNDS());
}
stack_pointer += -2;
assert(WITHIN_STACK_BOUNDS());
_PyFrame_SetStackPointer(frame, stack_pointer);
_PyStackRef tmp = container;
container = PyStackRef_NULL;
stack_pointer[-1] = container;
stack_pointer[-3] = container;
PyStackRef_CLOSE(tmp);
tmp = v;
v = PyStackRef_NULL;
stack_pointer[-2] = v;
stack_pointer[-4] = v;
PyStackRef_CLOSE(tmp);
stack_pointer = _PyFrame_GetStackPointer(frame);
stack_pointer += -2;
stack_pointer += -4;
assert(WITHIN_STACK_BOUNDS());
if (err) {
JUMP_TO_ERROR();
@ -1732,7 +1726,7 @@
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
_PyFrame_SetStackPointer(frame, stack_pointer);
assert(EMPTY());
assert(STACK_LEVEL() == 0);
_Py_LeaveRecursiveCallPy(tstate);
_PyInterpreterFrame *dying = frame;
frame = tstate->current_frame = dying->previous;
@ -2073,7 +2067,6 @@
_PyFrame_SetStackPointer(frame, stack_pointer);
PyStackRef_CLOSE(seq);
stack_pointer = _PyFrame_GetStackPointer(frame);
stack_pointer[-1] = val0;
break;
}
@ -3004,7 +2997,6 @@
stack_pointer = _PyFrame_GetStackPointer(frame);
self_or_null = PyStackRef_NULL;
stack_pointer += 1;
assert(WITHIN_STACK_BOUNDS());
}
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
@ -3057,7 +3049,6 @@
}
self_or_null[0] = PyStackRef_NULL;
stack_pointer += 1;
assert(WITHIN_STACK_BOUNDS());
}
}
else {
@ -3073,7 +3064,6 @@
JUMP_TO_ERROR();
}
stack_pointer += 1;
assert(WITHIN_STACK_BOUNDS());
}
attr = PyStackRef_FromPyObjectSteal(attr_o);
stack_pointer[-1] = attr;
@ -5289,7 +5279,6 @@
case _EXIT_INIT_CHECK: {
_PyStackRef should_be_none;
should_be_none = stack_pointer[-1];
assert(STACK_LEVEL() == 2);
if (!PyStackRef_IsNone(should_be_none)) {
_PyFrame_SetStackPointer(frame, stack_pointer);
PyErr_Format(PyExc_TypeError,
@ -6347,7 +6336,6 @@
stack_pointer = _PyFrame_GetStackPointer(frame);
tuple = PyStackRef_FromPyObjectSteal(tuple_o);
stack_pointer += 2;
assert(WITHIN_STACK_BOUNDS());
}
stack_pointer[-2] = tuple;
stack_pointer[-1] = kwargs_out;
@ -6414,7 +6402,7 @@
if (gen == NULL) {
JUMP_TO_ERROR();
}
assert(EMPTY());
assert(STACK_LEVEL() == 0);
_PyFrame_SetStackPointer(frame, stack_pointer);
_PyInterpreterFrame *gen_frame = &gen->gi_iframe;
frame->instr_ptr++;
@ -6513,7 +6501,6 @@
else {
res = value;
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
}
stack_pointer[0] = res;
stack_pointer += 1;

View file

@ -1062,7 +1062,6 @@
Py_DECREF(slice);
stack_pointer = _PyFrame_GetStackPointer(frame);
stack_pointer += 2;
assert(WITHIN_STACK_BOUNDS());
}
stack_pointer += -3;
assert(WITHIN_STACK_BOUNDS());
@ -1498,7 +1497,6 @@
JUMP_TO_LABEL(error);
}
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
}
}
stack_pointer[0] = res;
@ -1983,7 +1981,6 @@
JUMP_TO_LABEL(error);
}
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
}
}
stack_pointer[0] = res;
@ -2097,7 +2094,6 @@
JUMP_TO_LABEL(error);
}
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
}
}
stack_pointer[0] = res;
@ -2212,7 +2208,6 @@
JUMP_TO_LABEL(error);
}
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
}
}
stack_pointer[0] = res;
@ -2305,7 +2300,6 @@
JUMP_TO_LABEL(error);
}
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
}
}
stack_pointer[0] = res;
@ -2366,7 +2360,6 @@
stack_pointer = _PyFrame_GetStackPointer(frame);
tuple = PyStackRef_FromPyObjectSteal(tuple_o);
stack_pointer += 2;
assert(WITHIN_STACK_BOUNDS());
}
}
// _DO_CALL_FUNCTION_EX
@ -2494,7 +2487,6 @@
JUMP_TO_LABEL(error);
}
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
}
}
stack_pointer[0] = result;
@ -2736,7 +2728,6 @@
}
STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o);
if (CONVERSION_FAILED(args_o)) {
stack_pointer[-1] = kwnames;
_PyFrame_SetStackPointer(frame, stack_pointer);
_PyStackRef tmp = kwnames;
kwnames = PyStackRef_NULL;
@ -3070,7 +3061,6 @@
JUMP_TO_LABEL(error);
}
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
}
}
stack_pointer[0] = res;
@ -3422,7 +3412,6 @@
JUMP_TO_LABEL(error);
}
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
}
}
stack_pointer[0] = res;
@ -3544,7 +3533,6 @@
JUMP_TO_LABEL(error);
}
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
}
}
stack_pointer[0] = res;
@ -3646,7 +3634,6 @@
JUMP_TO_LABEL(error);
}
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
}
}
stack_pointer[0] = res;
@ -3759,7 +3746,6 @@
JUMP_TO_LABEL(error);
}
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
}
}
stack_pointer[0] = res;
@ -3879,7 +3865,6 @@
JUMP_TO_LABEL(error);
}
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
}
}
stack_pointer[0] = res;
@ -4154,7 +4139,6 @@
JUMP_TO_LABEL(error);
}
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
}
}
stack_pointer[0] = res;
@ -4227,7 +4211,6 @@
JUMP_TO_LABEL(error);
}
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
}
}
stack_pointer[0] = res;
@ -5310,7 +5293,6 @@
INSTRUCTION_STATS(EXIT_INIT_CHECK);
_PyStackRef should_be_none;
should_be_none = stack_pointer[-1];
assert(STACK_LEVEL() == 2);
if (!PyStackRef_IsNone(should_be_none)) {
_PyFrame_SetStackPointer(frame, stack_pointer);
PyErr_Format(PyExc_TypeError,
@ -5369,7 +5351,6 @@
else {
res = value;
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
}
stack_pointer[0] = res;
stack_pointer += 1;
@ -6130,8 +6111,9 @@
}
// _DO_CALL
{
self_or_null = maybe_self;
callable = func;
args = &stack_pointer[-oparg];
self_or_null = &stack_pointer[-1 - oparg];
callable = &stack_pointer[-2 - oparg];
PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]);
int total_args = oparg;
_PyStackRef *arguments = args;
@ -6246,7 +6228,6 @@
JUMP_TO_LABEL(error);
}
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
}
}
stack_pointer[0] = res;
@ -6307,7 +6288,6 @@
stack_pointer = _PyFrame_GetStackPointer(frame);
tuple = PyStackRef_FromPyObjectSteal(tuple_o);
stack_pointer += 2;
assert(WITHIN_STACK_BOUNDS());
}
}
// _DO_CALL_FUNCTION_EX
@ -6435,7 +6415,6 @@
JUMP_TO_LABEL(error);
}
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
}
}
stack_pointer[0] = result;
@ -6753,14 +6732,16 @@
frame->instr_ptr = next_instr;
next_instr += 2;
INSTRUCTION_STATS(INSTRUMENTED_FOR_ITER);
_PyStackRef iter;
_PyStackRef next;
/* Skip 1 cache entry */
_PyStackRef iter_stackref = TOP();
PyObject *iter = PyStackRef_AsPyObjectBorrow(iter_stackref);
iter = stack_pointer[-1];
PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter);
_PyFrame_SetStackPointer(frame, stack_pointer);
PyObject *next = (*Py_TYPE(iter)->tp_iternext)(iter);
PyObject *next_o = (*Py_TYPE(iter_o)->tp_iternext)(iter_o);
stack_pointer = _PyFrame_GetStackPointer(frame);
if (next != NULL) {
PUSH(PyStackRef_FromPyObjectSteal(next));
if (next_o != NULL) {
next = PyStackRef_FromPyObjectSteal(next_o);
INSTRUMENTED_JUMP(this_instr, next_instr, PY_MONITORING_EVENT_BRANCH_LEFT);
}
else {
@ -6779,7 +6760,11 @@
assert(next_instr[oparg].op.code == END_FOR ||
next_instr[oparg].op.code == INSTRUMENTED_END_FOR);
JUMPBY(oparg + 1);
DISPATCH();
}
stack_pointer[0] = next;
stack_pointer += 1;
assert(WITHIN_STACK_BOUNDS());
DISPATCH();
}
@ -7063,14 +7048,17 @@
frame->instr_ptr = next_instr;
next_instr += 2;
INSTRUCTION_STATS(INSTRUMENTED_POP_JUMP_IF_FALSE);
_PyStackRef cond;
/* Skip 1 cache entry */
_PyStackRef cond = POP();
cond = stack_pointer[-1];
assert(PyStackRef_BoolCheck(cond));
int jump = PyStackRef_IsFalse(cond);
RECORD_BRANCH_TAKEN(this_instr[1].cache, jump);
if (jump) {
INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT);
}
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
DISPATCH();
}
@ -7084,18 +7072,24 @@
frame->instr_ptr = next_instr;
next_instr += 2;
INSTRUCTION_STATS(INSTRUMENTED_POP_JUMP_IF_NONE);
_PyStackRef value;
/* Skip 1 cache entry */
_PyStackRef value_stackref = POP();
int jump = PyStackRef_IsNone(value_stackref);
value = stack_pointer[-1];
int jump = PyStackRef_IsNone(value);
RECORD_BRANCH_TAKEN(this_instr[1].cache, jump);
if (jump) {
INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT);
}
else {
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
_PyFrame_SetStackPointer(frame, stack_pointer);
PyStackRef_CLOSE(value_stackref);
PyStackRef_CLOSE(value);
stack_pointer = _PyFrame_GetStackPointer(frame);
stack_pointer += 1;
}
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
DISPATCH();
}
@ -7109,16 +7103,22 @@
frame->instr_ptr = next_instr;
next_instr += 2;
INSTRUCTION_STATS(INSTRUMENTED_POP_JUMP_IF_NOT_NONE);
_PyStackRef value;
/* Skip 1 cache entry */
_PyStackRef value_stackref = POP();
int jump = !PyStackRef_IsNone(value_stackref);
value = stack_pointer[-1];
int jump = !PyStackRef_IsNone(value);
RECORD_BRANCH_TAKEN(this_instr[1].cache, jump);
if (jump) {
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
_PyFrame_SetStackPointer(frame, stack_pointer);
PyStackRef_CLOSE(value_stackref);
PyStackRef_CLOSE(value);
stack_pointer = _PyFrame_GetStackPointer(frame);
INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT);
}
else {
stack_pointer += -1;
}
DISPATCH();
}
@ -7132,14 +7132,17 @@
frame->instr_ptr = next_instr;
next_instr += 2;
INSTRUCTION_STATS(INSTRUMENTED_POP_JUMP_IF_TRUE);
_PyStackRef cond;
/* Skip 1 cache entry */
_PyStackRef cond = POP();
cond = stack_pointer[-1];
assert(PyStackRef_BoolCheck(cond));
int jump = PyStackRef_IsTrue(cond);
RECORD_BRANCH_TAKEN(this_instr[1].cache, jump);
if (jump) {
INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT);
}
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
DISPATCH();
}
@ -7254,7 +7257,7 @@
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
_PyFrame_SetStackPointer(frame, stack_pointer);
assert(EMPTY());
assert(STACK_LEVEL() == 0);
_Py_LeaveRecursiveCallPy(tstate);
_PyInterpreterFrame *dying = frame;
frame = tstate->current_frame = dying->previous;
@ -7353,9 +7356,11 @@
tstate->current_frame = frame->previous;
assert(!_PyErr_Occurred(tstate));
PyObject *result = PyStackRef_AsPyObjectSteal(retval);
LLTRACE_RESUME_FRAME();
return result;
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
return result;
DISPATCH();
}
TARGET(IS_OP) {
@ -7684,7 +7689,6 @@
}
self_or_null[0] = PyStackRef_NULL;
stack_pointer += 1;
assert(WITHIN_STACK_BOUNDS());
}
}
else {
@ -7700,7 +7704,6 @@
JUMP_TO_LABEL(error);
}
stack_pointer += 1;
assert(WITHIN_STACK_BOUNDS());
}
attr = PyStackRef_FromPyObjectSteal(attr_o);
}
@ -7886,8 +7889,9 @@
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 1);
_PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(
tstate, PyStackRef_FromPyObjectNew(f), 2, frame);
STACK_SHRINK(1);
new_frame->localsplus[0] = owner;
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
new_frame->localsplus[1] = PyStackRef_FromPyObjectNew(name);
frame->return_offset = 10 ;
DISPATCH_INLINED(new_frame);
@ -9551,7 +9555,6 @@
stack_pointer = _PyFrame_GetStackPointer(frame);
self_or_null = PyStackRef_NULL;
stack_pointer += 1;
assert(WITHIN_STACK_BOUNDS());
}
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
@ -10243,7 +10246,7 @@
if (gen == NULL) {
JUMP_TO_LABEL(error);
}
assert(EMPTY());
assert(STACK_LEVEL() == 0);
_PyFrame_SetStackPointer(frame, stack_pointer);
_PyInterpreterFrame *gen_frame = &gen->gi_iframe;
frame->instr_ptr++;
@ -10281,7 +10284,7 @@
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
_PyFrame_SetStackPointer(frame, stack_pointer);
assert(EMPTY());
assert(STACK_LEVEL() == 0);
_Py_LeaveRecursiveCallPy(tstate);
_PyInterpreterFrame *dying = frame;
frame = tstate->current_frame = dying->previous;
@ -10339,8 +10342,9 @@
{
PyGenObject *gen = (PyGenObject *)receiver_o;
_PyInterpreterFrame *gen_frame = &gen->gi_iframe;
STACK_SHRINK(1);
_PyFrame_StackPush(gen_frame, PyStackRef_MakeHeapSafe(v));
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
gen->gi_frame_state = FRAME_EXECUTING;
gen->gi_exc_state.previous_item = tstate->exc_info;
tstate->exc_info = &gen->gi_exc_state;
@ -11095,21 +11099,18 @@
Py_DECREF(slice);
stack_pointer = _PyFrame_GetStackPointer(frame);
stack_pointer += 2;
assert(WITHIN_STACK_BOUNDS());
}
stack_pointer += -2;
assert(WITHIN_STACK_BOUNDS());
_PyFrame_SetStackPointer(frame, stack_pointer);
_PyStackRef tmp = container;
container = PyStackRef_NULL;
stack_pointer[-1] = container;
stack_pointer[-3] = container;
PyStackRef_CLOSE(tmp);
tmp = v;
v = PyStackRef_NULL;
stack_pointer[-2] = v;
stack_pointer[-4] = v;
PyStackRef_CLOSE(tmp);
stack_pointer = _PyFrame_GetStackPointer(frame);
stack_pointer += -2;
stack_pointer += -4;
assert(WITHIN_STACK_BOUNDS());
if (err) {
JUMP_TO_LABEL(error);
@ -11465,7 +11466,6 @@
stack_pointer = _PyFrame_GetStackPointer(frame);
res = PyStackRef_True;
stack_pointer += 1;
assert(WITHIN_STACK_BOUNDS());
}
stack_pointer[-1] = res;
DISPATCH();
@ -11573,7 +11573,6 @@
stack_pointer = _PyFrame_GetStackPointer(frame);
res = PyStackRef_True;
stack_pointer += 1;
assert(WITHIN_STACK_BOUNDS());
}
}
stack_pointer[-1] = res;
@ -11861,7 +11860,6 @@
_PyFrame_SetStackPointer(frame, stack_pointer);
PyStackRef_CLOSE(seq);
stack_pointer = _PyFrame_GetStackPointer(frame);
stack_pointer[-1] = val0;
DISPATCH();
}
@ -11984,25 +11982,29 @@ JUMP_TO_LABEL(error);
LABEL(pop_4_error)
{
STACK_SHRINK(4);
stack_pointer -= 4;
assert(WITHIN_STACK_BOUNDS());
JUMP_TO_LABEL(error);
}
LABEL(pop_3_error)
{
STACK_SHRINK(3);
stack_pointer -= 3;
assert(WITHIN_STACK_BOUNDS());
JUMP_TO_LABEL(error);
}
LABEL(pop_2_error)
{
STACK_SHRINK(2);
stack_pointer -= 2;
assert(WITHIN_STACK_BOUNDS());
JUMP_TO_LABEL(error);
}
LABEL(pop_1_error)
{
STACK_SHRINK(1);
stack_pointer -= 1;
assert(WITHIN_STACK_BOUNDS());
JUMP_TO_LABEL(error);
}

View file

@ -310,7 +310,6 @@
else {
res = sym_new_type(ctx, &PyLong_Type);
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
}
stack_pointer[-1] = res;
break;
@ -339,7 +338,6 @@
else {
res = sym_new_type(ctx, &PyLong_Type);
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
}
stack_pointer[-1] = res;
break;
@ -368,7 +366,6 @@
else {
res = sym_new_type(ctx, &PyLong_Type);
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
}
stack_pointer[-1] = res;
break;
@ -418,7 +415,6 @@
else {
res = sym_new_type(ctx, &PyFloat_Type);
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
}
stack_pointer[-1] = res;
break;
@ -448,7 +444,6 @@
else {
res = sym_new_type(ctx, &PyFloat_Type);
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
}
stack_pointer[-1] = res;
break;
@ -478,7 +473,6 @@
else {
res = sym_new_type(ctx, &PyFloat_Type);
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
}
stack_pointer[-1] = res;
break;
@ -506,7 +500,6 @@
else {
res = sym_new_type(ctx, &PyUnicode_Type);
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
}
stack_pointer[-1] = res;
break;
@ -1257,7 +1250,6 @@
else {
res = sym_new_type(ctx, &PyBool_Type);
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
}
stack_pointer[-1] = res;
break;
@ -2031,7 +2023,6 @@
if (co == NULL) {
ctx->done = true;
}
stack_pointer[-1] = res;
break;
}

View file

@ -71,6 +71,9 @@ _Py_stackref_close(_PyStackRef ref, const char *filename, int linenumber)
}
PyObject *obj;
if (ref.index <= LAST_PREDEFINED_STACKREF_INDEX) {
if (ref.index == 0) {
_Py_FatalErrorFormat(__func__, "Passing NULL to PyStackRef_CLOSE at %s:%d\n", filename, linenumber);
}
// Pre-allocated reference to None, False or True -- Do not clear
TableEntry *entry = _Py_hashtable_get(interp->open_stackrefs_table, (void *)ref.index);
obj = entry->obj;

View file

@ -13,9 +13,8 @@ from typing import Callable, TextIO, Iterator, Iterable
from lexer import Token
from stack import Storage, StackError
from parser import Stmt, SimpleStmt, BlockStmt, IfStmt, ForStmt, WhileStmt, MacroIfStmt
# Set this to true for voluminous output showing state of stack and locals
PRINT_STACKS = False
from stack import PRINT_STACKS
DEBUG = False
class TokenIterator:
@ -161,7 +160,7 @@ class Emitter:
self.emit(") {\n")
next(tkn_iter) # Semi colon
assert inst is not None
assert inst.family is not None, inst
assert inst.family is not None
family_name = inst.family.name
self.emit(f"UPDATE_MISS_STATS({family_name});\n")
self.emit(f"assert(_PyOpcode_Deopt[opcode] == ({family_name}));\n")
@ -242,6 +241,7 @@ class Emitter:
next(tkn_iter)
next(tkn_iter)
next(tkn_iter)
self._print_storage("DECREF_INPUTS", storage)
try:
storage.close_inputs(self.out)
except StackError as ex:
@ -249,7 +249,6 @@ class Emitter:
except Exception as ex:
ex.args = (ex.args[0] + str(tkn),)
raise
self._print_storage(storage)
return True
def kill_inputs(
@ -372,7 +371,7 @@ class Emitter:
next(tkn_iter)
storage.clear_inputs("when syncing stack")
storage.flush(self.out)
self._print_storage(storage)
storage.stack.clear(self.out)
return True
def stack_pointer(
@ -406,7 +405,6 @@ class Emitter:
def emit_save(self, storage: Storage) -> None:
storage.flush(self.out)
storage.save(self.out)
self._print_storage(storage)
def save_stack(
self,
@ -424,7 +422,6 @@ class Emitter:
def emit_reload(self, storage: Storage) -> None:
storage.reload(self.out)
self._print_storage(storage)
def reload_stack(
self,
@ -453,9 +450,10 @@ class Emitter:
self.out.emit(f" {uop.instruction_size} ")
return True
def _print_storage(self, storage: Storage) -> None:
if PRINT_STACKS:
def _print_storage(self, reason:str, storage: Storage) -> None:
if DEBUG:
self.out.start_line()
self.emit(f"/* {reason} */\n")
self.emit(storage.as_comment())
self.out.start_line()
@ -514,7 +512,6 @@ class Emitter:
var.memory_offset = None
break
if tkn.text.startswith("DISPATCH"):
self._print_storage(storage)
reachable = False
self.out.emit(tkn)
else:
@ -536,6 +533,8 @@ class Emitter:
self.out.emit(stmt.condition)
branch = stmt.else_ is not None
reachable = True
if branch:
else_storage = storage.copy()
for s in stmt.body:
r, tkn, storage = self._emit_stmt(s, uop, storage, inst)
if tkn is not None:
@ -543,7 +542,6 @@ class Emitter:
if not r:
reachable = False
if branch:
else_storage = storage.copy()
assert stmt.else_ is not None
self.out.emit(stmt.else_)
assert stmt.else_body is not None
@ -553,7 +551,8 @@ class Emitter:
self.out.emit(tkn)
if not r:
reachable = False
storage.merge(else_storage, self.out)
else_storage.merge(storage, self.out) # type: ignore[possibly-undefined]
storage = else_storage
self.out.emit(stmt.endif)
return reachable, None, storage
@ -596,7 +595,6 @@ class Emitter:
reachable = True
return reachable, rbrace, storage
except StackError as ex:
self._print_storage(if_storage)
assert rbrace is not None
raise analysis_error(ex.args[0], rbrace) from None
@ -659,20 +657,18 @@ class Emitter:
storage: Storage,
inst: Instruction | None,
emit_braces: bool = True
) -> Storage:
) -> tuple[bool, Storage]:
self.out.start_line()
reachable, tkn, storage = self.emit_BlockStmt(code.body, code, storage, inst, emit_braces)
assert tkn is not None
try:
if reachable:
self._print_storage(storage)
storage.push_outputs()
self._print_storage(storage)
if emit_braces:
self.out.emit(tkn)
except StackError as ex:
raise analysis_error(ex.args[0], tkn) from None
return storage
return reachable, storage
def emit(self, txt: str | Token) -> None:
self.out.emit(txt)

View file

@ -145,7 +145,7 @@ def write_uop(
# No reference management of inputs needed.
for var in storage.inputs: # type: ignore[possibly-undefined]
var.in_local = False
storage = emitter.emit_tokens(override, storage, None, False)
_, storage = emitter.emit_tokens(override, storage, None, False)
out.start_line()
storage.flush(out)
else:

View file

@ -6,6 +6,8 @@ from typing import Iterator
UNUSED = {"unused"}
# Set this to true for voluminous output showing state of stack and locals
PRINT_STACKS = False
def maybe_parenthesize(sym: str) -> str:
"""Add parentheses around a string if it contains an operator
@ -131,9 +133,6 @@ class PointerOffset:
return None
return self.numeric
def __bool__(self) -> bool:
return self.numeric != 0 or bool(self.positive) or bool(self.negative)
def __str__(self) -> str:
return self.to_c()
@ -151,7 +150,7 @@ class Local:
def compact_str(self) -> str:
mtag = "M" if self.memory_offset else ""
dtag = "D" if self.in_local else ""
dtag = "L" if self.in_local else ""
atag = "A" if self.is_array() else ""
return f"'{self.item.name}'{mtag}{dtag}{atag}"
@ -167,6 +166,11 @@ class Local:
def from_memory(defn: StackItem, offset: PointerOffset) -> "Local":
return Local(defn, offset, True)
@staticmethod
def register(name: str) -> "Local":
item = StackItem(name, None, "", False, True)
return Local(item, None, True)
def kill(self) -> None:
self.in_local = False
self.memory_offset = None
@ -230,20 +234,16 @@ class Stack:
raise StackError(f"Dropping live value '{var.name}'")
def pop(self, var: StackItem, out: CWriter) -> Local:
if self.variables:
top = self.variables[-1]
if var.is_array() != top.is_array() or top.size != var.size:
# Mismatch in variables
self.clear(out)
self.logical_sp = self.logical_sp.pop(var)
indirect = "&" if var.is_array() else ""
if self.variables:
popped = self.variables.pop()
if var.is_array() ^ popped.is_array():
raise StackError(
f"Array mismatch when popping '{popped.name}' from stack to assign to '{var.name}'. "
f"Expected {array_or_scalar(var)} got {array_or_scalar(popped)}"
)
if popped.size != var.size:
raise StackError(
f"Size mismatch when popping '{popped.name}' from stack to assign to '{var.name}'. "
f"Expected {var_size(var)} got {var_size(popped.item)}"
)
assert var.is_array() == popped.is_array() and popped.size == var.size
if not var.used:
return popped
if popped.name != var.name:
@ -255,27 +255,33 @@ class Stack:
if popped.memory_offset is None:
popped.memory_offset = self.logical_sp
assert popped.memory_offset == self.logical_sp, (popped, self.as_comment())
offset = popped.memory_offset.to_c()
offset = popped.memory_offset - self.physical_sp
if var.is_array():
defn = f"{var.name} = &stack_pointer[{offset}];\n"
defn = f"{var.name} = &stack_pointer[{offset.to_c()}];\n"
else:
defn = f"{var.name} = stack_pointer[{offset}];\n"
defn = f"{var.name} = stack_pointer[{offset.to_c()}];\n"
popped.in_local = True
else:
defn = rename
out.emit(defn)
return popped
self.base_offset = self.logical_sp
if var.name in UNUSED or not var.used:
return Local.unused(var, self.base_offset)
cast = f"({var.type})" if (not indirect and var.type) else ""
bits = ".bits" if cast and self.extract_bits else ""
offset = (self.base_offset - self.physical_sp).to_c()
assign = f"{var.name} = {cast}{indirect}stack_pointer[{offset}]{bits};\n"
c_offset = (self.base_offset - self.physical_sp).to_c()
assign = f"{var.name} = {cast}{indirect}stack_pointer[{c_offset}]{bits};\n"
out.emit(assign)
self._print(out)
return Local.from_memory(var, self.base_offset)
def clear(self, out: CWriter) -> None:
"Flush to memory and clear variables stack"
self.flush(out)
self.variables = []
self.base_offset = self.logical_sp
def push(self, var: Local) -> None:
assert(var not in self.variables)
self.variables.append(var)
@ -298,10 +304,11 @@ class Stack:
diff = self.logical_sp - self.physical_sp
out.start_line()
out.emit(f"stack_pointer += {diff.to_c()};\n")
out.emit("assert(WITHIN_STACK_BOUNDS());\n")
out.emit(f"assert(WITHIN_STACK_BOUNDS());\n")
self.physical_sp = self.logical_sp
self._print(out)
def flush(self, out: CWriter) -> None:
def save_variables(self, out: CWriter) -> None:
out.start_line()
var_offset = self.base_offset
for var in self.variables:
@ -310,10 +317,15 @@ class Stack:
not var.memory_offset and
not var.is_array()
):
self._print(out)
var.memory_offset = var_offset
stack_offset = var_offset - self.physical_sp
Stack._do_emit(out, var.item, stack_offset, self.cast_type, self.extract_bits)
self._print(out)
var_offset = var_offset.push(var.item)
def flush(self, out: CWriter) -> None:
self.save_variables(out)
self._save_physical_sp(out)
out.start_line()
@ -329,9 +341,13 @@ class Stack:
def as_comment(self) -> str:
variables = ", ".join([v.compact_str() for v in self.variables])
return (
f"/* Variables: {variables}. base: {self.base_offset.to_c()}. sp: {self.physical_sp.to_c()}. logical_sp: {self.logical_sp.to_c()} */"
f"/* Variables=[{variables}]; base={self.base_offset.to_c()}; sp={self.physical_sp.to_c()}; logical_sp={self.logical_sp.to_c()} */"
)
def _print(self, out: CWriter) -> None:
if PRINT_STACKS:
out.emit(self.as_comment() + "\n")
def copy(self) -> "Stack":
other = Stack(self.extract_bits, self.cast_type)
other.base_offset = self.base_offset
@ -358,7 +374,6 @@ class Stack:
diff = other.physical_sp - self.physical_sp
out.start_line()
out.emit(f"stack_pointer += {diff.to_c()};\n")
out.emit("assert(WITHIN_STACK_BOUNDS());\n")
self.physical_sp = other.physical_sp
def merge(self, other: "Stack", out: CWriter) -> None:
@ -621,10 +636,10 @@ class Storage:
def as_comment(self) -> str:
stack_comment = self.stack.as_comment()
next_line = "\n "
next_line = "\n "
inputs = ", ".join([var.compact_str() for var in self.inputs])
outputs = ", ".join([var.compact_str() for var in self.outputs])
return f"{stack_comment[:-2]}{next_line}inputs: {inputs}{next_line}outputs: {outputs}*/"
return f"{stack_comment[:-2]}{next_line}inputs: {inputs} outputs: {outputs}*/"
def close_inputs(self, out: CWriter) -> None:
@ -637,7 +652,7 @@ class Storage:
tmp_defined = True
out.emit(f"tmp = {name};\n")
out.emit(f"{name} = {overwrite};\n")
self.stack.flush(out)
self.stack.save_variables(out)
out.emit(f"{close}(tmp);\n")
else:
out.emit(f"{close}({name});\n")
@ -678,7 +693,6 @@ class Storage:
if output is not None:
raise StackError("Cannot call DECREF_INPUTS with more than one live output")
output = var
self.stack.flush(out)
if output is not None:
if output.is_array():
assert len(self.inputs) == 1
@ -691,6 +705,7 @@ class Storage:
return
if var_size(lowest.item) != var_size(output.item):
raise StackError("Cannot call DECREF_INPUTS with live output not matching first input size")
self.stack.flush(out)
lowest.in_local = True
close_variable(lowest, output.name)
assert lowest.memory_offset is not None

View file

@ -9,6 +9,8 @@ from analyzer import (
Analysis,
Instruction,
Uop,
Label,
CodeSection,
Part,
analyze_files,
Skip,
@ -22,9 +24,13 @@ from generators_common import (
write_header,
type_and_null,
Emitter,
TokenIterator,
always_true,
emit_to,
)
from cwriter import CWriter
from typing import TextIO
from lexer import Token
from stack import Local, Stack, StackError, get_stack_effect, Storage
DEFAULT_OUTPUT = ROOT / "Python/generated_cases.c.h"
@ -69,23 +75,23 @@ def write_uop(
stack: Stack,
inst: Instruction,
braces: bool,
) -> tuple[int, Stack]:
) -> tuple[bool, int, Stack]:
# out.emit(stack.as_comment() + "\n")
if isinstance(uop, Skip):
entries = "entries" if uop.size > 1 else "entry"
emitter.emit(f"/* Skip {uop.size} cache {entries} */\n")
return (offset + uop.size), stack
return True, (offset + uop.size), stack
if isinstance(uop, Flush):
emitter.emit(f"// flush\n")
stack.flush(emitter.out)
return offset, stack
return True, offset, stack
locals: dict[str, Local] = {}
emitter.out.start_line()
if braces:
emitter.out.emit(f"// {uop.name}\n")
emitter.emit("{\n")
stack._print(emitter.out)
storage = Storage.for_uop(stack, uop, emitter.out)
emitter._print_storage(storage)
for cache in uop.caches:
if cache.name != "unused":
@ -102,12 +108,12 @@ def write_uop(
emitter.emit(f"(void){cache.name};\n")
offset += cache.size
storage = emitter.emit_tokens(uop, storage, inst, False)
reachable, storage = emitter.emit_tokens(uop, storage, inst, False)
if braces:
emitter.out.start_line()
emitter.emit("}\n")
# emitter.emit(stack.as_comment() + "\n")
return offset, storage.stack
return reachable, offset, storage.stack
def uses_this(inst: Instruction) -> bool:
@ -204,6 +210,9 @@ def generate_tier1_labels(
emitter.emit_tokens(label, storage, None)
emitter.emit("\n\n")
def get_popped(inst: Instruction, analysis: Analysis) -> str:
stack = get_stack_effect(inst)
return (-stack.base_offset).to_c()
def generate_tier1_cases(
analysis: Analysis, outfile: TextIO, lines: bool
@ -214,6 +223,7 @@ def generate_tier1_cases(
for name, inst in sorted(analysis.instructions.items()):
out.emit("\n")
out.emit(f"TARGET({name}) {{\n")
popped = get_popped(inst, analysis)
# We need to ifdef it because this breaks platforms
# without computed gotos/tail calling.
out.emit(f"#if Py_TAIL_CALL_INTERP\n")
@ -251,11 +261,10 @@ def generate_tier1_cases(
for part in inst.parts:
# Only emit braces if more than one uop
insert_braces = len([p for p in inst.parts if isinstance(p, Uop)]) > 1
offset, stack = write_uop(part, emitter, offset, stack, inst, insert_braces)
reachable, offset, stack = write_uop(part, emitter, offset, stack, inst, insert_braces)
out.start_line()
stack.flush(out)
if not inst.parts[-1].properties.always_exits:
if reachable: # type: ignore[possibly-undefined]
stack.flush(out)
out.emit("DISPATCH();\n")
out.start_line()
out.emit("}")

View file

@ -154,7 +154,7 @@ def write_uop(uop: Uop, emitter: Emitter, stack: Stack) -> Stack:
cast = f"uint{cache.size*16}_t"
emitter.emit(f"{type}{cache.name} = ({cast})CURRENT_OPERAND{idx}();\n")
idx += 1
storage = emitter.emit_tokens(uop, storage, None, False)
_, storage = emitter.emit_tokens(uop, storage, None, False)
storage.flush(emitter.out)
except StackError as ex:
raise analysis_error(ex.args[0], uop.body.open) from None