case INSTRUMENTED_END_SEND:
return 2;
case INSTRUMENTED_FOR_ITER:
- return 0;
+ return 1;
case INSTRUMENTED_INSTRUCTION:
return 0;
case INSTRUMENTED_JUMP_BACKWARD:
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:
case INSTRUMENTED_END_SEND:
return 1;
case INSTRUMENTED_FOR_ITER:
- return 0;
+ return 2;
case INSTRUMENTED_INSTRUCTION:
return 0;
case INSTRUMENTED_JUMP_BACKWARD:
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
// 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;
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();
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;
}
_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;
{
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;
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);
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 {
next_instr[oparg].op.code == INSTRUMENTED_END_FOR);
/* Skip END_FOR */
JUMPBY(oparg + 1);
+ DISPATCH();
}
}
_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'",
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++;
}
next_instr = frame->instr_ptr;
if (next_instr != this_instr) {
+ SYNC_SP();
DISPATCH();
}
}
_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, ( -- )) {
}
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;
}
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);
}
dump_item(*ptr);
}
printf("]\n");
- printf(" stack=[");
- for (_PyStackRef *ptr = stack_base; ptr < stack_pointer; ptr++) {
- if (ptr != stack_base) {
- printf(", ");
+ 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);
}
- dump_item(*ptr);
+ printf("]\n");
}
- printf("]\n");
fflush(stdout);
PyErr_SetRaisedException(exc);
_PyFrame_GetStackPointer(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);
}
* 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;
_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
#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()))
stack_pointer = _PyFrame_GetStackPointer(frame);
res = PyStackRef_True;
stack_pointer += 1;
- assert(WITHIN_STACK_BOUNDS());
}
stack_pointer[-1] = res;
break;
stack_pointer = _PyFrame_GetStackPointer(frame);
res = PyStackRef_True;
stack_pointer += 1;
- assert(WITHIN_STACK_BOUNDS());
}
stack_pointer[-1] = res;
break;
Py_DECREF(slice);
stack_pointer = _PyFrame_GetStackPointer(frame);
stack_pointer += 2;
- assert(WITHIN_STACK_BOUNDS());
}
stack_pointer += -3;
assert(WITHIN_STACK_BOUNDS());
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();
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;
_PyFrame_SetStackPointer(frame, stack_pointer);
PyStackRef_CLOSE(seq);
stack_pointer = _PyFrame_GetStackPointer(frame);
- stack_pointer[-1] = val0;
break;
}
stack_pointer = _PyFrame_GetStackPointer(frame);
self_or_null = PyStackRef_NULL;
stack_pointer += 1;
- assert(WITHIN_STACK_BOUNDS());
}
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
}
self_or_null[0] = PyStackRef_NULL;
stack_pointer += 1;
- assert(WITHIN_STACK_BOUNDS());
}
}
else {
JUMP_TO_ERROR();
}
stack_pointer += 1;
- assert(WITHIN_STACK_BOUNDS());
}
attr = PyStackRef_FromPyObjectSteal(attr_o);
stack_pointer[-1] = attr;
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,
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;
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++;
else {
res = value;
stack_pointer += -1;
- assert(WITHIN_STACK_BOUNDS());
}
stack_pointer[0] = res;
stack_pointer += 1;
Py_DECREF(slice);
stack_pointer = _PyFrame_GetStackPointer(frame);
stack_pointer += 2;
- assert(WITHIN_STACK_BOUNDS());
}
stack_pointer += -3;
assert(WITHIN_STACK_BOUNDS());
JUMP_TO_LABEL(error);
}
stack_pointer += -1;
- assert(WITHIN_STACK_BOUNDS());
}
}
stack_pointer[0] = res;
JUMP_TO_LABEL(error);
}
stack_pointer += -1;
- assert(WITHIN_STACK_BOUNDS());
}
}
stack_pointer[0] = res;
JUMP_TO_LABEL(error);
}
stack_pointer += -1;
- assert(WITHIN_STACK_BOUNDS());
}
}
stack_pointer[0] = res;
JUMP_TO_LABEL(error);
}
stack_pointer += -1;
- assert(WITHIN_STACK_BOUNDS());
}
}
stack_pointer[0] = res;
JUMP_TO_LABEL(error);
}
stack_pointer += -1;
- assert(WITHIN_STACK_BOUNDS());
}
}
stack_pointer[0] = res;
stack_pointer = _PyFrame_GetStackPointer(frame);
tuple = PyStackRef_FromPyObjectSteal(tuple_o);
stack_pointer += 2;
- assert(WITHIN_STACK_BOUNDS());
}
}
// _DO_CALL_FUNCTION_EX
JUMP_TO_LABEL(error);
}
stack_pointer += -1;
- assert(WITHIN_STACK_BOUNDS());
}
}
stack_pointer[0] = result;
}
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;
JUMP_TO_LABEL(error);
}
stack_pointer += -1;
- assert(WITHIN_STACK_BOUNDS());
}
}
stack_pointer[0] = res;
JUMP_TO_LABEL(error);
}
stack_pointer += -1;
- assert(WITHIN_STACK_BOUNDS());
}
}
stack_pointer[0] = res;
JUMP_TO_LABEL(error);
}
stack_pointer += -1;
- assert(WITHIN_STACK_BOUNDS());
}
}
stack_pointer[0] = res;
JUMP_TO_LABEL(error);
}
stack_pointer += -1;
- assert(WITHIN_STACK_BOUNDS());
}
}
stack_pointer[0] = res;
JUMP_TO_LABEL(error);
}
stack_pointer += -1;
- assert(WITHIN_STACK_BOUNDS());
}
}
stack_pointer[0] = res;
JUMP_TO_LABEL(error);
}
stack_pointer += -1;
- assert(WITHIN_STACK_BOUNDS());
}
}
stack_pointer[0] = res;
JUMP_TO_LABEL(error);
}
stack_pointer += -1;
- assert(WITHIN_STACK_BOUNDS());
}
}
stack_pointer[0] = res;
JUMP_TO_LABEL(error);
}
stack_pointer += -1;
- assert(WITHIN_STACK_BOUNDS());
}
}
stack_pointer[0] = res;
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,
else {
res = value;
stack_pointer += -1;
- assert(WITHIN_STACK_BOUNDS());
}
stack_pointer[0] = res;
stack_pointer += 1;
}
// _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;
JUMP_TO_LABEL(error);
}
stack_pointer += -1;
- assert(WITHIN_STACK_BOUNDS());
}
}
stack_pointer[0] = res;
stack_pointer = _PyFrame_GetStackPointer(frame);
tuple = PyStackRef_FromPyObjectSteal(tuple_o);
stack_pointer += 2;
- assert(WITHIN_STACK_BOUNDS());
}
}
// _DO_CALL_FUNCTION_EX
JUMP_TO_LABEL(error);
}
stack_pointer += -1;
- assert(WITHIN_STACK_BOUNDS());
}
}
stack_pointer[0] = result;
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 {
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();
}
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();
}
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();
}
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();
}
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();
}
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;
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) {
}
self_or_null[0] = PyStackRef_NULL;
stack_pointer += 1;
- assert(WITHIN_STACK_BOUNDS());
}
}
else {
JUMP_TO_LABEL(error);
}
stack_pointer += 1;
- assert(WITHIN_STACK_BOUNDS());
}
attr = PyStackRef_FromPyObjectSteal(attr_o);
}
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);
stack_pointer = _PyFrame_GetStackPointer(frame);
self_or_null = PyStackRef_NULL;
stack_pointer += 1;
- assert(WITHIN_STACK_BOUNDS());
}
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
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++;
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;
{
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;
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);
stack_pointer = _PyFrame_GetStackPointer(frame);
res = PyStackRef_True;
stack_pointer += 1;
- assert(WITHIN_STACK_BOUNDS());
}
stack_pointer[-1] = res;
DISPATCH();
stack_pointer = _PyFrame_GetStackPointer(frame);
res = PyStackRef_True;
stack_pointer += 1;
- assert(WITHIN_STACK_BOUNDS());
}
}
stack_pointer[-1] = res;
_PyFrame_SetStackPointer(frame, stack_pointer);
PyStackRef_CLOSE(seq);
stack_pointer = _PyFrame_GetStackPointer(frame);
- stack_pointer[-1] = val0;
DISPATCH();
}
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);
}
else {
res = sym_new_type(ctx, &PyLong_Type);
stack_pointer += -1;
- assert(WITHIN_STACK_BOUNDS());
}
stack_pointer[-1] = res;
break;
else {
res = sym_new_type(ctx, &PyLong_Type);
stack_pointer += -1;
- assert(WITHIN_STACK_BOUNDS());
}
stack_pointer[-1] = res;
break;
else {
res = sym_new_type(ctx, &PyLong_Type);
stack_pointer += -1;
- assert(WITHIN_STACK_BOUNDS());
}
stack_pointer[-1] = res;
break;
else {
res = sym_new_type(ctx, &PyFloat_Type);
stack_pointer += -1;
- assert(WITHIN_STACK_BOUNDS());
}
stack_pointer[-1] = res;
break;
else {
res = sym_new_type(ctx, &PyFloat_Type);
stack_pointer += -1;
- assert(WITHIN_STACK_BOUNDS());
}
stack_pointer[-1] = res;
break;
else {
res = sym_new_type(ctx, &PyFloat_Type);
stack_pointer += -1;
- assert(WITHIN_STACK_BOUNDS());
}
stack_pointer[-1] = res;
break;
else {
res = sym_new_type(ctx, &PyUnicode_Type);
stack_pointer += -1;
- assert(WITHIN_STACK_BOUNDS());
}
stack_pointer[-1] = res;
break;
else {
res = sym_new_type(ctx, &PyBool_Type);
stack_pointer += -1;
- assert(WITHIN_STACK_BOUNDS());
}
stack_pointer[-1] = res;
break;
if (co == NULL) {
ctx->done = true;
}
- stack_pointer[-1] = res;
break;
}
}
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;
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:
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")
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:
except Exception as ex:
ex.args = (ex.args[0] + str(tkn),)
raise
- self._print_storage(storage)
return True
def kill_inputs(
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(
def emit_save(self, storage: Storage) -> None:
storage.flush(self.out)
storage.save(self.out)
- self._print_storage(storage)
def save_stack(
self,
def emit_reload(self, storage: Storage) -> None:
storage.reload(self.out)
- self._print_storage(storage)
def reload_stack(
self,
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()
var.memory_offset = None
break
if tkn.text.startswith("DISPATCH"):
- self._print_storage(storage)
reachable = False
self.out.emit(tkn)
else:
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:
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
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
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
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)
# 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:
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
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()
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}"
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
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:
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)
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:
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()
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
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:
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:
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")
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
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
Analysis,
Instruction,
Uop,
+ Label,
+ CodeSection,
Part,
analyze_files,
Skip,
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"
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":
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:
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
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")
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("}")
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