if (cond) {
JUMP_TO_LABEL(label);
}
- // Comment is ok
DISPATCH();
}
"""
LABEL(somewhere)
{
-
}
"""
self.run_cases_test(input, output)
}
// THIRD
{
- // Mark j and k as used
if (cond) {
JUMP_TO_LABEL(pop_2_error);
}
output = """
LABEL(other_label)
{
-
}
LABEL(other_label2)
{
-
}
LABEL(my_label)
{
- // Comment
_PyFrame_SetStackPointer(frame, stack_pointer);
do_thing();
stack_pointer = _PyFrame_GetStackPointer(frame);
output = """
LABEL(one)
{
- /* STACK SPILLED */
stack_pointer = _PyFrame_GetStackPointer(frame);
JUMP_TO_LABEL(two);
}
output = """
LABEL(my_label_1)
{
- // Comment
_PyFrame_SetStackPointer(frame, stack_pointer);
do_thing1();
stack_pointer = _PyFrame_GetStackPointer(frame);
LABEL(my_label_2)
{
- // Comment
_PyFrame_SetStackPointer(frame, stack_pointer);
do_thing2();
stack_pointer = _PyFrame_GetStackPointer(frame);
op(_CHECK_PERIODIC_IF_NOT_YIELD_FROM, (--)) {
if ((oparg & RESUME_OPARG_LOCATION_MASK) < RESUME_AFTER_YIELD_FROM) {
_Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY();
- QSBR_QUIESCENT_STATE(tstate); \
+ QSBR_QUIESCENT_STATE(tstate);
if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) {
int err = _Py_HandlePending(tstate);
ERROR_IF(err != 0, error);
PyObject *attr_o = FT_ATOMIC_LOAD_PTR_ACQUIRE(*value_ptr);
DEOPT_IF(attr_o == NULL);
#ifdef Py_GIL_DISABLED
- if (!_Py_TryIncrefCompareStackRef(value_ptr, attr_o, &attr)) {
+ int increfed = _Py_TryIncrefCompareStackRef(value_ptr, attr_o, &attr);
+ if (!increfed) {
DEOPT_IF(true);
}
#else
}
STAT_INC(LOAD_ATTR, hit);
#ifdef Py_GIL_DISABLED
- if (!_Py_TryIncrefCompareStackRef(&ep->me_value, attr_o, &attr)) {
+ int increfed = _Py_TryIncrefCompareStackRef(&ep->me_value, attr_o, &attr);
+ if (!increfed) {
DEOPT_IF(true);
}
#else
oparg = CURRENT_OPARG();
if ((oparg & RESUME_OPARG_LOCATION_MASK) < RESUME_AFTER_YIELD_FROM) {
_Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY();
- QSBR_QUIESCENT_STATE(tstate); \
+ QSBR_QUIESCENT_STATE(tstate);
if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) {
_PyFrame_SetStackPointer(frame, stack_pointer);
int err = _Py_HandlePending(tstate);
case _END_FOR: {
_PyStackRef value;
value = stack_pointer[-1];
- /* Don't update instr_ptr, so that POP_ITER sees
- * the FOR_ITER as the previous instruction.
- * This has the benign side effect that if value is
- * finalized it will see the location as the FOR_ITER's.
- */
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
_PyFrame_SetStackPointer(frame, stack_pointer);
_PyStackRef value;
_PyStackRef res;
value = stack_pointer[-1];
- // This one is a bit weird, because we expect *some* failures:
if (!PyStackRef_IsNone(value)) {
UOP_STAT_INC(uopcode, miss);
JUMP_TO_JUMP_TARGET();
JUMP_TO_JUMP_TARGET();
}
STAT_INC(BINARY_OP, hit);
- /* Handle `left = left + right` or `left += right` for str.
- *
- * When possible, extend `left` in place rather than
- * allocating a new PyUnicodeObject. This attempts to avoid
- * quadratic behavior when one neglects to use str.join().
- *
- * If `left` has only two references remaining (one from
- * the stack, one in the locals), DECREFing `left` leaves
- * only the locals reference, so PyUnicode_Append knows
- * that the string is safe to mutate.
- */
assert(Py_REFCNT(left_o) >= 2 || !PyStackRef_IsHeapSafe(left));
PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc);
PyObject *temp = PyStackRef_AsPyObjectSteal(*target_local);
JUMP_TO_ERROR();
}
#if TIER_ONE
- // The STORE_FAST is already done. This is done here in tier one,
- // and during trace projection in tier two:
+
assert(next_instr->op.code == STORE_FAST);
SKIP_OVER(1);
#endif
PyStackRef_AsPyObjectSteal(stop));
stack_pointer = _PyFrame_GetStackPointer(frame);
PyObject *res_o;
- // Can't use ERROR_IF() here, because we haven't
- // DECREF'ed container yet, and we still own slice.
if (slice == NULL) {
res_o = NULL;
}
UOP_STAT_INC(uopcode, miss);
JUMP_TO_JUMP_TARGET();
}
- // Deopt unless 0 <= sub < PyList_Size(list)
if (!_PyLong_IsNonNegativeCompact((PyLongObject *)sub)) {
UOP_STAT_INC(uopcode, miss);
JUMP_TO_JUMP_TARGET();
UOP_STAT_INC(uopcode, miss);
JUMP_TO_JUMP_TARGET();
}
- // Specialize for reading an ASCII character from any string:
Py_UCS4 c = PyUnicode_READ_CHAR(str, index);
if (Py_ARRAY_LENGTH(_Py_SINGLETON(strings).ascii) <= c) {
UOP_STAT_INC(uopcode, miss);
UOP_STAT_INC(uopcode, miss);
JUMP_TO_JUMP_TARGET();
}
- // Deopt unless 0 <= sub < PyTuple_Size(list)
if (!_PyLong_IsNonNegativeCompact((PyLongObject *)sub)) {
UOP_STAT_INC(uopcode, miss);
JUMP_TO_JUMP_TARGET();
if (rc <= 0) {
JUMP_TO_ERROR();
}
- // not found or error
res = PyStackRef_FromPyObjectSteal(res_o);
stack_pointer[0] = res;
stack_pointer += 1;
sub = stack_pointer[-1];
container = stack_pointer[-2];
v = stack_pointer[-3];
- /* container[sub] = v */
_PyFrame_SetStackPointer(frame, stack_pointer);
int err = PyObject_SetItem(PyStackRef_AsPyObjectBorrow(container), PyStackRef_AsPyObjectBorrow(sub), PyStackRef_AsPyObjectBorrow(v));
_PyStackRef tmp = sub;
UOP_STAT_INC(uopcode, miss);
JUMP_TO_JUMP_TARGET();
}
- // Ensure nonnegative, zero-or-one-digit ints.
if (!_PyLong_IsNonNegativeCompact((PyLongObject *)sub)) {
UOP_STAT_INC(uopcode, miss);
JUMP_TO_JUMP_TARGET();
UOP_STAT_INC(uopcode, miss);
JUMP_TO_JUMP_TARGET();
}
- // Ensure index < len(list)
if (index >= PyList_GET_SIZE(list)) {
UNLOCK_OBJECT(list);
if (true) {
FT_ATOMIC_STORE_PTR_RELEASE(_PyList_ITEMS(list)[index],
PyStackRef_AsPyObjectSteal(value));
assert(old_value != NULL);
- UNLOCK_OBJECT(list); // unlock before decrefs!
+ UNLOCK_OBJECT(list);
PyStackRef_CLOSE_SPECIALIZED(sub_st, _PyLong_ExactDealloc);
stack_pointer += -3;
assert(WITHIN_STACK_BOUNDS());
_PyStackRef container;
sub = stack_pointer[-1];
container = stack_pointer[-2];
- /* del container[sub] */
_PyFrame_SetStackPointer(frame, stack_pointer);
int err = PyObject_DelItem(PyStackRef_AsPyObjectBorrow(container),
PyStackRef_AsPyObjectBorrow(sub));
_PyFrame_SetStackPointer(frame, stack_pointer);
assert(EMPTY());
_Py_LeaveRecursiveCallPy(tstate);
- // GH-99729: We need to unlink the frame *before* clearing it:
_PyInterpreterFrame *dying = frame;
frame = tstate->current_frame = dying->previous;
_PyEval_FrameClearAndPop(tstate, dying);
_PyStackRef value;
oparg = CURRENT_OPARG();
retval = stack_pointer[-1];
- // NOTE: It's important that YIELD_VALUE never raises an exception!
- // The compiler treats any exception raised here as a failed close()
- // or throw() call.
assert(frame->owner != FRAME_OWNED_BY_INTERPRETER);
frame->instr_ptr++;
PyGenObject *gen = _PyGen_GetGeneratorFromFrame(frame);
_PyInterpreterFrame *gen_frame = frame;
frame = tstate->current_frame = frame->previous;
gen_frame->previous = NULL;
- /* We don't know which of these is relevant here, so keep them equal */
assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER);
#if TIER_ONE
assert(frame->instr_ptr->op.code == INSTRUMENTED_LINE ||
case _LOAD_COMMON_CONSTANT: {
_PyStackRef value;
oparg = CURRENT_OPARG();
- // Keep in sync with _common_constants in opcode.py
assert(oparg < NUM_COMMON_CONSTANTS);
value = PyStackRef_FromPyObjectNew(tstate->interp->common_consts[oparg]);
stack_pointer[0] = value;
_PyFrame_SetStackPointer(frame, stack_pointer);
err = PyObject_DelItem(ns, name);
stack_pointer = _PyFrame_GetStackPointer(frame);
- // Can't use ERROR_IF here.
if (err != 0) {
_PyFrame_SetStackPointer(frame, stack_pointer);
_PyEval_FormatExcCheckArg(tstate, PyExc_NameError,
_PyFrame_SetStackPointer(frame, stack_pointer);
int err = PyDict_Pop(GLOBALS(), name, NULL);
stack_pointer = _PyFrame_GetStackPointer(frame);
- // Can't use ERROR_IF here.
if (err < 0) {
JUMP_TO_ERROR();
}
case _MAKE_CELL: {
oparg = CURRENT_OPARG();
- // "initial" is probably NULL but not if it's an arg (or set
- // via the f_locals proxy before MAKE_CELL has run).
PyObject *initial = PyStackRef_AsPyObjectBorrow(GETLOCAL(oparg));
PyObject *cell = PyCell_New(initial);
if (cell == NULL) {
case _DELETE_DEREF: {
oparg = CURRENT_OPARG();
PyObject *cell = PyStackRef_AsPyObjectBorrow(GETLOCAL(oparg));
- // Can't use ERROR_IF here.
- // Fortunately we don't need its superpower.
PyObject *oldobj = PyCell_SwapTakeRef((PyCellObject *)cell, NULL);
if (oldobj == NULL) {
_PyFrame_SetStackPointer(frame, stack_pointer);
case _COPY_FREE_VARS: {
oparg = CURRENT_OPARG();
- /* Copy closure variables to free variables */
PyCodeObject *co = _PyFrame_GetCode(frame);
assert(PyStackRef_FunctionCheck(frame->f_funcobj));
PyFunctionObject *func = (PyFunctionObject *)PyStackRef_AsPyObjectBorrow(frame->f_funcobj);
stack_pointer = _PyFrame_GetStackPointer(frame);
JUMP_TO_ERROR();
}
- /* check if __annotations__ in locals()... */
_PyFrame_SetStackPointer(frame, stack_pointer);
int err = PyMapping_GetOptionalItem(LOCALS(), &_Py_ID(__annotations__), &ann_dict);
stack_pointer = _PyFrame_GetStackPointer(frame);
dict_st = stack_pointer[-3 - (oparg - 1)];
PyObject *dict = PyStackRef_AsPyObjectBorrow(dict_st);
assert(PyDict_CheckExact(dict));
- /* dict[key] = value */
- // Do not DECREF INPUTS because the function steals the references
_PyFrame_SetStackPointer(frame, stack_pointer);
int err = _PyDict_SetItem_Take2(
(PyDictObject *)dict,
JUMP_TO_ERROR();
}
if (method_found) {
- self_or_null = self_st; // transfer ownership
+ self_or_null = self_st;
} else {
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 1);
PyObject *attr_o;
if (oparg & 1) {
- /* Designed to work in tandem with CALL, pushes two values. */
attr_o = NULL;
_PyFrame_SetStackPointer(frame, stack_pointer);
int is_meth = _PyObject_GetMethod(PyStackRef_AsPyObjectBorrow(owner), name, &attr_o);
stack_pointer = _PyFrame_GetStackPointer(frame);
if (is_meth) {
- /* We can bypass temporary bound method object.
- meth is unbound method and obj is self.
- meth | self | arg1 | ... | argN
- */
- assert(attr_o != NULL); // No errors on this branch
- self_or_null[0] = owner; // Transfer ownership
+ assert(attr_o != NULL);
+ self_or_null[0] = owner;
}
else {
- /* meth is not an unbound method (but a regular attr, or
- something was returned by a descriptor protocol). Set
- the second element of the stack to NULL, to signal
- CALL that it's not a method call.
- meth | NULL | arg1 | ... | argN
- */
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
_PyFrame_SetStackPointer(frame, stack_pointer);
}
}
else {
- /* Classic, pushes one value. */
_PyFrame_SetStackPointer(frame, stack_pointer);
attr_o = PyObject_GetAttr(PyStackRef_AsPyObjectBorrow(owner), name);
stack_pointer = _PyFrame_GetStackPointer(frame);
JUMP_TO_JUMP_TARGET();
}
#ifdef Py_GIL_DISABLED
- if (!_Py_TryIncrefCompareStackRef(value_ptr, attr_o, &attr)) {
+ int increfed = _Py_TryIncrefCompareStackRef(value_ptr, attr_o, &attr);
+ if (!increfed) {
if (true) {
UOP_STAT_INC(uopcode, miss);
JUMP_TO_JUMP_TARGET();
}
STAT_INC(LOAD_ATTR, hit);
#ifdef Py_GIL_DISABLED
- if (!_Py_TryIncrefCompareStackRef(&ep->me_value, attr_o, &attr)) {
+ int increfed = _Py_TryIncrefCompareStackRef(&ep->me_value, attr_o, &attr);
+ if (!increfed) {
if (true) {
UOP_STAT_INC(uopcode, miss);
JUMP_TO_JUMP_TARGET();
stack_pointer = _PyFrame_GetStackPointer(frame);
FT_ATOMIC_STORE_PTR_RELEASE(ep->me_value, PyStackRef_AsPyObjectSteal(value));
UNLOCK_OBJECT(dict);
- // old_value should be DECREFed after GC track checking is done, if not, it could raise a segmentation fault,
- // when dict only holds the strong reference to value in ep->me_value.
STAT_INC(STORE_ATTR, hit);
stack_pointer += -2;
assert(WITHIN_STACK_BOUNDS());
STAT_INC(COMPARE_OP, hit);
double dleft = PyFloat_AS_DOUBLE(left_o);
double dright = PyFloat_AS_DOUBLE(right_o);
- // 1 if NaN, 2 if <, 4 if >, 8 if ==; this matches low four bits of the oparg
int sign_ish = COMPARISON_BIT(dleft, dright);
PyStackRef_CLOSE_SPECIALIZED(left, _PyFloat_ExactDealloc);
PyStackRef_CLOSE_SPECIALIZED(right, _PyFloat_ExactDealloc);
res = (sign_ish & oparg) ? PyStackRef_True : PyStackRef_False;
- // It's always a bool, so we don't care about oparg & 16.
stack_pointer[-2] = res;
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
_PyLong_DigitCount((PyLongObject *)right_o) <= 1);
Py_ssize_t ileft = _PyLong_CompactValue((PyLongObject *)left_o);
Py_ssize_t iright = _PyLong_CompactValue((PyLongObject *)right_o);
- // 2 if <, 4 if >, 8 if ==; this matches the low 4 bits of the oparg
int sign_ish = COMPARISON_BIT(ileft, iright);
PyStackRef_CLOSE_SPECIALIZED(left, _PyLong_ExactDealloc);
PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc);
res = (sign_ish & oparg) ? PyStackRef_True : PyStackRef_False;
- // It's always a bool, so we don't care about oparg & 16.
stack_pointer[-2] = res;
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
assert((oparg & 0xf) == COMPARISON_NOT_EQUALS || (oparg & 0xf) == COMPARISON_EQUALS);
assert(COMPARISON_NOT_EQUALS + 1 == COMPARISON_EQUALS);
res = ((COMPARISON_NOT_EQUALS + eq) & oparg) ? PyStackRef_True : PyStackRef_False;
- // It's always a bool, so we don't care about oparg & 16.
stack_pointer[-2] = res;
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
JUMP_TO_JUMP_TARGET();
}
STAT_INC(CONTAINS_OP, hit);
- // Note: both set and frozenset use the same seq_contains method!
_PyFrame_SetStackPointer(frame, stack_pointer);
int res = _PySet_Contains((PySetObject *)right_o, left_o);
_PyStackRef tmp = right;
_PyStackRef obj;
_PyStackRef len;
obj = stack_pointer[-1];
- // PUSH(len(TOS))
_PyFrame_SetStackPointer(frame, stack_pointer);
Py_ssize_t len_i = PyObject_Length(PyStackRef_AsPyObjectBorrow(obj));
stack_pointer = _PyFrame_GetStackPointer(frame);
names = stack_pointer[-1];
type = stack_pointer[-2];
subject = stack_pointer[-3];
- // Pop TOS and TOS1. Set TOS to a tuple of attributes on success, or
- // None on failure.
assert(PyTuple_CheckExact(PyStackRef_AsPyObjectBorrow(names)));
_PyFrame_SetStackPointer(frame, stack_pointer);
PyObject *attrs_o = _PyEval_MatchClass(tstate,
stack_pointer += -3;
assert(WITHIN_STACK_BOUNDS());
if (attrs_o) {
- assert(PyTuple_CheckExact(attrs_o)); // Success!
+ assert(PyTuple_CheckExact(attrs_o));
attrs = PyStackRef_FromPyObjectSteal(attrs_o);
}
else {
if (_PyErr_Occurred(tstate)) {
JUMP_TO_ERROR();
}
- // Error!
- attrs = PyStackRef_None; // Failure!
+ attrs = PyStackRef_None;
}
stack_pointer[0] = attrs;
stack_pointer += 1;
_PyStackRef values_or_none;
keys = stack_pointer[-1];
subject = stack_pointer[-2];
- // On successful match, PUSH(values). Otherwise, PUSH(None).
_PyFrame_SetStackPointer(frame, stack_pointer);
PyObject *values_or_none_o = _PyEval_MatchKeys(tstate,
PyStackRef_AsPyObjectBorrow(subject), PyStackRef_AsPyObjectBorrow(keys));
_PyStackRef iterable;
_PyStackRef iter;
iterable = stack_pointer[-1];
- /* before: [obj]; after [getiter(obj)] */
_PyFrame_SetStackPointer(frame, stack_pointer);
PyObject *iter_o = PyObject_GetIter(PyStackRef_AsPyObjectBorrow(iterable));
stack_pointer = _PyFrame_GetStackPointer(frame);
_PyStackRef iterable;
_PyStackRef iter;
iterable = stack_pointer[-1];
- /* before: [obj]; after [getiter(obj)] */
PyObject *iterable_o = PyStackRef_AsPyObjectBorrow(iterable);
if (PyCoro_CheckExact(iterable_o)) {
- /* `iterable` is a coroutine */
if (!(_PyFrame_GetCode(frame)->co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE))) {
- /* and it is used in a 'yield from' expression of a
- regular generator. */
_PyFrame_SetStackPointer(frame, stack_pointer);
_PyErr_SetString(tstate, PyExc_TypeError,
"cannot 'yield from' a coroutine object "
}
iter = iterable;
}
+ else if (PyGen_CheckExact(iterable_o)) {
+ iter = iterable;
+ }
else {
- if (PyGen_CheckExact(iterable_o)) {
- iter = iterable;
- }
- else {
- /* `iterable` is not a generator. */
- _PyFrame_SetStackPointer(frame, stack_pointer);
- PyObject *iter_o = PyObject_GetIter(iterable_o);
- stack_pointer = _PyFrame_GetStackPointer(frame);
- if (iter_o == NULL) {
- JUMP_TO_ERROR();
- }
- iter = PyStackRef_FromPyObjectSteal(iter_o);
- _PyFrame_SetStackPointer(frame, stack_pointer);
- _PyStackRef tmp = iterable;
- iterable = iter;
- stack_pointer[-1] = iterable;
- PyStackRef_CLOSE(tmp);
- stack_pointer = _PyFrame_GetStackPointer(frame);
+ _PyFrame_SetStackPointer(frame, stack_pointer);
+ PyObject *iter_o = PyObject_GetIter(iterable_o);
+ stack_pointer = _PyFrame_GetStackPointer(frame);
+ if (iter_o == NULL) {
+ JUMP_TO_ERROR();
}
+ iter = PyStackRef_FromPyObjectSteal(iter_o);
+ _PyFrame_SetStackPointer(frame, stack_pointer);
+ _PyStackRef tmp = iterable;
+ iterable = iter;
+ stack_pointer[-1] = iterable;
+ PyStackRef_CLOSE(tmp);
+ stack_pointer = _PyFrame_GetStackPointer(frame);
}
stack_pointer[-1] = iter;
break;
_PyStackRef iter;
_PyStackRef next;
iter = stack_pointer[-1];
- /* before: [iter]; after: [iter, iter()] *or* [] (and jump over END_FOR.) */
PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter);
_PyFrame_SetStackPointer(frame, stack_pointer);
PyObject *next_o = (*Py_TYPE(iter_o)->tp_iternext)(iter_o);
_PyErr_Clear(tstate);
stack_pointer = _PyFrame_GetStackPointer(frame);
}
- /* iterator ended normally */
- /* The translator sets the deopt target just past the matching END_FOR */
if (true) {
UOP_STAT_INC(uopcode, miss);
JUMP_TO_JUMP_TARGET();
}
}
next = PyStackRef_FromPyObjectSteal(next_o);
- // Common case: no jump, leave it to the code generator
stack_pointer[0] = next;
stack_pointer += 1;
assert(WITHIN_STACK_BOUNDS());
_PyFrame_SetStackPointer(frame, stack_pointer);
int result = _PyList_GetItemRefNoLock(seq, it->it_index, &next);
stack_pointer = _PyFrame_GetStackPointer(frame);
- // A negative result means we lost a race with another thread
- // and we need to take the slow path.
if (result < 0) {
UOP_STAT_INC(uopcode, miss);
JUMP_TO_JUMP_TARGET();
JUMP_TO_JUMP_TARGET();
}
#ifdef Py_GIL_DISABLED
- // Since generators can't be used by multiple threads anyway we
- // don't need to deopt here, but this lets us work on making
- // generators thread-safe without necessarily having to
- // specialize them thread-safely as well.
+
if (!_PyObject_IsUniquelyReferenced((PyObject *)gen)) {
UOP_STAT_INC(uopcode, miss);
JUMP_TO_JUMP_TARGET();
gen->gi_exc_state.previous_item = tstate->exc_info;
tstate->exc_info = &gen->gi_exc_state;
gen_frame->previous = frame;
- // oparg is the return offset from the next instruction.
frame->return_offset = (uint16_t)( 2 + oparg);
stack_pointer[0].bits = (uintptr_t)gen_frame;
stack_pointer += 1;
lasti = stack_pointer[-3];
exit_self = stack_pointer[-4];
exit_func = stack_pointer[-5];
- /* At the top of the stack are 4 values:
- - val: TOP = exc_info()
- - unused: SECOND = previous exception
- - lasti: THIRD = lasti of exception in exc_info()
- - exit_self: FOURTH = the context or NULL
- - exit_func: FIFTH = the context.__exit__ function or context.__exit__ bound method
- We call FOURTH(type(TOP), TOP, GetTraceback(TOP)).
- Then we push the __exit__ return value.
- */
PyObject *exc, *tb;
PyObject *val_o = PyStackRef_AsPyObjectBorrow(val);
PyObject *exit_func_o = PyStackRef_AsPyObjectBorrow(exit_func);
tb = Py_None;
}
assert(PyStackRef_LongCheck(lasti));
- (void)lasti; // Shut up compiler warning if asserts are off
+ (void)lasti;
PyObject *stack[5] = {NULL, PyStackRef_AsPyObjectBorrow(exit_self), exc, val_o, tb};
int has_self = !PyStackRef_IsNull(exit_self);
_PyFrame_SetStackPointer(frame, stack_pointer);
owner = stack_pointer[-1];
PyObject *descr = (PyObject *)CURRENT_OPERAND0();
assert(oparg & 1);
- /* Cached method object */
STAT_INC(LOAD_ATTR, hit);
assert(descr != NULL);
assert(_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR));
uint16_t dictoffset = (uint16_t)CURRENT_OPERAND0();
char *ptr = ((char *)PyStackRef_AsPyObjectBorrow(owner)) + MANAGED_DICT_OFFSET + dictoffset;
PyObject *dict = FT_ATOMIC_LOAD_PTR_ACQUIRE(*(PyObject **)ptr);
- /* This object has a __dict__, just not yet created */
if (dict != NULL) {
UOP_STAT_INC(uopcode, miss);
JUMP_TO_JUMP_TARGET();
self_or_null = &stack_pointer[-1 - oparg];
callable = &stack_pointer[-2 - oparg];
PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]);
- // oparg counts all of the args, but *not* self:
int total_args = oparg;
if (!PyStackRef_IsNull(self_or_null[0])) {
args--;
args, total_args, NULL, frame
);
stack_pointer = _PyFrame_GetStackPointer(frame);
- // The frame has stolen all the arguments from the stack.
stack_pointer += -2 - oparg;
assert(WITHIN_STACK_BOUNDS());
if (temp == NULL) {
arguments--;
total_args++;
}
- /* Callable is not a normal Python function */
STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o);
if (CONVERSION_FAILED(args_o)) {
_PyFrame_SetStackPointer(frame, stack_pointer);
case _PUSH_FRAME: {
_PyInterpreterFrame *new_frame;
new_frame = (_PyInterpreterFrame *)stack_pointer[-1].bits;
- // Write it out explicitly because it's subtly different.
- // Eventually this should be the only occurrence of this code.
assert(tstate->interp->eval_frame == NULL);
_PyInterpreterFrame *temp = new_frame;
stack_pointer += -1;
stack_pointer = _PyFrame_GetStackPointer(frame);
assert(_PyFrame_GetBytecode(shim)[0].op.code == EXIT_INIT_CHECK);
assert(_PyFrame_GetBytecode(shim)[1].op.code == RETURN_VALUE);
- /* Push self onto stack of shim */
shim->localsplus[0] = PyStackRef_DUP(self[0]);
_PyFrame_SetStackPointer(frame, stack_pointer);
_PyInterpreterFrame *temp = _PyEvalFramePushAndInit(
}
init_frame = temp;
frame->return_offset = 1 + INLINE_CACHE_ENTRIES_CALL;
- /* Account for pushing the extra frame.
- * We don't check recursion depth here,
- * as it will be checked after start_frame */
tstate->py_recursion_remaining--;
stack_pointer[0].bits = (uintptr_t)init_frame;
stack_pointer += 1;
args = &stack_pointer[-oparg];
self_or_null = &stack_pointer[-1 - oparg];
callable = &stack_pointer[-2 - oparg];
- /* Builtin METH_O functions */
PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]);
int total_args = oparg;
if (!PyStackRef_IsNull(self_or_null[0])) {
UOP_STAT_INC(uopcode, miss);
JUMP_TO_JUMP_TARGET();
}
- // CPython promises to check all non-vectorcall function calls.
if (_Py_ReachedRecursionLimit(tstate)) {
UOP_STAT_INC(uopcode, miss);
JUMP_TO_JUMP_TARGET();
args = &stack_pointer[-oparg];
self_or_null = &stack_pointer[-1 - oparg];
callable = &stack_pointer[-2 - oparg];
- /* Builtin METH_FASTCALL functions, without keywords */
PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]);
int total_args = oparg;
_PyStackRef *arguments = args;
}
STAT_INC(CALL, hit);
PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable_o);
- /* res = func(self, args, nargs) */
STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o);
if (CONVERSION_FAILED(args_o)) {
_PyFrame_SetStackPointer(frame, stack_pointer);
args = &stack_pointer[-oparg];
self_or_null = &stack_pointer[-1 - oparg];
callable = &stack_pointer[-2 - oparg];
- /* Builtin METH_FASTCALL | METH_KEYWORDS functions */
PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]);
int total_args = oparg;
_PyStackRef *arguments = args;
JUMP_TO_JUMP_TARGET();
}
STAT_INC(CALL, hit);
- /* res = func(self, arguments, nargs, kwnames) */
_PyFrame_SetStackPointer(frame, stack_pointer);
PyCFunctionFastWithKeywords cfunc =
(PyCFunctionFastWithKeywords)(void(*)(void))
args = &stack_pointer[-oparg];
self_or_null = &stack_pointer[-1 - oparg];
callable = &stack_pointer[-2 - oparg];
- /* len(o) */
PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]);
int total_args = oparg;
if (!PyStackRef_IsNull(self_or_null[0])) {
args = &stack_pointer[-oparg];
self_or_null = &stack_pointer[-1 - oparg];
callable = stack_pointer[-2 - oparg];
- /* isinstance(o, o2) */
PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable);
int total_args = oparg;
_PyStackRef *arguments = args;
JUMP_TO_ERROR();
}
#if TIER_ONE
- // Skip the following POP_TOP. This is done here in tier one, and
- // during trace projection in tier two:
+
assert(next_instr->op.code == POP_TOP);
SKIP_OVER(1);
#endif
UOP_STAT_INC(uopcode, miss);
JUMP_TO_JUMP_TARGET();
}
- // CPython promises to check all non-vectorcall function calls.
if (_Py_ReachedRecursionLimit(tstate)) {
UOP_STAT_INC(uopcode, miss);
JUMP_TO_JUMP_TARGET();
UOP_STAT_INC(uopcode, miss);
JUMP_TO_JUMP_TARGET();
}
- // CPython promises to check all non-vectorcall function calls.
if (_Py_ReachedRecursionLimit(tstate)) {
UOP_STAT_INC(uopcode, miss);
JUMP_TO_JUMP_TARGET();
total_args++;
}
PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o;
- /* Builtin METH_FASTCALL methods, without keywords */
if (!Py_IS_TYPE(method, &PyMethodDescr_Type)) {
UOP_STAT_INC(uopcode, miss);
JUMP_TO_JUMP_TARGET();
self_or_null = &stack_pointer[-2 - oparg];
callable = &stack_pointer[-3 - oparg];
PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]);
- // oparg counts all of the args, but *not* self:
int total_args = oparg;
_PyStackRef *arguments = args;
if (!PyStackRef_IsNull(self_or_null[0])) {
_PyFrame_SetStackPointer(frame, stack_pointer);
PyStackRef_CLOSE(kwnames);
stack_pointer = _PyFrame_GetStackPointer(frame);
- // The frame has stolen all the arguments from the stack,
- // so there is no need to clean them up.
stack_pointer += -2 - oparg;
assert(WITHIN_STACK_BOUNDS());
if (temp == NULL) {
arguments--;
total_args++;
}
- /* Callable is not a normal Python function */
STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o);
if (CONVERSION_FAILED(args_o)) {
_PyFrame_SetStackPointer(frame, stack_pointer);
_PyStackRef res;
value = stack_pointer[-1];
PyObject *value_o = PyStackRef_AsPyObjectBorrow(value);
- /* If value is a unicode object, then we know the result
- * of format(value) is value itself. */
if (!PyUnicode_CheckExact(value_o)) {
_PyFrame_SetStackPointer(frame, stack_pointer);
PyObject *res_o = PyObject_Format(value_o, NULL);
case _MAKE_WARM: {
current_executor->vm_data.warm = true;
- // It's okay if this ends up going negative.
if (--tstate->interp->trace_run_counter == 0) {
_Py_set_eval_breaker_bit(tstate, _PY_EVAL_JIT_INVALIDATE_COLD_BIT);
}
JUMP_TO_PREDICTED(BINARY_OP);
}
STAT_INC(BINARY_OP, hit);
- /* Handle `left = left + right` or `left += right` for str.
- *
- * When possible, extend `left` in place rather than
- * allocating a new PyUnicodeObject. This attempts to avoid
- * quadratic behavior when one neglects to use str.join().
- *
- * If `left` has only two references remaining (one from
- * the stack, one in the locals), DECREFing `left` leaves
- * only the locals reference, so PyUnicode_Append knows
- * that the string is safe to mutate.
- */
assert(Py_REFCNT(left_o) >= 2 || !PyStackRef_IsHeapSafe(left));
PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc);
PyObject *temp = PyStackRef_AsPyObjectSteal(*target_local);
JUMP_TO_LABEL(error);
}
#if TIER_ONE
- // The STORE_FAST is already done. This is done here in tier one,
- // and during trace projection in tier two:
+
assert(next_instr->op.code == STORE_FAST);
SKIP_OVER(1);
#endif
if (rc <= 0) {
JUMP_TO_LABEL(error);
}
- // not found or error
res = PyStackRef_FromPyObjectSteal(res_o);
stack_pointer[0] = res;
stack_pointer += 1;
}
// _PUSH_FRAME
{
- // Write it out explicitly because it's subtly different.
- // Eventually this should be the only occurrence of this code.
assert(tstate->interp->eval_frame == NULL);
_PyInterpreterFrame *temp = new_frame;
stack_pointer += -2;
assert(_PyOpcode_Deopt[opcode] == (BINARY_OP));
JUMP_TO_PREDICTED(BINARY_OP);
}
- // Deopt unless 0 <= sub < PyList_Size(list)
if (!_PyLong_IsNonNegativeCompact((PyLongObject *)sub)) {
UPDATE_MISS_STATS(BINARY_OP);
assert(_PyOpcode_Deopt[opcode] == (BINARY_OP));
assert(_PyOpcode_Deopt[opcode] == (BINARY_OP));
JUMP_TO_PREDICTED(BINARY_OP);
}
- // Specialize for reading an ASCII character from any string:
Py_UCS4 c = PyUnicode_READ_CHAR(str, index);
if (Py_ARRAY_LENGTH(_Py_SINGLETON(strings).ascii) <= c) {
UPDATE_MISS_STATS(BINARY_OP);
assert(_PyOpcode_Deopt[opcode] == (BINARY_OP));
JUMP_TO_PREDICTED(BINARY_OP);
}
- // Deopt unless 0 <= sub < PyTuple_Size(list)
if (!_PyLong_IsNonNegativeCompact((PyLongObject *)sub)) {
UPDATE_MISS_STATS(BINARY_OP);
assert(_PyOpcode_Deopt[opcode] == (BINARY_OP));
_PyStackRef res;
// _SPECIALIZE_BINARY_SLICE
{
- // Placeholder until we implement BINARY_SLICE specialization
#if ENABLE_SPECIALIZATION
OPCODE_DEFERRED_INC(BINARY_SLICE);
#endif /* ENABLE_SPECIALIZATION */
PyStackRef_AsPyObjectSteal(stop));
stack_pointer = _PyFrame_GetStackPointer(frame);
PyObject *res_o;
- // Can't use ERROR_IF() here, because we haven't
- // DECREF'ed container yet, and we still own slice.
if (slice == NULL) {
res_o = NULL;
}
{
args = &stack_pointer[-oparg];
PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]);
- // oparg counts all of the args, but *not* self:
int total_args = oparg;
_PyStackRef *arguments = args;
if (!PyStackRef_IsNull(self_or_null[0])) {
arguments--;
total_args++;
}
- // Check if the call can be inlined or not
if (Py_TYPE(callable_o) == &PyFunction_Type &&
tstate->interp->eval_frame == NULL &&
((PyFunctionObject *)callable_o)->vectorcall == _PyFunction_Vectorcall)
arguments, total_args, NULL, frame
);
stack_pointer = _PyFrame_GetStackPointer(frame);
- // Manipulate stack directly since we leave using DISPATCH_INLINED().
stack_pointer += -2 - oparg;
assert(WITHIN_STACK_BOUNDS());
- // The frame has stolen all the arguments from the stack,
- // so there is no need to clean them up.
if (new_frame == NULL) {
JUMP_TO_LABEL(error);
}
frame->return_offset = 4 ;
DISPATCH_INLINED(new_frame);
}
- /* Callable is not a normal Python function */
STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o);
if (CONVERSION_FAILED(args_o)) {
_PyFrame_SetStackPointer(frame, stack_pointer);
stack_pointer = _PyFrame_GetStackPointer(frame);
assert(_PyFrame_GetBytecode(shim)[0].op.code == EXIT_INIT_CHECK);
assert(_PyFrame_GetBytecode(shim)[1].op.code == RETURN_VALUE);
- /* Push self onto stack of shim */
shim->localsplus[0] = PyStackRef_DUP(self[0]);
_PyFrame_SetStackPointer(frame, stack_pointer);
_PyInterpreterFrame *temp = _PyEvalFramePushAndInit(
}
init_frame = temp;
frame->return_offset = 1 + INLINE_CACHE_ENTRIES_CALL;
- /* Account for pushing the extra frame.
- * We don't check recursion depth here,
- * as it will be checked after start_frame */
tstate->py_recursion_remaining--;
}
// _PUSH_FRAME
{
new_frame = init_frame;
- // Write it out explicitly because it's subtly different.
- // Eventually this should be the only occurrence of this code.
assert(tstate->interp->eval_frame == NULL);
_PyInterpreterFrame *temp = new_frame;
_PyFrame_SetStackPointer(frame, stack_pointer);
}
// _PUSH_FRAME
{
- // Write it out explicitly because it's subtly different.
- // Eventually this should be the only occurrence of this code.
assert(tstate->interp->eval_frame == NULL);
_PyInterpreterFrame *temp = new_frame;
stack_pointer += -2 - oparg;
{
args = &stack_pointer[-oparg];
PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]);
- // oparg counts all of the args, but *not* self:
int total_args = oparg;
if (!PyStackRef_IsNull(self_or_null[0])) {
args--;
args, total_args, NULL, frame
);
stack_pointer = _PyFrame_GetStackPointer(frame);
- // The frame has stolen all the arguments from the stack.
stack_pointer += -2 - oparg;
assert(WITHIN_STACK_BOUNDS());
if (temp == NULL) {
}
// _PUSH_FRAME
{
- // Write it out explicitly because it's subtly different.
- // Eventually this should be the only occurrence of this code.
assert(tstate->interp->eval_frame == NULL);
_PyInterpreterFrame *temp = new_frame;
_PyFrame_SetStackPointer(frame, stack_pointer);
args = &stack_pointer[-oparg];
self_or_null = &stack_pointer[-1 - oparg];
callable = &stack_pointer[-2 - oparg];
- /* Builtin METH_FASTCALL functions, without keywords */
PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]);
int total_args = oparg;
_PyStackRef *arguments = args;
}
STAT_INC(CALL, hit);
PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable_o);
- /* res = func(self, args, nargs) */
STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o);
if (CONVERSION_FAILED(args_o)) {
_PyFrame_SetStackPointer(frame, stack_pointer);
args = &stack_pointer[-oparg];
self_or_null = &stack_pointer[-1 - oparg];
callable = &stack_pointer[-2 - oparg];
- /* Builtin METH_FASTCALL | METH_KEYWORDS functions */
PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]);
int total_args = oparg;
_PyStackRef *arguments = args;
JUMP_TO_PREDICTED(CALL);
}
STAT_INC(CALL, hit);
- /* res = func(self, arguments, nargs, kwnames) */
_PyFrame_SetStackPointer(frame, stack_pointer);
PyCFunctionFastWithKeywords cfunc =
(PyCFunctionFastWithKeywords)(void(*)(void))
args = &stack_pointer[-oparg];
self_or_null = &stack_pointer[-1 - oparg];
callable = &stack_pointer[-2 - oparg];
- /* Builtin METH_O functions */
PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]);
int total_args = oparg;
if (!PyStackRef_IsNull(self_or_null[0])) {
assert(_PyOpcode_Deopt[opcode] == (CALL));
JUMP_TO_PREDICTED(CALL);
}
- // CPython promises to check all non-vectorcall function calls.
if (_Py_ReachedRecursionLimit(tstate)) {
UPDATE_MISS_STATS(CALL);
assert(_PyOpcode_Deopt[opcode] == (CALL));
func_st = func;
(void)null;
PyObject *func = PyStackRef_AsPyObjectBorrow(func_st);
- // DICT_MERGE is called before this opcode if there are kwargs.
- // It converts all dict subtypes in kwargs into regular dicts.
EVAL_CALL_STAT_INC_IF_FUNCTION(EVAL_CALL_FUNCTION_EX, func);
PyObject *result_o;
assert(!_PyErr_Occurred(tstate));
tstate, func_st, locals,
nargs, callargs, kwargs, frame);
stack_pointer = _PyFrame_GetStackPointer(frame);
- // Need to sync the stack since we exit with DISPATCH_INLINED.
stack_pointer += -2;
assert(WITHIN_STACK_BOUNDS());
if (new_frame == NULL) {
args = &stack_pointer[-oparg];
self_or_null = &stack_pointer[-1 - oparg];
callable = stack_pointer[-2 - oparg];
- /* isinstance(o, o2) */
PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable);
int total_args = oparg;
_PyStackRef *arguments = args;
args = &stack_pointer[-1 - oparg];
PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]);
PyObject *kwnames_o = PyStackRef_AsPyObjectBorrow(kwnames);
- // oparg counts all of the args, but *not* self:
int total_args = oparg;
_PyStackRef *arguments = args;
if (!PyStackRef_IsNull(self_or_null[0])) {
total_args++;
}
int positional_args = total_args - (int)PyTuple_GET_SIZE(kwnames_o);
- // Check if the call can be inlined or not
if (Py_TYPE(callable_o) == &PyFunction_Type &&
tstate->interp->eval_frame == NULL &&
((PyFunctionObject *)callable_o)->vectorcall == _PyFunction_Vectorcall)
_PyFrame_SetStackPointer(frame, stack_pointer);
PyStackRef_CLOSE(kwnames);
stack_pointer = _PyFrame_GetStackPointer(frame);
- // Sync stack explicitly since we leave using DISPATCH_INLINED().
- // The frame has stolen all the arguments from the stack,
- // so there is no need to clean them up.
if (new_frame == NULL) {
JUMP_TO_LABEL(error);
}
frame->return_offset = 4 ;
DISPATCH_INLINED(new_frame);
}
- /* Callable is not a normal Python function */
STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o);
if (CONVERSION_FAILED(args_o)) {
stack_pointer[-1] = kwnames;
kwnames = stack_pointer[-1];
args = &stack_pointer[-1 - oparg];
PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]);
- // oparg counts all of the args, but *not* self:
int total_args = oparg;
_PyStackRef *arguments = args;
if (!PyStackRef_IsNull(self_or_null[0])) {
_PyFrame_SetStackPointer(frame, stack_pointer);
PyStackRef_CLOSE(kwnames);
stack_pointer = _PyFrame_GetStackPointer(frame);
- // The frame has stolen all the arguments from the stack,
- // so there is no need to clean them up.
stack_pointer += -2 - oparg;
assert(WITHIN_STACK_BOUNDS());
if (temp == NULL) {
}
// _PUSH_FRAME
{
- // Write it out explicitly because it's subtly different.
- // Eventually this should be the only occurrence of this code.
assert(tstate->interp->eval_frame == NULL);
_PyInterpreterFrame *temp = new_frame;
_PyFrame_SetStackPointer(frame, stack_pointer);
arguments--;
total_args++;
}
- /* Callable is not a normal Python function */
STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o);
if (CONVERSION_FAILED(args_o)) {
_PyFrame_SetStackPointer(frame, stack_pointer);
args = &stack_pointer[-1 - oparg];
self_or_null = &stack_pointer[-2 - oparg];
PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]);
- // oparg counts all of the args, but *not* self:
int total_args = oparg;
_PyStackRef *arguments = args;
if (!PyStackRef_IsNull(self_or_null[0])) {
_PyFrame_SetStackPointer(frame, stack_pointer);
PyStackRef_CLOSE(kwnames);
stack_pointer = _PyFrame_GetStackPointer(frame);
- // The frame has stolen all the arguments from the stack,
- // so there is no need to clean them up.
stack_pointer += -2 - oparg;
assert(WITHIN_STACK_BOUNDS());
if (temp == NULL) {
}
// _PUSH_FRAME
{
- // Write it out explicitly because it's subtly different.
- // Eventually this should be the only occurrence of this code.
assert(tstate->interp->eval_frame == NULL);
_PyInterpreterFrame *temp = new_frame;
_PyFrame_SetStackPointer(frame, stack_pointer);
args = &stack_pointer[-oparg];
self_or_null = &stack_pointer[-1 - oparg];
callable = &stack_pointer[-2 - oparg];
- /* len(o) */
PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]);
int total_args = oparg;
if (!PyStackRef_IsNull(self_or_null[0])) {
JUMP_TO_LABEL(error);
}
#if TIER_ONE
- // Skip the following POP_TOP. This is done here in tier one, and
- // during trace projection in tier two:
+
assert(next_instr->op.code == POP_TOP);
SKIP_OVER(1);
#endif
total_args++;
}
PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o;
- /* Builtin METH_FASTCALL methods, without keywords */
if (!Py_IS_TYPE(method, &PyMethodDescr_Type)) {
UPDATE_MISS_STATS(CALL);
assert(_PyOpcode_Deopt[opcode] == (CALL));
assert(_PyOpcode_Deopt[opcode] == (CALL));
JUMP_TO_PREDICTED(CALL);
}
- // CPython promises to check all non-vectorcall function calls.
if (_Py_ReachedRecursionLimit(tstate)) {
UPDATE_MISS_STATS(CALL);
assert(_PyOpcode_Deopt[opcode] == (CALL));
assert(_PyOpcode_Deopt[opcode] == (CALL));
JUMP_TO_PREDICTED(CALL);
}
- // CPython promises to check all non-vectorcall function calls.
if (_Py_ReachedRecursionLimit(tstate)) {
UPDATE_MISS_STATS(CALL);
assert(_PyOpcode_Deopt[opcode] == (CALL));
arguments--;
total_args++;
}
- /* Callable is not a normal Python function */
STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o);
if (CONVERSION_FAILED(args_o)) {
_PyFrame_SetStackPointer(frame, stack_pointer);
}
// _PUSH_FRAME
{
- // Write it out explicitly because it's subtly different.
- // Eventually this should be the only occurrence of this code.
assert(tstate->interp->eval_frame == NULL);
_PyInterpreterFrame *temp = new_frame;
stack_pointer += -2 - oparg;
args = &stack_pointer[-oparg];
self_or_null = &stack_pointer[-1 - oparg];
PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]);
- // oparg counts all of the args, but *not* self:
int total_args = oparg;
if (!PyStackRef_IsNull(self_or_null[0])) {
args--;
args, total_args, NULL, frame
);
stack_pointer = _PyFrame_GetStackPointer(frame);
- // The frame has stolen all the arguments from the stack.
stack_pointer += -2 - oparg;
assert(WITHIN_STACK_BOUNDS());
if (temp == NULL) {
}
// _PUSH_FRAME
{
- // Write it out explicitly because it's subtly different.
- // Eventually this should be the only occurrence of this code.
assert(tstate->interp->eval_frame == NULL);
_PyInterpreterFrame *temp = new_frame;
_PyFrame_SetStackPointer(frame, stack_pointer);
STAT_INC(COMPARE_OP, hit);
double dleft = PyFloat_AS_DOUBLE(left_o);
double dright = PyFloat_AS_DOUBLE(right_o);
- // 1 if NaN, 2 if <, 4 if >, 8 if ==; this matches low four bits of the oparg
int sign_ish = COMPARISON_BIT(dleft, dright);
PyStackRef_CLOSE_SPECIALIZED(left, _PyFloat_ExactDealloc);
PyStackRef_CLOSE_SPECIALIZED(right, _PyFloat_ExactDealloc);
res = (sign_ish & oparg) ? PyStackRef_True : PyStackRef_False;
- // It's always a bool, so we don't care about oparg & 16.
}
stack_pointer[-2] = res;
stack_pointer += -1;
_PyLong_DigitCount((PyLongObject *)right_o) <= 1);
Py_ssize_t ileft = _PyLong_CompactValue((PyLongObject *)left_o);
Py_ssize_t iright = _PyLong_CompactValue((PyLongObject *)right_o);
- // 2 if <, 4 if >, 8 if ==; this matches the low 4 bits of the oparg
int sign_ish = COMPARISON_BIT(ileft, iright);
PyStackRef_CLOSE_SPECIALIZED(left, _PyLong_ExactDealloc);
PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc);
res = (sign_ish & oparg) ? PyStackRef_True : PyStackRef_False;
- // It's always a bool, so we don't care about oparg & 16.
}
stack_pointer[-2] = res;
stack_pointer += -1;
assert((oparg & 0xf) == COMPARISON_NOT_EQUALS || (oparg & 0xf) == COMPARISON_EQUALS);
assert(COMPARISON_NOT_EQUALS + 1 == COMPARISON_EQUALS);
res = ((COMPARISON_NOT_EQUALS + eq) & oparg) ? PyStackRef_True : PyStackRef_False;
- // It's always a bool, so we don't care about oparg & 16.
}
stack_pointer[-2] = res;
stack_pointer += -1;
JUMP_TO_PREDICTED(CONTAINS_OP);
}
STAT_INC(CONTAINS_OP, hit);
- // Note: both set and frozenset use the same seq_contains method!
_PyFrame_SetStackPointer(frame, stack_pointer);
int res = _PySet_Contains((PySetObject *)right_o, left_o);
_PyStackRef tmp = right;
frame->instr_ptr = next_instr;
next_instr += 1;
INSTRUCTION_STATS(COPY_FREE_VARS);
- /* Copy closure variables to free variables */
PyCodeObject *co = _PyFrame_GetCode(frame);
assert(PyStackRef_FunctionCheck(frame->f_funcobj));
PyFunctionObject *func = (PyFunctionObject *)PyStackRef_AsPyObjectBorrow(frame->f_funcobj);
next_instr += 1;
INSTRUCTION_STATS(DELETE_DEREF);
PyObject *cell = PyStackRef_AsPyObjectBorrow(GETLOCAL(oparg));
- // Can't use ERROR_IF here.
- // Fortunately we don't need its superpower.
PyObject *oldobj = PyCell_SwapTakeRef((PyCellObject *)cell, NULL);
if (oldobj == NULL) {
_PyFrame_SetStackPointer(frame, stack_pointer);
_PyFrame_SetStackPointer(frame, stack_pointer);
int err = PyDict_Pop(GLOBALS(), name, NULL);
stack_pointer = _PyFrame_GetStackPointer(frame);
- // Can't use ERROR_IF here.
if (err < 0) {
JUMP_TO_LABEL(error);
}
_PyFrame_SetStackPointer(frame, stack_pointer);
err = PyObject_DelItem(ns, name);
stack_pointer = _PyFrame_GetStackPointer(frame);
- // Can't use ERROR_IF here.
if (err != 0) {
_PyFrame_SetStackPointer(frame, stack_pointer);
_PyEval_FormatExcCheckArg(tstate, PyExc_NameError,
_PyStackRef sub;
sub = stack_pointer[-1];
container = stack_pointer[-2];
- /* del container[sub] */
_PyFrame_SetStackPointer(frame, stack_pointer);
int err = PyObject_DelItem(PyStackRef_AsPyObjectBorrow(container),
PyStackRef_AsPyObjectBorrow(sub));
_PyStackRef exc_st;
exc_st = stack_pointer[-1];
awaitable_st = stack_pointer[-2];
- JUMPBY(0); // Pretend jump as we need source offset for monitoring
+ JUMPBY(0);
(void)oparg;
PyObject *exc = PyStackRef_AsPyObjectBorrow(exc_st);
assert(exc && PyExceptionInstance_Check(exc));
INSTRUCTION_STATS(END_FOR);
_PyStackRef value;
value = stack_pointer[-1];
- /* Don't update instr_ptr, so that POP_ITER sees
- * the FOR_ITER as the previous instruction.
- * This has the benign side effect that if value is
- * finalized it will see the location as the FOR_ITER's.
- */
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
_PyFrame_SetStackPointer(frame, stack_pointer);
assert(executor->vm_data.code == code);
assert(executor->vm_data.valid);
assert(tstate->previous_executor == NULL);
- /* If the eval breaker is set then stay in tier 1.
- * This avoids any potentially infinite loops
- * involving _RESUME_CHECK */
if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) {
opcode = executor->vm_data.opcode;
oparg = (oparg & ~255) | executor->vm_data.oparg;
_PyStackRef res;
value = stack_pointer[-1];
PyObject *value_o = PyStackRef_AsPyObjectBorrow(value);
- /* If value is a unicode object, then we know the result
- * of format(value) is value itself. */
if (!PyUnicode_CheckExact(value_o)) {
_PyFrame_SetStackPointer(frame, stack_pointer);
PyObject *res_o = PyObject_Format(value_o, NULL);
}
// _FOR_ITER
{
- /* before: [iter]; after: [iter, iter()] *or* [] (and jump over END_FOR.) */
PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter);
_PyFrame_SetStackPointer(frame, stack_pointer);
PyObject *next_o = (*Py_TYPE(iter_o)->tp_iternext)(iter_o);
_PyErr_Clear(tstate);
stack_pointer = _PyFrame_GetStackPointer(frame);
}
- /* iterator ended normally */
assert(next_instr[oparg].op.code == END_FOR ||
next_instr[oparg].op.code == INSTRUMENTED_END_FOR);
- /* Jump forward oparg, then skip following END_FOR */
JUMPBY(oparg + 1);
DISPATCH();
}
next = PyStackRef_FromPyObjectSteal(next_o);
- // Common case: no jump, leave it to the code generator
}
stack_pointer[0] = next;
stack_pointer += 1;
JUMP_TO_PREDICTED(FOR_ITER);
}
#ifdef Py_GIL_DISABLED
- // Since generators can't be used by multiple threads anyway we
- // don't need to deopt here, but this lets us work on making
- // generators thread-safe without necessarily having to
- // specialize them thread-safely as well.
if (!_PyObject_IsUniquelyReferenced((PyObject *)gen)) {
UPDATE_MISS_STATS(FOR_ITER);
assert(_PyOpcode_Deopt[opcode] == (FOR_ITER));
gen->gi_exc_state.previous_item = tstate->exc_info;
tstate->exc_info = &gen->gi_exc_state;
gen_frame->previous = frame;
- // oparg is the return offset from the next instruction.
frame->return_offset = (uint16_t)( 2 + oparg);
}
// _PUSH_FRAME
{
new_frame = gen_frame;
- // Write it out explicitly because it's subtly different.
- // Eventually this should be the only occurrence of this code.
assert(tstate->interp->eval_frame == NULL);
_PyInterpreterFrame *temp = new_frame;
_PyFrame_SetStackPointer(frame, stack_pointer);
{
PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter);
assert(Py_TYPE(iter_o) == &PyListIter_Type);
- // For free-threaded Python, the loop exit can happen at any point during
- // item retrieval, so it doesn't make much sense to check and jump
- // separately before item retrieval. Any length check we do here can be
- // invalid by the time we actually try to fetch the item.
#ifdef Py_GIL_DISABLED
assert(_PyObject_IsUniquelyReferenced(iter_o));
(void)iter_o;
Py_DECREF(seq);
stack_pointer = _PyFrame_GetStackPointer(frame);
}
- /* Jump forward oparg, then skip following END_FOR instruction */
JUMPBY(oparg + 1);
DISPATCH();
}
_PyFrame_SetStackPointer(frame, stack_pointer);
int result = _PyList_GetItemRefNoLock(seq, it->it_index, &next);
stack_pointer = _PyFrame_GetStackPointer(frame);
- // A negative result means we lost a race with another thread
- // and we need to take the slow path.
if (result < 0) {
UPDATE_MISS_STATS(FOR_ITER);
assert(_PyOpcode_Deopt[opcode] == (FOR_ITER));
}
if (result == 0) {
it->it_index = -1;
- /* Jump forward oparg, then skip following END_FOR instruction */
JUMPBY(oparg + 1);
DISPATCH();
}
#endif
STAT_INC(FOR_ITER, hit);
if (r->len <= 0) {
- // Jump over END_FOR instruction.
JUMPBY(oparg + 1);
DISPATCH();
}
stack_pointer = _PyFrame_GetStackPointer(frame);
}
#endif
- /* Jump forward oparg, then skip following END_FOR instruction */
+
JUMPBY(oparg + 1);
DISPATCH();
}
_PyStackRef iterable;
_PyStackRef iter;
iterable = stack_pointer[-1];
- /* before: [obj]; after [getiter(obj)] */
_PyFrame_SetStackPointer(frame, stack_pointer);
PyObject *iter_o = PyObject_GetIter(PyStackRef_AsPyObjectBorrow(iterable));
stack_pointer = _PyFrame_GetStackPointer(frame);
_PyStackRef obj;
_PyStackRef len;
obj = stack_pointer[-1];
- // PUSH(len(TOS))
_PyFrame_SetStackPointer(frame, stack_pointer);
Py_ssize_t len_i = PyObject_Length(PyStackRef_AsPyObjectBorrow(obj));
stack_pointer = _PyFrame_GetStackPointer(frame);
_PyStackRef iterable;
_PyStackRef iter;
iterable = stack_pointer[-1];
- /* before: [obj]; after [getiter(obj)] */
PyObject *iterable_o = PyStackRef_AsPyObjectBorrow(iterable);
if (PyCoro_CheckExact(iterable_o)) {
- /* `iterable` is a coroutine */
if (!(_PyFrame_GetCode(frame)->co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE))) {
- /* and it is used in a 'yield from' expression of a
- regular generator. */
_PyFrame_SetStackPointer(frame, stack_pointer);
_PyErr_SetString(tstate, PyExc_TypeError,
"cannot 'yield from' a coroutine object "
}
iter = iterable;
}
+ else if (PyGen_CheckExact(iterable_o)) {
+ iter = iterable;
+ }
else {
- if (PyGen_CheckExact(iterable_o)) {
- iter = iterable;
- }
- else {
- /* `iterable` is not a generator. */
- _PyFrame_SetStackPointer(frame, stack_pointer);
- PyObject *iter_o = PyObject_GetIter(iterable_o);
- stack_pointer = _PyFrame_GetStackPointer(frame);
- if (iter_o == NULL) {
- JUMP_TO_LABEL(error);
- }
- iter = PyStackRef_FromPyObjectSteal(iter_o);
- _PyFrame_SetStackPointer(frame, stack_pointer);
- _PyStackRef tmp = iterable;
- iterable = iter;
- stack_pointer[-1] = iterable;
- PyStackRef_CLOSE(tmp);
- stack_pointer = _PyFrame_GetStackPointer(frame);
+ _PyFrame_SetStackPointer(frame, stack_pointer);
+ PyObject *iter_o = PyObject_GetIter(iterable_o);
+ stack_pointer = _PyFrame_GetStackPointer(frame);
+ if (iter_o == NULL) {
+ JUMP_TO_LABEL(error);
}
+ iter = PyStackRef_FromPyObjectSteal(iter_o);
+ _PyFrame_SetStackPointer(frame, stack_pointer);
+ _PyStackRef tmp = iterable;
+ iterable = iter;
+ stack_pointer[-1] = iterable;
+ PyStackRef_CLOSE(tmp);
+ stack_pointer = _PyFrame_GetStackPointer(frame);
}
stack_pointer[-1] = iter;
DISPATCH();
if (is_meth) {
arg0 = PyStackRef_AsPyObjectBorrow(maybe_self[0]);
}
+ else if (oparg) {
+ arg0 = PyStackRef_AsPyObjectBorrow(args[0]);
+ }
else {
- if (oparg) {
- arg0 = PyStackRef_AsPyObjectBorrow(args[0]);
- }
- else {
- arg0 = &_PyInstrumentation_MISSING;
- }
+ arg0 = &_PyInstrumentation_MISSING;
}
_PyFrame_SetStackPointer(frame, stack_pointer);
int err = _Py_call_instrumentation_2args(
self_or_null = maybe_self;
callable = func;
PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]);
- // oparg counts all of the args, but *not* self:
int total_args = oparg;
_PyStackRef *arguments = args;
if (!PyStackRef_IsNull(self_or_null[0])) {
arguments--;
total_args++;
}
- // Check if the call can be inlined or not
if (Py_TYPE(callable_o) == &PyFunction_Type &&
tstate->interp->eval_frame == NULL &&
((PyFunctionObject *)callable_o)->vectorcall == _PyFunction_Vectorcall)
arguments, total_args, NULL, frame
);
stack_pointer = _PyFrame_GetStackPointer(frame);
- // Manipulate stack directly since we leave using DISPATCH_INLINED().
stack_pointer += -2 - oparg;
assert(WITHIN_STACK_BOUNDS());
- // The frame has stolen all the arguments from the stack,
- // so there is no need to clean them up.
if (new_frame == NULL) {
JUMP_TO_LABEL(error);
}
frame->return_offset = 4 ;
DISPATCH_INLINED(new_frame);
}
- /* Callable is not a normal Python function */
STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o);
if (CONVERSION_FAILED(args_o)) {
_PyFrame_SetStackPointer(frame, stack_pointer);
func_st = func;
(void)null;
PyObject *func = PyStackRef_AsPyObjectBorrow(func_st);
- // DICT_MERGE is called before this opcode if there are kwargs.
- // It converts all dict subtypes in kwargs into regular dicts.
EVAL_CALL_STAT_INC_IF_FUNCTION(EVAL_CALL_FUNCTION_EX, func);
PyObject *result_o;
assert(!_PyErr_Occurred(tstate));
tstate, func_st, locals,
nargs, callargs, kwargs, frame);
stack_pointer = _PyFrame_GetStackPointer(frame);
- // Need to sync the stack since we exit with DISPATCH_INLINED.
stack_pointer += -2;
assert(WITHIN_STACK_BOUNDS());
if (new_frame == NULL) {
if (is_meth) {
arg = PyStackRef_AsPyObjectBorrow(self_or_null[0]);
}
+ else if (args) {
+ arg = PyStackRef_AsPyObjectBorrow(args[0]);
+ }
else {
- if (args) {
- arg = PyStackRef_AsPyObjectBorrow(args[0]);
- }
- else {
- arg = &_PyInstrumentation_MISSING;
- }
+ arg = &_PyInstrumentation_MISSING;
}
PyObject *function = PyStackRef_AsPyObjectBorrow(callable[0]);
stack_pointer[-1] = kwnames_out;
kwnames = kwnames_out;
PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]);
PyObject *kwnames_o = PyStackRef_AsPyObjectBorrow(kwnames);
- // oparg counts all of the args, but *not* self:
int total_args = oparg;
_PyStackRef *arguments = args;
if (!PyStackRef_IsNull(self_or_null[0])) {
total_args++;
}
int positional_args = total_args - (int)PyTuple_GET_SIZE(kwnames_o);
- // Check if the call can be inlined or not
if (Py_TYPE(callable_o) == &PyFunction_Type &&
tstate->interp->eval_frame == NULL &&
((PyFunctionObject *)callable_o)->vectorcall == _PyFunction_Vectorcall)
_PyFrame_SetStackPointer(frame, stack_pointer);
PyStackRef_CLOSE(kwnames);
stack_pointer = _PyFrame_GetStackPointer(frame);
- // Sync stack explicitly since we leave using DISPATCH_INLINED().
- // The frame has stolen all the arguments from the stack,
- // so there is no need to clean them up.
if (new_frame == NULL) {
JUMP_TO_LABEL(error);
}
frame->return_offset = 4 ;
DISPATCH_INLINED(new_frame);
}
- /* Callable is not a normal Python function */
STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o);
if (CONVERSION_FAILED(args_o)) {
_PyFrame_SetStackPointer(frame, stack_pointer);
{
exc_st = stack_pointer[-1];
awaitable_st = stack_pointer[-2];
- JUMPBY(0); // Pretend jump as we need source offset for monitoring
+ JUMPBY(0);
(void)oparg;
PyObject *exc = PyStackRef_AsPyObjectBorrow(exc_st);
assert(exc && PyExceptionInstance_Check(exc));
_PyStackRef value;
value = stack_pointer[-1];
receiver = stack_pointer[-2];
- /* Need to create a fake StopIteration error here,
- * to conform to PEP 380 */
if (PyStackRef_GenCheck(receiver)) {
_PyFrame_SetStackPointer(frame, stack_pointer);
int err = monitor_stop_iteration(tstate, frame, this_instr, PyStackRef_AsPyObjectBorrow(value));
_PyErr_Clear(tstate);
stack_pointer = _PyFrame_GetStackPointer(frame);
}
- /* iterator ended normally */
assert(next_instr[oparg].op.code == END_FOR ||
next_instr[oparg].op.code == INSTRUMENTED_END_FOR);
- /* Skip END_FOR */
JUMPBY(oparg + 1);
}
DISPATCH();
}
if (_PyOpcode_Caches[original_opcode]) {
_PyBinaryOpCache *cache = (_PyBinaryOpCache *)(next_instr+1);
- /* Prevent the underlying instruction from specializing
- * and overwriting the instrumentation. */
PAUSE_ADAPTIVE_COUNTER(cache->counter);
}
opcode = original_opcode;
JUMP_TO_LABEL(error);
}
}
- // we make no attempt to optimize here; specializations should
- // handle any case whose performance we care about
PyObject *stack[] = {class, self};
_PyFrame_SetStackPointer(frame, stack_pointer);
PyObject *super = PyObject_Vectorcall(global_super, stack, oparg & 2, NULL);
frame->instr_ptr = next_instr;
next_instr += 1;
INSTRUCTION_STATS(INSTRUMENTED_NOT_TAKEN);
- (void)this_instr; // INSTRUMENTED_JUMP requires this_instr
+ (void)this_instr;
INSTRUMENTED_JUMP(prev_instr, next_instr, PY_MONITORING_EVENT_BRANCH_LEFT);
DISPATCH();
}
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
- // follow
next_instr = frame->instr_ptr;
DISPATCH();
}
{
if ((oparg & RESUME_OPARG_LOCATION_MASK) < RESUME_AFTER_YIELD_FROM) {
_Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY();
- QSBR_QUIESCENT_STATE(tstate); \
+ QSBR_QUIESCENT_STATE(tstate);
if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) {
_PyFrame_SetStackPointer(frame, stack_pointer);
int err = _Py_HandlePending(tstate);
JUMP_TO_LABEL(error);
}
if (frame->instr_ptr != this_instr) {
- /* Instrumentation has jumped */
next_instr = frame->instr_ptr;
}
}
_PyFrame_SetStackPointer(frame, stack_pointer);
assert(EMPTY());
_Py_LeaveRecursiveCallPy(tstate);
- // GH-99729: We need to unlink the frame *before* clearing it:
_PyInterpreterFrame *dying = frame;
frame = tstate->current_frame = dying->previous;
_PyEval_FrameClearAndPop(tstate, dying);
// _YIELD_VALUE
{
retval = val;
- // NOTE: It's important that YIELD_VALUE never raises an exception!
- // The compiler treats any exception raised here as a failed close()
- // or throw() call.
assert(frame->owner != FRAME_OWNED_BY_INTERPRETER);
frame->instr_ptr++;
PyGenObject *gen = _PyGen_GetGeneratorFromFrame(frame);
_PyInterpreterFrame *gen_frame = frame;
frame = tstate->current_frame = frame->previous;
gen_frame->previous = NULL;
- /* We don't know which of these is relevant here, so keep them equal */
assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER);
#if TIER_ONE
assert(frame->instr_ptr->op.code == INSTRUMENTED_LINE ||
retval = stack_pointer[-1];
assert(frame->owner == FRAME_OWNED_BY_INTERPRETER);
assert(_PyFrame_IsIncomplete(frame));
- /* Restore previous frame and return. */
tstate->current_frame = frame->previous;
assert(!_PyErr_Occurred(tstate));
PyObject *result = PyStackRef_AsPyObjectSteal(retval);
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
- /* Not strictly necessary, but prevents warnings */
return result;
}
#if ENABLE_SPECIALIZATION
if (this_instr->op.code == JUMP_BACKWARD) {
this_instr->op.code = tstate->interp->jit ? JUMP_BACKWARD_JIT : JUMP_BACKWARD_NO_JIT;
- // Need to re-dispatch so the warmup counter isn't off by one:
next_instr = this_instr;
DISPATCH_SAME_OPARG();
}
}
// _JUMP_BACKWARD_NO_INTERRUPT
{
- /* This bytecode is used in the `yield from` or `await` loop.
- * If there is an interrupt, we want it handled in the innermost
- * generator or coroutine, so we deliberately do not check it here.
- * (see bpo-30039).
- */
assert(oparg <= INSTR_OFFSET());
JUMPBY(-oparg);
}
}
// _JUMP_BACKWARD_NO_INTERRUPT
{
- /* This bytecode is used in the `yield from` or `await` loop.
- * If there is an interrupt, we want it handled in the innermost
- * generator or coroutine, so we deliberately do not check it here.
- * (see bpo-30039).
- */
assert(oparg <= INSTR_OFFSET());
JUMPBY(-oparg);
}
_Py_BackoffCounter counter = this_instr[1].counter;
if (backoff_counter_triggers(counter) && this_instr->op.code == JUMP_BACKWARD_JIT) {
_Py_CODEUNIT *start = this_instr;
- /* Back up over EXTENDED_ARGs so optimizer sees the whole instruction */
while (oparg > 255) {
oparg >>= 8;
start--;
frame->instr_ptr = next_instr;
next_instr += 1;
INSTRUCTION_STATS(JUMP_BACKWARD_NO_INTERRUPT);
- /* This bytecode is used in the `yield from` or `await` loop.
- * If there is an interrupt, we want it handled in the innermost
- * generator or coroutine, so we deliberately do not check it here.
- * (see bpo-30039).
- */
assert(oparg <= INSTR_OFFSET());
JUMPBY(-oparg);
DISPATCH();
}
// _JUMP_BACKWARD_NO_INTERRUPT
{
- /* This bytecode is used in the `yield from` or `await` loop.
- * If there is an interrupt, we want it handled in the innermost
- * generator or coroutine, so we deliberately do not check it here.
- * (see bpo-30039).
- */
assert(oparg <= INSTR_OFFSET());
JUMPBY(-oparg);
}
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 1);
PyObject *attr_o;
if (oparg & 1) {
- /* Designed to work in tandem with CALL, pushes two values. */
attr_o = NULL;
_PyFrame_SetStackPointer(frame, stack_pointer);
int is_meth = _PyObject_GetMethod(PyStackRef_AsPyObjectBorrow(owner), name, &attr_o);
stack_pointer = _PyFrame_GetStackPointer(frame);
if (is_meth) {
- /* We can bypass temporary bound method object.
- meth is unbound method and obj is self.
- meth | self | arg1 | ... | argN
- */
- assert(attr_o != NULL); // No errors on this branch
- self_or_null[0] = owner; // Transfer ownership
+ assert(attr_o != NULL);
+ self_or_null[0] = owner;
}
else {
- /* meth is not an unbound method (but a regular attr, or
- something was returned by a descriptor protocol). Set
- the second element of the stack to NULL, to signal
- CALL that it's not a method call.
- meth | NULL | arg1 | ... | argN
- */
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
_PyFrame_SetStackPointer(frame, stack_pointer);
}
}
else {
- /* Classic, pushes one value. */
_PyFrame_SetStackPointer(frame, stack_pointer);
attr_o = PyObject_GetAttr(PyStackRef_AsPyObjectBorrow(owner), name);
stack_pointer = _PyFrame_GetStackPointer(frame);
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;
new_frame->localsplus[1] = PyStackRef_FromPyObjectNew(name);
JUMP_TO_PREDICTED(LOAD_ATTR);
}
#ifdef Py_GIL_DISABLED
- if (!_Py_TryIncrefCompareStackRef(value_ptr, attr_o, &attr)) {
+ int increfed = _Py_TryIncrefCompareStackRef(value_ptr, attr_o, &attr);
+ if (!increfed) {
if (true) {
UPDATE_MISS_STATS(LOAD_ATTR);
assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR));
uint16_t dictoffset = read_u16(&this_instr[4].cache);
char *ptr = ((char *)PyStackRef_AsPyObjectBorrow(owner)) + MANAGED_DICT_OFFSET + dictoffset;
PyObject *dict = FT_ATOMIC_LOAD_PTR_ACQUIRE(*(PyObject **)ptr);
- /* This object has a __dict__, just not yet created */
if (dict != NULL) {
UPDATE_MISS_STATS(LOAD_ATTR);
assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR));
{
PyObject *descr = read_obj(&this_instr[6].cache);
assert(oparg & 1);
- /* Cached method object */
STAT_INC(LOAD_ATTR, hit);
assert(descr != NULL);
assert(_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR));
}
// _PUSH_FRAME
{
- // Write it out explicitly because it's subtly different.
- // Eventually this should be the only occurrence of this code.
assert(tstate->interp->eval_frame == NULL);
_PyInterpreterFrame *temp = new_frame;
stack_pointer += -1;
}
STAT_INC(LOAD_ATTR, hit);
#ifdef Py_GIL_DISABLED
- if (!_Py_TryIncrefCompareStackRef(&ep->me_value, attr_o, &attr)) {
+ int increfed = _Py_TryIncrefCompareStackRef(&ep->me_value, attr_o, &attr);
+ if (!increfed) {
if (true) {
UPDATE_MISS_STATS(LOAD_ATTR);
assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR));
next_instr += 1;
INSTRUCTION_STATS(LOAD_COMMON_CONSTANT);
_PyStackRef value;
- // Keep in sync with _common_constants in opcode.py
assert(oparg < NUM_COMMON_CONSTANTS);
value = PyStackRef_FromPyObjectNew(tstate->interp->common_consts[oparg]);
stack_pointer[0] = value;
_Py_CODEUNIT* const this_instr = next_instr - 1;
(void)this_instr;
_PyStackRef value;
- /* We can't do this in the bytecode compiler as
- * marshalling can intern strings and make them immortal. */
PyObject *obj = GETITEM(FRAME_CO_CONSTS, oparg);
value = PyStackRef_FromPyObjectNew(obj);
#if ENABLE_SPECIALIZATION_FT
if (!_Py_atomic_compare_exchange_uint8(
&this_instr->op.code, &expected,
_Py_IsImmortal(obj) ? LOAD_CONST_IMMORTAL : LOAD_CONST_MORTAL)) {
- // We might lose a race with instrumentation, which we don't care about.
assert(expected >= MIN_INSTRUMENTED_OPCODE);
}
#else
stack_pointer = _PyFrame_GetStackPointer(frame);
if (v_o == NULL) {
if (!_PyErr_Occurred(tstate)) {
- /* _PyDict_LoadGlobal() returns NULL without raising
- * an exception if the key doesn't exist */
_PyFrame_SetStackPointer(frame, stack_pointer);
_PyEval_FormatExcCheckArg(tstate, PyExc_NameError,
NAME_ERROR_MSG, name);
}
}
else {
- /* Slow-path if globals or builtins is not a dict */
- /* namespace 1: globals */
_PyFrame_SetStackPointer(frame, stack_pointer);
int err = PyMapping_GetOptionalItem(GLOBALS(), name, &v_o);
stack_pointer = _PyFrame_GetStackPointer(frame);
JUMP_TO_LABEL(error);
}
if (v_o == NULL) {
- /* namespace 2: builtins */
_PyFrame_SetStackPointer(frame, stack_pointer);
int err = PyMapping_GetOptionalItem(BUILTINS(), name, &v_o);
stack_pointer = _PyFrame_GetStackPointer(frame);
JUMP_TO_LABEL(error);
}
}
- // we make no attempt to optimize here; specializations should
- // handle any case whose performance we care about
PyObject *stack[] = {class, self};
_PyFrame_SetStackPointer(frame, stack_pointer);
PyObject *super = PyObject_Vectorcall(global_super, stack, oparg & 2, NULL);
JUMP_TO_LABEL(error);
}
if (method_found) {
- self_or_null = self_st; // transfer ownership
+ self_or_null = self_st;
} else {
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
frame->instr_ptr = next_instr;
next_instr += 1;
INSTRUCTION_STATS(MAKE_CELL);
- // "initial" is probably NULL but not if it's an arg (or set
- // via the f_locals proxy before MAKE_CELL has run).
PyObject *initial = PyStackRef_AsPyObjectBorrow(GETLOCAL(oparg));
PyObject *cell = PyCell_New(initial);
if (cell == NULL) {
dict_st = stack_pointer[-3 - (oparg - 1)];
PyObject *dict = PyStackRef_AsPyObjectBorrow(dict_st);
assert(PyDict_CheckExact(dict));
- /* dict[key] = value */
- // Do not DECREF INPUTS because the function steals the references
_PyFrame_SetStackPointer(frame, stack_pointer);
int err = _PyDict_SetItem_Take2(
(PyDictObject *)dict,
names = stack_pointer[-1];
type = stack_pointer[-2];
subject = stack_pointer[-3];
- // Pop TOS and TOS1. Set TOS to a tuple of attributes on success, or
- // None on failure.
assert(PyTuple_CheckExact(PyStackRef_AsPyObjectBorrow(names)));
_PyFrame_SetStackPointer(frame, stack_pointer);
PyObject *attrs_o = _PyEval_MatchClass(tstate,
stack_pointer += -3;
assert(WITHIN_STACK_BOUNDS());
if (attrs_o) {
- assert(PyTuple_CheckExact(attrs_o)); // Success!
+ assert(PyTuple_CheckExact(attrs_o));
attrs = PyStackRef_FromPyObjectSteal(attrs_o);
}
else {
if (_PyErr_Occurred(tstate)) {
JUMP_TO_LABEL(error);
}
- // Error!
- attrs = PyStackRef_None; // Failure!
+ attrs = PyStackRef_None;
}
stack_pointer[0] = attrs;
stack_pointer += 1;
_PyStackRef values_or_none;
keys = stack_pointer[-1];
subject = stack_pointer[-2];
- // On successful match, PUSH(values). Otherwise, PUSH(None).
_PyFrame_SetStackPointer(frame, stack_pointer);
PyObject *values_or_none_o = _PyEval_MatchKeys(tstate,
PyStackRef_AsPyObjectBorrow(subject), PyStackRef_AsPyObjectBorrow(keys));
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
- // follow
next_instr = frame->instr_ptr;
DISPATCH();
}
{
if ((oparg & RESUME_OPARG_LOCATION_MASK) < RESUME_AFTER_YIELD_FROM) {
_Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY();
- QSBR_QUIESCENT_STATE(tstate); \
+ QSBR_QUIESCENT_STATE(tstate);
if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) {
_PyFrame_SetStackPointer(frame, stack_pointer);
int err = _Py_HandlePending(tstate);
_PyFrame_SetStackPointer(frame, stack_pointer);
assert(EMPTY());
_Py_LeaveRecursiveCallPy(tstate);
- // GH-99729: We need to unlink the frame *before* clearing it:
_PyInterpreterFrame *dying = frame;
frame = tstate->current_frame = dying->previous;
_PyEval_FrameClearAndPop(tstate, dying);
// _PUSH_FRAME
{
new_frame = gen_frame;
- // Write it out explicitly because it's subtly different.
- // Eventually this should be the only occurrence of this code.
assert(tstate->interp->eval_frame == NULL);
_PyInterpreterFrame *temp = new_frame;
stack_pointer += -1;
stack_pointer = _PyFrame_GetStackPointer(frame);
JUMP_TO_LABEL(error);
}
- /* check if __annotations__ in locals()... */
_PyFrame_SetStackPointer(frame, stack_pointer);
int err = PyMapping_GetOptionalItem(LOCALS(), &_Py_ID(__annotations__), &ann_dict);
stack_pointer = _PyFrame_GetStackPointer(frame);
stack_pointer = _PyFrame_GetStackPointer(frame);
FT_ATOMIC_STORE_PTR_RELEASE(ep->me_value, PyStackRef_AsPyObjectSteal(value));
UNLOCK_OBJECT(dict);
- // old_value should be DECREFed after GC track checking is done, if not, it could raise a segmentation fault,
- // when dict only holds the strong reference to value in ep->me_value.
STAT_INC(STORE_ATTR, hit);
stack_pointer += -2;
assert(WITHIN_STACK_BOUNDS());
_PyStackRef stop;
// _SPECIALIZE_STORE_SLICE
{
- // Placeholder until we implement STORE_SLICE specialization
#if ENABLE_SPECIALIZATION
OPCODE_DEFERRED_INC(STORE_SLICE);
#endif /* ENABLE_SPECIALIZATION */
// _STORE_SUBSCR
{
v = stack_pointer[-3];
- /* container[sub] = v */
_PyFrame_SetStackPointer(frame, stack_pointer);
int err = PyObject_SetItem(PyStackRef_AsPyObjectBorrow(container), PyStackRef_AsPyObjectBorrow(sub), PyStackRef_AsPyObjectBorrow(v));
_PyStackRef tmp = sub;
assert(_PyOpcode_Deopt[opcode] == (STORE_SUBSCR));
JUMP_TO_PREDICTED(STORE_SUBSCR);
}
- // Ensure nonnegative, zero-or-one-digit ints.
if (!_PyLong_IsNonNegativeCompact((PyLongObject *)sub)) {
UPDATE_MISS_STATS(STORE_SUBSCR);
assert(_PyOpcode_Deopt[opcode] == (STORE_SUBSCR));
assert(_PyOpcode_Deopt[opcode] == (STORE_SUBSCR));
JUMP_TO_PREDICTED(STORE_SUBSCR);
}
- // Ensure index < len(list)
if (index >= PyList_GET_SIZE(list)) {
UNLOCK_OBJECT(list);
if (true) {
FT_ATOMIC_STORE_PTR_RELEASE(_PyList_ITEMS(list)[index],
PyStackRef_AsPyObjectSteal(value));
assert(old_value != NULL);
- UNLOCK_OBJECT(list); // unlock before decrefs!
+ UNLOCK_OBJECT(list);
PyStackRef_CLOSE_SPECIALIZED(sub_st, _PyLong_ExactDealloc);
stack_pointer += -3;
assert(WITHIN_STACK_BOUNDS());
/* Skip 1 cache entry */
/* Skip 2 cache entries */
value = stack_pointer[-1];
- // This one is a bit weird, because we expect *some* failures:
if (!PyStackRef_IsNone(value)) {
UPDATE_MISS_STATS(TO_BOOL);
assert(_PyOpcode_Deopt[opcode] == (TO_BOOL));
lasti = stack_pointer[-3];
exit_self = stack_pointer[-4];
exit_func = stack_pointer[-5];
- /* At the top of the stack are 4 values:
- - val: TOP = exc_info()
- - unused: SECOND = previous exception
- - lasti: THIRD = lasti of exception in exc_info()
- - exit_self: FOURTH = the context or NULL
- - exit_func: FIFTH = the context.__exit__ function or context.__exit__ bound method
- We call FOURTH(type(TOP), TOP, GetTraceback(TOP)).
- Then we push the __exit__ return value.
- */
PyObject *exc, *tb;
PyObject *val_o = PyStackRef_AsPyObjectBorrow(val);
PyObject *exit_func_o = PyStackRef_AsPyObjectBorrow(exit_func);
tb = Py_None;
}
assert(PyStackRef_LongCheck(lasti));
- (void)lasti; // Shut up compiler warning if asserts are off
+ (void)lasti;
PyObject *stack[5] = {NULL, PyStackRef_AsPyObjectBorrow(exit_self), exc, val_o, tb};
int has_self = !PyStackRef_IsNull(exit_self);
_PyFrame_SetStackPointer(frame, stack_pointer);
_PyStackRef retval;
_PyStackRef value;
retval = stack_pointer[-1];
- // NOTE: It's important that YIELD_VALUE never raises an exception!
- // The compiler treats any exception raised here as a failed close()
- // or throw() call.
assert(frame->owner != FRAME_OWNED_BY_INTERPRETER);
frame->instr_ptr++;
PyGenObject *gen = _PyGen_GetGeneratorFromFrame(frame);
_PyInterpreterFrame *gen_frame = frame;
frame = tstate->current_frame = frame->previous;
gen_frame->previous = NULL;
- /* We don't know which of these is relevant here, so keep them equal */
assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER);
#if TIER_ONE
assert(frame->instr_ptr->op.code == INSTRUMENTED_LINE ||
LABEL(error)
{
- /* Double-check exception status. */
#ifdef NDEBUG
if (!_PyErr_Occurred(tstate)) {
_PyFrame_SetStackPointer(frame, stack_pointer);
assert(_PyErr_Occurred(tstate));
#endif
- /* Log traceback info. */
assert(frame->owner != FRAME_OWNED_BY_INTERPRETER);
if (!_PyFrame_IsIncomplete(frame)) {
_PyFrame_SetStackPointer(frame, stack_pointer);
LABEL(exception_unwind)
{
- /* STACK SPILLED */
- /* We can't use frame->instr_ptr here, as RERAISE may have set it */
int offset = INSTR_OFFSET()-1;
int level, handler, lasti;
int handled = get_exception_handler(_PyFrame_GetCode(frame), offset, &level, &handler, &lasti);
if (handled == 0) {
- // No handlers, so exit.
assert(_PyErr_Occurred(tstate));
- /* Pop remaining stack entries. */
_PyStackRef *stackbase = _PyFrame_Stackbase(frame);
while (frame->stackpointer > stackbase) {
_PyStackRef ref = _PyFrame_StackPop(frame);
}
_PyFrame_StackPush(frame, PyStackRef_FromPyObjectSteal(lasti));
}
- /* Make the raw exception data
- available to the handler,
- so a program can emulate the
- Python main loop. */
PyObject *exc = _PyErr_GetRaisedException(tstate);
_PyFrame_StackPush(frame, PyStackRef_FromPyObjectSteal(exc));
next_instr = _PyFrame_GetBytecode(frame) + handler;
if (err < 0) {
JUMP_TO_LABEL(exception_unwind);
}
- /* Resume normal execution */
#ifdef Py_DEBUG
if (frame->lltrace >= 5) {
lltrace_resume_frame(frame);
LABEL(exit_unwind)
{
- /* STACK SPILLED */
assert(_PyErr_Occurred(tstate));
_Py_LeaveRecursiveCallPy(tstate);
assert(frame->owner != FRAME_OWNED_BY_INTERPRETER);
- // GH-99729: We need to unlink the frame *before* clearing it:
_PyInterpreterFrame *dying = frame;
frame = tstate->current_frame = dying->previous;
_PyEval_FrameClearAndPop(tstate, dying);
frame->return_offset = 0;
if (frame->owner == FRAME_OWNED_BY_INTERPRETER) {
- /* Restore previous frame and exit */
tstate->current_frame = frame->previous;
return NULL;
}
LABEL(start_frame)
{
- /* STACK SPILLED */
int too_deep = _Py_EnterRecursivePy(tstate);
if (too_deep) {
JUMP_TO_LABEL(exit_unwind);
JUMP_TO_LABEL(exit_unwind);
}
frame->lltrace = lltrace;
- /* _PyEval_EvalFrameDefault() must not be called with an exception set,
- because it can clear it (directly or indirectly) and so the
- caller loses its exception */
assert(!_PyErr_Occurred(tstate));
#endif
stack_pointer = _PyFrame_GetStackPointer(frame);
}
op(_TO_BOOL, (value -- res)) {
- if (!optimize_to_bool(this_instr, ctx, value, &res)) {
+ int already_bool = optimize_to_bool(this_instr, ctx, value, &res);
+ if (!already_bool) {
res = sym_new_truthiness(ctx, value, true);
}
}
op(_TO_BOOL_BOOL, (value -- res)) {
- if (!optimize_to_bool(this_instr, ctx, value, &res)) {
+ int already_bool = optimize_to_bool(this_instr, ctx, value, &res);
+ if (!already_bool) {
sym_set_type(value, &PyBool_Type);
res = sym_new_truthiness(ctx, value, true);
}
}
op(_TO_BOOL_INT, (value -- res)) {
- if (!optimize_to_bool(this_instr, ctx, value, &res)) {
+ int already_bool = optimize_to_bool(this_instr, ctx, value, &res);
+ if (!already_bool) {
sym_set_type(value, &PyLong_Type);
res = sym_new_truthiness(ctx, value, true);
}
}
op(_TO_BOOL_LIST, (value -- res)) {
- if (!optimize_to_bool(this_instr, ctx, value, &res)) {
+ int already_bool = optimize_to_bool(this_instr, ctx, value, &res);
+ if (!already_bool) {
sym_set_type(value, &PyList_Type);
res = sym_new_type(ctx, &PyBool_Type);
}
}
op(_TO_BOOL_NONE, (value -- res)) {
- if (!optimize_to_bool(this_instr, ctx, value, &res)) {
+ int already_bool = optimize_to_bool(this_instr, ctx, value, &res);
+ if (!already_bool) {
sym_set_const(value, Py_None);
res = sym_new_const(ctx, Py_False);
}
}
op(_TO_BOOL_STR, (value -- res)) {
- if (!optimize_to_bool(this_instr, ctx, value, &res)) {
+ int already_bool = optimize_to_bool(this_instr, ctx, value, &res);
+ if (!already_bool) {
res = sym_new_truthiness(ctx, value, true);
}
}
case _LOAD_FAST_CHECK: {
JitOptSymbol *value;
value = GETLOCAL(oparg);
- // We guarantee this will error - just bail and don't optimize it.
if (sym_is_null(value)) {
ctx->done = true;
}
JitOptSymbol *value;
JitOptSymbol *res;
value = stack_pointer[-1];
- if (!optimize_to_bool(this_instr, ctx, value, &res)) {
+ int already_bool = optimize_to_bool(this_instr, ctx, value, &res);
+ if (!already_bool) {
res = sym_new_truthiness(ctx, value, true);
}
stack_pointer[-1] = res;
JitOptSymbol *value;
JitOptSymbol *res;
value = stack_pointer[-1];
- if (!optimize_to_bool(this_instr, ctx, value, &res)) {
+ int already_bool = optimize_to_bool(this_instr, ctx, value, &res);
+ if (!already_bool) {
sym_set_type(value, &PyBool_Type);
res = sym_new_truthiness(ctx, value, true);
}
JitOptSymbol *value;
JitOptSymbol *res;
value = stack_pointer[-1];
- if (!optimize_to_bool(this_instr, ctx, value, &res)) {
+ int already_bool = optimize_to_bool(this_instr, ctx, value, &res);
+ if (!already_bool) {
sym_set_type(value, &PyLong_Type);
res = sym_new_truthiness(ctx, value, true);
}
JitOptSymbol *value;
JitOptSymbol *res;
value = stack_pointer[-1];
- if (!optimize_to_bool(this_instr, ctx, value, &res)) {
+ int already_bool = optimize_to_bool(this_instr, ctx, value, &res);
+ if (!already_bool) {
sym_set_type(value, &PyList_Type);
res = sym_new_type(ctx, &PyBool_Type);
}
JitOptSymbol *value;
JitOptSymbol *res;
value = stack_pointer[-1];
- if (!optimize_to_bool(this_instr, ctx, value, &res)) {
+ int already_bool = optimize_to_bool(this_instr, ctx, value, &res);
+ if (!already_bool) {
sym_set_const(value, Py_None);
res = sym_new_const(ctx, Py_False);
}
JitOptSymbol *value;
JitOptSymbol *res;
value = stack_pointer[-1];
- if (!optimize_to_bool(this_instr, ctx, value, &res)) {
+ int already_bool = optimize_to_bool(this_instr, ctx, value, &res);
+ if (!already_bool) {
res = sym_new_truthiness(ctx, value, true);
}
stack_pointer[-1] = res;
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
Py_DECREF(temp);
- // TODO gh-115506:
- // replace opcode with constant propagated one and add tests!
}
else {
res = sym_new_type(ctx, &PyLong_Type);
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
Py_DECREF(temp);
- // TODO gh-115506:
- // replace opcode with constant propagated one and add tests!
}
else {
res = sym_new_type(ctx, &PyLong_Type);
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
Py_DECREF(temp);
- // TODO gh-115506:
- // replace opcode with constant propagated one and add tests!
}
else {
res = sym_new_type(ctx, &PyLong_Type);
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
Py_DECREF(temp);
- // TODO gh-115506:
- // replace opcode with constant propagated one and update tests!
}
else {
res = sym_new_type(ctx, &PyFloat_Type);
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
Py_DECREF(temp);
- // TODO gh-115506:
- // replace opcode with constant propagated one and update tests!
}
else {
res = sym_new_type(ctx, &PyFloat_Type);
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
Py_DECREF(temp);
- // TODO gh-115506:
- // replace opcode with constant propagated one and update tests!
}
else {
res = sym_new_type(ctx, &PyFloat_Type);
else {
res = sym_new_type(ctx, &PyUnicode_Type);
}
- // _STORE_FAST:
GETLOCAL(this_instr->operand0) = res;
stack_pointer += -2;
assert(WITHIN_STACK_BOUNDS());
ctx->frame->stack_pointer = stack_pointer;
frame_pop(ctx);
stack_pointer = ctx->frame->stack_pointer;
- /* Stack space handling */
assert(corresponding_check_stack == NULL);
assert(co != NULL);
int framesize = co->co_framesize;
curr_space -= framesize;
co = get_code(this_instr);
if (co == NULL) {
- // might be impossible, but bailing is still safe
ctx->done = true;
}
res = temp;
/* _SEND is not a viable micro-op for tier 2 */
case _SEND_GEN_FRAME: {
- // We are about to hit the end of the trace:
ctx->done = true;
break;
}
case _UNPACK_SEQUENCE: {
JitOptSymbol **values;
values = &stack_pointer[-1];
- /* This has to be done manually */
for (int i = 0; i < oparg; i++) {
values[i] = sym_new_unknown(ctx);
}
case _UNPACK_EX: {
JitOptSymbol **values;
values = &stack_pointer[-1];
- /* This has to be done manually */
int totalargs = (oparg & 0xFF) + (oparg >> 8) + 1;
for (int i = 0; i < totalargs; i++) {
values[i] = sym_new_unknown(ctx);
if (sym_matches_type_version(owner, type_version)) {
REPLACE_OP(this_instr, _NOP, 0, 0);
} else {
- // add watcher so that whenever the type changes we invalidate this
PyTypeObject *type = _PyType_LookupByVersion(type_version);
- // if the type is null, it was not found in the cache (there was a conflict)
- // with the key, in which case we can't trust the version
if (type) {
- // if the type version was set properly, then add a watcher
- // if it wasn't this means that the type version was previously set to something else
- // and we set the owner to bottom, so we don't need to add a watcher because we must have
- // already added one earlier.
if (sym_set_type_version(owner, type_version)) {
PyType_Watch(TYPE_WATCHER_ID, (PyObject *)type);
_Py_BloomFilter_Add(dependencies, type);
}
}
if (attr == NULL) {
- /* No conversion made. We don't know what `attr` is. */
attr = sym_new_not_null(ctx);
}
stack_pointer[-1] = attr;
}
case _FOR_ITER_GEN_FRAME: {
- /* We are about to hit the end of the trace */
ctx->done = true;
break;
}
}
case _CHECK_PEP_523: {
- /* Setting the eval frame function invalidates
- * all executors, so no need to check dynamically */
if (_PyInterpreterState_GET()->eval_frame == NULL) {
REPLACE_OP(this_instr, _NOP, 0 ,0);
}
assert(self_or_null != NULL);
assert(args != NULL);
if (sym_is_not_null(self_or_null)) {
- // Bound method fiddling, same as _INIT_CALL_PY_EXACT_ARGS in VM
args--;
argcount++;
}
stack_pointer = new_frame->stack_pointer;
co = get_code(this_instr);
if (co == NULL) {
- // should be about to _EXIT_TRACE anyway
ctx->done = true;
break;
}
- /* Stack space handling */
int framesize = co->co_framesize;
assert(framesize > 0);
curr_space += framesize;
if (curr_space < 0 || curr_space > INT32_MAX) {
- // won't fit in signed 32-bit int
ctx->done = true;
break;
}
if (first_valid_check_stack == NULL) {
first_valid_check_stack = corresponding_check_stack;
}
- else {
- if (corresponding_check_stack) {
- // delete all but the first valid _CHECK_STACK_SPACE
- corresponding_check_stack->opcode = _NOP;
- }
+ else if (corresponding_check_stack) {
+ corresponding_check_stack->opcode = _NOP;
}
corresponding_check_stack = NULL;
break;
frame_pop(ctx);
stack_pointer = ctx->frame->stack_pointer;
res = sym_new_unknown(ctx);
- /* Stack space handling */
assert(corresponding_check_stack == NULL);
assert(co != NULL);
int framesize = co->co_framesize;
assert(WITHIN_STACK_BOUNDS());
co = get_code(this_instr);
if (co == NULL) {
- // might be impossible, but bailing is still safe
ctx->done = true;
}
stack_pointer[-1] = res;
bool lhs_float = sym_matches_type(left, &PyFloat_Type);
bool rhs_float = sym_matches_type(right, &PyFloat_Type);
if (!((lhs_int || lhs_float) && (rhs_int || rhs_float))) {
- // There's something other than an int or float involved:
res = sym_new_unknown(ctx);
}
- else {
- if (oparg == NB_POWER || oparg == NB_INPLACE_POWER) {
- // This one's fun... the *type* of the result depends on the
- // *values* being exponentiated. However, exponents with one
- // constant part are reasonably common, so it's probably worth
- // trying to infer some simple cases:
- // - A: 1 ** 1 -> 1 (int ** int -> int)
- // - B: 1 ** -1 -> 1.0 (int ** int -> float)
- // - C: 1.0 ** 1 -> 1.0 (float ** int -> float)
- // - D: 1 ** 1.0 -> 1.0 (int ** float -> float)
- // - E: -1 ** 0.5 ~> 1j (int ** float -> complex)
- // - F: 1.0 ** 1.0 -> 1.0 (float ** float -> float)
- // - G: -1.0 ** 0.5 ~> 1j (float ** float -> complex)
- if (rhs_float) {
- // Case D, E, F, or G... can't know without the sign of the LHS
- // or whether the RHS is whole, which isn't worth the effort:
- res = sym_new_unknown(ctx);
- }
- else {
- if (lhs_float) {
- // Case C:
- res = sym_new_type(ctx, &PyFloat_Type);
- }
- else {
- if (!sym_is_const(ctx, right)) {
- // Case A or B... can't know without the sign of the RHS:
- res = sym_new_unknown(ctx);
- }
- else {
- if (_PyLong_IsNegative((PyLongObject *)sym_get_const(ctx, right))) {
- // Case B:
- res = sym_new_type(ctx, &PyFloat_Type);
- }
- else {
- // Case A:
- res = sym_new_type(ctx, &PyLong_Type);
- }
- }
- }
- }
+ else if (oparg == NB_POWER || oparg == NB_INPLACE_POWER) {
+ if (rhs_float) {
+ res = sym_new_unknown(ctx);
+ }
+ else if (lhs_float) {
+ res = sym_new_type(ctx, &PyFloat_Type);
+ }
+ else if (!sym_is_const(ctx, right)) {
+ res = sym_new_unknown(ctx);
+ }
+ else if (_PyLong_IsNegative((PyLongObject *)sym_get_const(ctx, right))) {
+ res = sym_new_type(ctx, &PyFloat_Type);
}
else {
- if (oparg == NB_TRUE_DIVIDE || oparg == NB_INPLACE_TRUE_DIVIDE) {
- res = sym_new_type(ctx, &PyFloat_Type);
- }
- else {
- if (lhs_int && rhs_int) {
- res = sym_new_type(ctx, &PyLong_Type);
- }
- else {
- res = sym_new_type(ctx, &PyFloat_Type);
- }
- }
+ res = sym_new_type(ctx, &PyLong_Type);
}
}
+ else if (oparg == NB_TRUE_DIVIDE || oparg == NB_INPLACE_TRUE_DIVIDE) {
+ res = sym_new_type(ctx, &PyFloat_Type);
+ }
+ else if (lhs_int && rhs_int) {
+ res = sym_new_type(ctx, &PyLong_Type);
+ }
+ else {
+ res = sym_new_type(ctx, &PyFloat_Type);
+ }
stack_pointer[-2] = res;
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
assert(value != NULL);
eliminate_pop_guard(this_instr, !Py_IsNone(value));
}
- else {
- if (sym_has_type(flag)) {
- assert(!sym_matches_type(flag, &_PyNone_Type));
- eliminate_pop_guard(this_instr, true);
- }
+ else if (sym_has_type(flag)) {
+ assert(!sym_matches_type(flag, &_PyNone_Type));
+ eliminate_pop_guard(this_instr, true);
}
sym_set_const(flag, Py_None);
stack_pointer += -1;
assert(value != NULL);
eliminate_pop_guard(this_instr, Py_IsNone(value));
}
- else {
- if (sym_has_type(flag)) {
- assert(!sym_matches_type(flag, &_PyNone_Type));
- eliminate_pop_guard(this_instr, false);
- }
+ else if (sym_has_type(flag)) {
+ assert(!sym_matches_type(flag, &_PyNone_Type));
+ eliminate_pop_guard(this_instr, false);
}
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
case _CHECK_STACK_SPACE_OPERAND: {
uint32_t framesize = (uint32_t)this_instr->operand0;
(void)framesize;
- /* We should never see _CHECK_STACK_SPACE_OPERANDs.
- * They are only created at the end of this pass. */
Py_UNREACHABLE();
break;
}
import lexer
import parser
import re
-from typing import Optional
+from typing import Optional, Callable
+
+from parser import Stmt, SimpleStmt, BlockStmt, IfStmt, WhileStmt
@dataclass
class EscapingCall:
- start: lexer.Token
+ stmt: SimpleStmt
call: lexer.Token
- end: lexer.Token
kills: lexer.Token | None
@dataclass
class Properties:
- escaping_calls: dict[lexer.Token, EscapingCall]
+ escaping_calls: dict[SimpleStmt, EscapingCall]
escapes: bool
error_with_pop: bool
error_without_pop: bool
@staticmethod
def from_list(properties: list["Properties"]) -> "Properties":
- escaping_calls: dict[lexer.Token, EscapingCall] = {}
+ escaping_calls: dict[SimpleStmt, EscapingCall] = {}
for p in properties:
escaping_calls.update(p.escaping_calls)
return Properties(
annotations: list[str]
stack: StackEffect
caches: list[CacheEntry]
- deferred_refs: dict[lexer.Token, str | None]
local_stores: list[lexer.Token]
- body: list[lexer.Token]
+ body: BlockStmt
properties: Properties
_size: int = -1
implicitly_created: bool = False
return self.why_not_viable() is None
def is_super(self) -> bool:
- for tkn in self.body:
+ for tkn in self.body.tokens():
if tkn.kind == "IDENTIFIER" and tkn.text == "oparg1":
return True
return False
class Label:
- def __init__(self, name: str, spilled: bool, body: list[lexer.Token], properties: Properties):
+ def __init__(self, name: str, spilled: bool, body: BlockStmt, properties: Properties):
self.name = name
self.spilled = spilled
self.body = body
return [CacheEntry(i.name, int(i.size)) for i in caches]
-def find_assignment_target(node: parser.InstDef, idx: int) -> list[lexer.Token]:
- """Find the tokens that make up the left-hand side of an assignment"""
- offset = 0
- for tkn in reversed(node.block.tokens[: idx]):
- if tkn.kind in {"SEMI", "LBRACE", "RBRACE", "CMACRO"}:
- return node.block.tokens[idx - offset : idx]
- offset += 1
- return []
-
-
def find_variable_stores(node: parser.InstDef) -> list[lexer.Token]:
res: list[lexer.Token] = []
outnames = { out.name for out in node.outputs }
innames = { out.name for out in node.inputs }
- for idx, tkn in enumerate(node.block.tokens):
- if tkn.kind == "AND":
- name = node.block.tokens[idx+1]
- if name.text in outnames:
- res.append(name)
- if tkn.kind != "EQUALS":
- continue
- lhs = find_assignment_target(node, idx)
- assert lhs
- while lhs and lhs[0].kind == "COMMENT":
- lhs = lhs[1:]
- if len(lhs) != 1 or lhs[0].kind != "IDENTIFIER":
- continue
- name = lhs[0]
- if name.text in outnames or name.text in innames:
- res.append(name)
- return res
-
-def analyze_deferred_refs(node: parser.InstDef) -> dict[lexer.Token, str | None]:
- """Look for PyStackRef_FromPyObjectNew() calls"""
-
- def in_frame_push(idx: int) -> bool:
- for tkn in reversed(node.block.tokens[: idx - 1]):
- if tkn.kind in {"SEMI", "LBRACE", "RBRACE"}:
- return False
- if tkn.kind == "IDENTIFIER" and tkn.text == "_PyFrame_PushUnchecked":
- return True
- return False
-
- refs: dict[lexer.Token, str | None] = {}
- for idx, tkn in enumerate(node.block.tokens):
- if tkn.kind != "IDENTIFIER" or tkn.text != "PyStackRef_FromPyObjectNew":
- continue
-
- if idx == 0 or node.block.tokens[idx - 1].kind != "EQUALS":
- if in_frame_push(idx):
- # PyStackRef_FromPyObjectNew() is called in _PyFrame_PushUnchecked()
- refs[tkn] = None
- continue
- raise analysis_error("Expected '=' before PyStackRef_FromPyObjectNew", tkn)
-
- lhs = find_assignment_target(node, idx - 1)
- if len(lhs) == 0:
- raise analysis_error(
- "PyStackRef_FromPyObjectNew() must be assigned to an output", tkn
- )
-
- if lhs[0].kind == "TIMES" or any(
- t.kind == "ARROW" or t.kind == "LBRACKET" for t in lhs[1:]
- ):
- # Don't handle: *ptr = ..., ptr->field = ..., or ptr[field] = ...
- # Assume that they are visible to the GC.
- refs[tkn] = None
- continue
- if len(lhs) != 1 or lhs[0].kind != "IDENTIFIER":
- raise analysis_error(
- "PyStackRef_FromPyObjectNew() must be assigned to an output", tkn
- )
-
- name = lhs[0].text
- match = (
- any(var.name == name for var in node.inputs)
- or any(var.name == name for var in node.outputs)
- )
- if not match:
- raise analysis_error(
- f"PyStackRef_FromPyObjectNew() must be assigned to an input or output, not '{name}'",
- tkn,
- )
+ def find_stores_in_tokens(tokens: list[lexer.Token], callback: Callable[[lexer.Token], None]) -> None:
+ while tokens and tokens[0].kind == "COMMENT":
+ tokens = tokens[1:]
+ if len(tokens) < 4:
+ return
+ if tokens[1].kind == "EQUALS":
+ if tokens[0].kind == "IDENTIFIER":
+ name = tokens[0].text
+ if name in outnames or name in innames:
+ callback(tokens[0])
+ #Passing the address of a local is also a definition
+ for idx, tkn in enumerate(tokens):
+ if tkn.kind == "AND":
+ name_tkn = tokens[idx+1]
+ if name_tkn.text in outnames:
+ callback(name_tkn)
+
+ def visit(stmt: Stmt) -> None:
+ if isinstance(stmt, IfStmt):
+ def error(tkn: lexer.Token) -> None:
+ raise analysis_error("Cannot define variable in 'if' condition", tkn)
+ find_stores_in_tokens(stmt.condition, error)
+ elif isinstance(stmt, SimpleStmt):
+ find_stores_in_tokens(stmt.contents, res.append)
+
+ node.block.accept(visit)
+ return res
- refs[tkn] = name
- return refs
+#def analyze_deferred_refs(node: parser.InstDef) -> dict[lexer.Token, str | None]:
+ #"""Look for PyStackRef_FromPyObjectNew() calls"""
+
+ #def in_frame_push(idx: int) -> bool:
+ #for tkn in reversed(node.block.tokens[: idx - 1]):
+ #if tkn.kind in {"SEMI", "LBRACE", "RBRACE"}:
+ #return False
+ #if tkn.kind == "IDENTIFIER" and tkn.text == "_PyFrame_PushUnchecked":
+ #return True
+ #return False
+
+ #refs: dict[lexer.Token, str | None] = {}
+ #for idx, tkn in enumerate(node.block.tokens):
+ #if tkn.kind != "IDENTIFIER" or tkn.text != "PyStackRef_FromPyObjectNew":
+ #continue
+
+ #if idx == 0 or node.block.tokens[idx - 1].kind != "EQUALS":
+ #if in_frame_push(idx):
+ ## PyStackRef_FromPyObjectNew() is called in _PyFrame_PushUnchecked()
+ #refs[tkn] = None
+ #continue
+ #raise analysis_error("Expected '=' before PyStackRef_FromPyObjectNew", tkn)
+
+ #lhs = find_assignment_target(node, idx - 1)
+ #if len(lhs) == 0:
+ #raise analysis_error(
+ #"PyStackRef_FromPyObjectNew() must be assigned to an output", tkn
+ #)
+
+ #if lhs[0].kind == "TIMES" or any(
+ #t.kind == "ARROW" or t.kind == "LBRACKET" for t in lhs[1:]
+ #):
+ ## Don't handle: *ptr = ..., ptr->field = ..., or ptr[field] = ...
+ ## Assume that they are visible to the GC.
+ #refs[tkn] = None
+ #continue
+
+ #if len(lhs) != 1 or lhs[0].kind != "IDENTIFIER":
+ #raise analysis_error(
+ #"PyStackRef_FromPyObjectNew() must be assigned to an output", tkn
+ #)
+
+ #name = lhs[0].text
+ #match = (
+ #any(var.name == name for var in node.inputs)
+ #or any(var.name == name for var in node.outputs)
+ #)
+ #if not match:
+ #raise analysis_error(
+ #f"PyStackRef_FromPyObjectNew() must be assigned to an input or output, not '{name}'",
+ #tkn,
+ #)
+
+ #refs[tkn] = name
+
+ #return refs
def variable_used(node: parser.CodeDef, name: str) -> bool:
"""Determine whether a variable with a given name is used in a node."""
return any(
- token.kind == "IDENTIFIER" and token.text == name for token in node.block.tokens
+ token.kind == "IDENTIFIER" and token.text == name for token in node.block.tokens()
)
"_Py_ReachedRecursionLimit",
)
-def find_stmt_start(node: parser.CodeDef, idx: int) -> lexer.Token:
- assert idx < len(node.block.tokens)
- while True:
- tkn = node.block.tokens[idx-1]
- if tkn.kind in {"SEMI", "LBRACE", "RBRACE", "CMACRO"}:
- break
- idx -= 1
- assert idx > 0
- while node.block.tokens[idx].kind == "COMMENT":
- idx += 1
- return node.block.tokens[idx]
-
-
-def find_stmt_end(node: parser.CodeDef, idx: int) -> lexer.Token:
- assert idx < len(node.block.tokens)
- while True:
- idx += 1
- tkn = node.block.tokens[idx]
- if tkn.kind == "SEMI":
- return node.block.tokens[idx+1]
-
-def check_escaping_calls(instr: parser.CodeDef, escapes: dict[lexer.Token, EscapingCall]) -> None:
+def check_escaping_calls(instr: parser.CodeDef, escapes: dict[SimpleStmt, EscapingCall]) -> None:
+ error: lexer.Token | None = None
calls = {e.call for e in escapes.values()}
- in_if = 0
- tkn_iter = iter(instr.block.tokens)
- for tkn in tkn_iter:
- if tkn.kind == "IF":
- next(tkn_iter)
- in_if = 1
- if tkn.kind == "IDENTIFIER" and tkn.text in ("DEOPT_IF", "ERROR_IF", "EXIT_IF"):
- next(tkn_iter)
- in_if = 1
- elif tkn.kind == "LPAREN" and in_if:
- in_if += 1
- elif tkn.kind == "RPAREN":
- if in_if:
- in_if -= 1
- elif tkn in calls and in_if:
- raise analysis_error(f"Escaping call '{tkn.text} in condition", tkn)
-
-def find_escaping_api_calls(instr: parser.CodeDef) -> dict[lexer.Token, EscapingCall]:
- result: dict[lexer.Token, EscapingCall] = {}
- tokens = instr.block.tokens
- for idx, tkn in enumerate(tokens):
- try:
- next_tkn = tokens[idx+1]
- except IndexError:
- break
- if tkn.kind == "SWITCH":
- raise analysis_error(f"switch statements are not supported due to their complex flow control. Sorry.", tkn)
- if next_tkn.kind != lexer.LPAREN:
- continue
- if tkn.kind == lexer.IDENTIFIER:
- if tkn.text.upper() == tkn.text:
- # simple macro
- continue
- #if not tkn.text.startswith(("Py", "_Py", "monitor")):
- # continue
- if tkn.text.startswith(("sym_", "optimize_")):
- # Optimize functions
- continue
- if tkn.text.endswith("Check"):
- continue
- if tkn.text.startswith("Py_Is"):
- continue
- if tkn.text.endswith("CheckExact"):
- continue
- if tkn.text in NON_ESCAPING_FUNCTIONS:
+
+ def visit(stmt: Stmt) -> None:
+ nonlocal error
+ if isinstance(stmt, IfStmt) or isinstance(stmt, WhileStmt):
+ for tkn in stmt.condition:
+ if tkn in calls:
+ error = tkn
+ elif isinstance(stmt, SimpleStmt):
+ in_if = 0
+ tkn_iter = iter(stmt.contents)
+ for tkn in tkn_iter:
+ if tkn.kind == "IDENTIFIER" and tkn.text in ("DEOPT_IF", "ERROR_IF", "EXIT_IF"):
+ in_if = 1
+ next(tkn_iter)
+ elif tkn.kind == "LPAREN":
+ if in_if:
+ in_if += 1
+ elif tkn.kind == "RPAREN":
+ if in_if:
+ in_if -= 1
+ elif tkn in calls and in_if:
+ error = tkn
+
+
+ instr.block.accept(visit)
+ if error is not None:
+ raise analysis_error(f"Escaping call '{error.text} in condition", error)
+
+def find_escaping_api_calls(instr: parser.CodeDef) -> dict[SimpleStmt, EscapingCall]:
+ result: dict[SimpleStmt, EscapingCall] = {}
+
+ def visit(stmt: Stmt) -> None:
+ if not isinstance(stmt, SimpleStmt):
+ return
+ tokens = stmt.contents
+ for idx, tkn in enumerate(tokens):
+ try:
+ next_tkn = tokens[idx+1]
+ except IndexError:
+ break
+ if next_tkn.kind != lexer.LPAREN:
continue
- elif tkn.kind == "RPAREN":
- prev = tokens[idx-1]
- if prev.text.endswith("_t") or prev.text == "*" or prev.text == "int":
- #cast
+ if tkn.kind == lexer.IDENTIFIER:
+ if tkn.text.upper() == tkn.text:
+ # simple macro
+ continue
+ #if not tkn.text.startswith(("Py", "_Py", "monitor")):
+ # continue
+ if tkn.text.startswith(("sym_", "optimize_")):
+ # Optimize functions
+ continue
+ if tkn.text.endswith("Check"):
+ continue
+ if tkn.text.startswith("Py_Is"):
+ continue
+ if tkn.text.endswith("CheckExact"):
+ continue
+ if tkn.text in NON_ESCAPING_FUNCTIONS:
+ continue
+ elif tkn.kind == "RPAREN":
+ prev = tokens[idx-1]
+ if prev.text.endswith("_t") or prev.text == "*" or prev.text == "int":
+ #cast
+ continue
+ elif tkn.kind != "RBRACKET":
continue
- elif tkn.kind != "RBRACKET":
- continue
- if tkn.text in ("PyStackRef_CLOSE", "PyStackRef_XCLOSE"):
- if len(tokens) <= idx+2:
- raise analysis_error("Unexpected end of file", next_tkn)
- kills = tokens[idx+2]
- if kills.kind != "IDENTIFIER":
- raise analysis_error(f"Expected identifier, got '{kills.text}'", kills)
- else:
- kills = None
- start = find_stmt_start(instr, idx)
- end = find_stmt_end(instr, idx)
- result[start] = EscapingCall(start, tkn, end, kills)
+ if tkn.text in ("PyStackRef_CLOSE", "PyStackRef_XCLOSE"):
+ if len(tokens) <= idx+2:
+ raise analysis_error("Unexpected end of file", next_tkn)
+ kills = tokens[idx+2]
+ if kills.kind != "IDENTIFIER":
+ raise analysis_error(f"Expected identifier, got '{kills.text}'", kills)
+ else:
+ kills = None
+ result[stmt] = EscapingCall(stmt, tkn, kills)
+
+ instr.block.accept(visit)
check_escaping_calls(instr, result)
return result
annotations=op.annotations,
stack=analyze_stack(op),
caches=analyze_caches(inputs),
- deferred_refs=analyze_deferred_refs(op),
local_stores=find_variable_stores(op),
- body=op.block.tokens,
+ body=op.block,
properties=compute_properties(op),
)
for anno in op.annotations:
annotations=op.annotations,
stack=analyze_stack(op),
caches=analyze_caches(inputs),
- deferred_refs=analyze_deferred_refs(op),
local_stores=find_variable_stores(op),
- body=op.block.tokens,
+ body=op.block,
properties=properties,
)
rep.replicates = result
labels: dict[str, Label],
) -> None:
properties = compute_properties(label)
- labels[label.name] = Label(label.name, label.spilled, label.block.tokens, properties)
+ labels[label.name] = Label(label.name, label.spilled, label.block, properties)
def assign_opcodes(
If there is more than one instruction that contains the uop,
ensure that they all have the same size.
"""
- for tkn in uop.body:
- if tkn.text == "INSTRUCTION_SIZE":
- break
+ for tkn in uop.body.tokens():
+ if tkn.text == "INSTRUCTION_SIZE":
+ break
else:
return None
self.maybe_dedent(tkn.text)
self.set_position(tkn)
self.emit_text(tkn.text)
- if tkn.kind == "CMACRO":
+ if tkn.kind.startswith("CMACRO"):
self.newline = True
self.maybe_indent(tkn.text)
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
self.emit(") {\n")
next(tkn_iter) # Semi colon
assert inst is not None
- assert inst.family is not None
+ assert inst.family is not None, inst
family_name = inst.family.name
self.emit(f"UPDATE_MISS_STATS({family_name});\n")
self.emit(f"assert(_PyOpcode_Deopt[opcode] == ({family_name}));\n")
self.emit(storage.as_comment())
self.out.start_line()
- def _emit_if(
+ def _emit_stmt(
self,
- tkn_iter: TokenIterator,
+ stmt: Stmt,
uop: CodeSection,
storage: Storage,
inst: Instruction | None,
- ) -> tuple[bool, Token, Storage]:
- """Returns (reachable?, closing '}', stack)."""
- tkn = next(tkn_iter)
- assert tkn.kind == "LPAREN"
- self.out.emit(tkn)
- rparen = emit_to(self.out, tkn_iter, "RPAREN")
- self.emit(rparen)
- if_storage = storage.copy()
- reachable, rbrace, if_storage = self._emit_block(tkn_iter, uop, if_storage, inst, True)
- try:
- maybe_else = tkn_iter.peek()
- if maybe_else and maybe_else.kind == "ELSE":
- self._print_storage(storage)
- self.emit(rbrace)
- self.emit(next(tkn_iter))
- maybe_if = tkn_iter.peek()
- if maybe_if and maybe_if.kind == "IF":
- # Emit extra braces around the if to get scoping right
- self.emit(" {\n")
- self.emit(next(tkn_iter))
- else_reachable, rbrace, else_storage = self._emit_if(tkn_iter, uop, storage, inst)
- self.out.start_line()
- self.emit("}\n")
- else:
- else_reachable, rbrace, else_storage = self._emit_block(tkn_iter, uop, storage, inst, True)
- if not reachable:
- # Discard the if storage
- reachable = else_reachable
- storage = else_storage
- elif not else_reachable:
- # Discard the else storage
- storage = if_storage
- reachable = True
- else:
- if PRINT_STACKS:
- self.emit("/* Merge */\n")
- self.out.emit(if_storage.as_comment())
- self.out.emit("\n")
- self.out.emit(else_storage.as_comment())
- else_storage.merge(if_storage, self.out)
- storage = else_storage
- self._print_storage(storage)
- else:
- if reachable:
- if PRINT_STACKS:
- self.emit("/* Merge */\n")
- if_storage.merge(storage, self.out)
- storage = if_storage
- self._print_storage(storage)
- else:
- # Discard the if storage
- reachable = True
- except StackError as ex:
- self._print_storage(if_storage)
- raise analysis_error(ex.args[0], rbrace) from None
- return reachable, rbrace, storage
-
- def _emit_block(
+ ) -> tuple[bool, Token | None, Storage]:
+ method_name = "emit_" + stmt.__class__.__name__
+ method = getattr(self, method_name, None)
+ if method is None:
+ raise NotImplementedError
+ return method(stmt, uop, storage, inst) # type: ignore[no-any-return]
+
+ def emit_SimpleStmt(
self,
- tkn_iter: TokenIterator,
+ stmt: SimpleStmt,
uop: CodeSection,
storage: Storage,
inst: Instruction | None,
- emit_first_brace: bool
- ) -> tuple[bool, Token, Storage]:
- """ Returns (reachable?, closing '}', stack)."""
- braces = 1
+ ) -> tuple[bool, Token | None, Storage]:
local_stores = set(uop.local_stores)
- tkn = next(tkn_iter)
- reload: Token | None = None
+ reachable = True
+ tkn = stmt.contents[-1]
try:
- reachable = True
- line : int = -1
- if tkn.kind != "LBRACE":
- raise analysis_error(f"PEP 7: expected '{{', found: {tkn.text}", tkn)
- escaping_calls = uop.properties.escaping_calls
- if emit_first_brace:
- self.emit(tkn)
- self._print_storage(storage)
+ if stmt in uop.properties.escaping_calls:
+ escape = uop.properties.escaping_calls[stmt]
+ if escape.kills is not None:
+ self.stackref_kill(escape.kills, storage, True)
+ self.emit_save(storage)
+ tkn_iter = TokenIterator(stmt.contents)
for tkn in tkn_iter:
- if PRINT_STACKS and tkn.line != line:
- self.out.start_line()
- self.emit(storage.as_comment())
- self.out.start_line()
- line = tkn.line
- if tkn in escaping_calls:
- escape = escaping_calls[tkn]
- if escape.kills is not None:
- if tkn == reload:
- self.emit_reload(storage)
- self.stackref_kill(escape.kills, storage, True)
- self.emit_save(storage)
- elif tkn != reload:
- self.emit_save(storage)
- reload = escape.end
- elif tkn == reload:
- self.emit_reload(storage)
- if tkn.kind == "LBRACE":
- self.out.emit(tkn)
- braces += 1
- elif tkn.kind == "RBRACE":
- self._print_storage(storage)
- braces -= 1
- if braces == 0:
- return reachable, tkn, storage
- self.out.emit(tkn)
- elif tkn.kind == "GOTO":
+ if tkn.kind == "GOTO":
label_tkn = next(tkn_iter)
self.goto_label(tkn, label_tkn, storage)
reachable = False
self._print_storage(storage)
reachable = False
self.out.emit(tkn)
- elif tkn.kind == "IF":
+ else:
+ self.out.emit(tkn)
+ if stmt in uop.properties.escaping_calls:
+ self.emit_reload(storage)
+ return reachable, None, storage
+ except StackError as ex:
+ raise analysis_error(ex.args[0], tkn) #from None
+
+
+ def emit_MacroIfStmt(
+ self,
+ stmt: MacroIfStmt,
+ uop: CodeSection,
+ storage: Storage,
+ inst: Instruction | None,
+ ) -> tuple[bool, Token | None, Storage]:
+ self.out.emit(stmt.condition)
+ branch = stmt.else_ is not None
+ reachable = True
+ for s in stmt.body:
+ r, tkn, storage = self._emit_stmt(s, uop, storage, inst)
+ if tkn is not None:
+ self.out.emit(tkn)
+ 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
+ for s in stmt.else_body:
+ r, tkn, else_storage = self._emit_stmt(s, uop, else_storage, inst)
+ if tkn is not None:
self.out.emit(tkn)
- if_reachable, rbrace, storage = self._emit_if(tkn_iter, uop, storage, inst)
- if reachable:
- reachable = if_reachable
- self.out.emit(rbrace)
+ if not r:
+ reachable = False
+ storage.merge(else_storage, self.out)
+ self.out.emit(stmt.endif)
+ return reachable, None, storage
+
+
+ def emit_IfStmt(
+ self,
+ stmt: IfStmt,
+ uop: CodeSection,
+ storage: Storage,
+ inst: Instruction | None,
+ ) -> tuple[bool, Token | None, Storage]:
+ self.out.emit(stmt.if_)
+ for tkn in stmt.condition:
+ self.out.emit(tkn)
+ if_storage = storage.copy()
+ rbrace: Token | None = stmt.if_
+ try:
+ reachable, rbrace, if_storage = self._emit_stmt(stmt.body, uop, if_storage, inst)
+ if stmt.else_ is not None:
+ assert rbrace is not None
+ self.out.emit(rbrace)
+ self.out.emit(stmt.else_)
+ if stmt.else_body is not None:
+ else_reachable, rbrace, else_storage = self._emit_stmt(stmt.else_body, uop, storage, inst)
+ if not reachable:
+ reachable, storage = else_reachable, else_storage
+ elif not else_reachable:
+ # Discard the else storage
+ storage = if_storage
else:
+ #Both reachable
+ else_storage.merge(if_storage, self.out)
+ storage = else_storage
+ else:
+ if reachable:
+ if_storage.merge(storage, self.out)
+ storage = if_storage
+ else:
+ # Discard the if 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
+
+ def emit_BlockStmt(
+ self,
+ stmt: BlockStmt,
+ uop: CodeSection,
+ storage: Storage,
+ inst: Instruction | None,
+ emit_braces: bool = True,
+ ) -> tuple[bool, Token | None, Storage]:
+ """ Returns (reachable?, closing '}', stack)."""
+ tkn: Token | None = None
+ try:
+ if emit_braces:
+ self.out.emit(stmt.open)
+ reachable = True
+ for s in stmt.body:
+ reachable, tkn, storage = self._emit_stmt(s, uop, storage, inst)
+ if tkn is not None:
self.out.emit(tkn)
+ if not reachable:
+ break
+ return reachable, stmt.close, storage
except StackError as ex:
+ if tkn is None:
+ tkn = stmt.close
raise analysis_error(ex.args[0], tkn) from None
- raise analysis_error("Expecting closing brace. Reached end of file", tkn)
+
+ def emit_ForStmt(
+ self,
+ stmt: ForStmt,
+ uop: CodeSection,
+ storage: Storage,
+ inst: Instruction | None,
+ ) -> tuple[bool, Token | None, Storage]:
+ """ Returns (reachable?, closing '}', stack)."""
+ self.out.emit(stmt.for_)
+ for tkn in stmt.header:
+ self.out.emit(tkn)
+ return self._emit_stmt(stmt.body, uop, storage, inst)
+
+ def emit_WhileStmt(
+ self,
+ stmt: WhileStmt,
+ uop: CodeSection,
+ storage: Storage,
+ inst: Instruction | None,
+ ) -> tuple[bool, Token | None, Storage]:
+ """ Returns (reachable?, closing '}', stack)."""
+ self.out.emit(stmt.while_)
+ for tkn in stmt.condition:
+ self.out.emit(tkn)
+ return self._emit_stmt(stmt.body, uop, storage, inst)
+
def emit_tokens(
self,
code: CodeSection,
storage: Storage,
inst: Instruction | None,
+ emit_braces: bool = True
) -> Storage:
- tkn_iter = TokenIterator(code.body)
self.out.start_line()
- reachable, rbrace, storage = self._emit_block(tkn_iter, code, storage, inst, False)
+ 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], rbrace) from None
+ raise analysis_error(ex.args[0], tkn) from None
return storage
def emit(self, txt: str | Token) -> None:
# Macros
macro = r"#.*\n"
-CMACRO = "CMACRO"
+CMACRO_IF = "CMACRO_IF"
+CMACRO_ELSE = "CMACRO_ELSE"
+CMACRO_ENDIF = "CMACRO_ENDIF"
+CMACRO_OTHER = "CMACRO_OTHER"
id_re = r"[a-zA-Z_][0-9a-zA-Z_]*"
IDENTIFIER = "IDENTIFIER"
linestart = -1
for m in matcher.finditer(src):
start, end = m.span()
+ macro_body = ""
text = m.group(0)
if text in keywords:
kind = keywords[text]
elif text[0] == "'":
kind = CHARACTER
elif text[0] == "#":
- kind = CMACRO
+ macro_body = text[1:].strip()
+ if macro_body.startswith("if"):
+ kind = CMACRO_IF
+ elif macro_body.startswith("else"):
+ kind = CMACRO_ELSE
+ elif macro_body.startswith("endif"):
+ kind = CMACRO_ENDIF
+ else:
+ kind = CMACRO_OTHER
elif text[0] == "/" and text[1] in "/*":
kind = COMMENT
else:
line += newlines
else:
begin = line, start - linestart
- if kind == CMACRO:
+ if macro_body:
linestart = end
line += 1
if kind != "\n":
# 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)
+ storage = emitter.emit_tokens(override, storage, None, False)
out.start_line()
storage.flush(out)
else:
out.start_line()
stack.flush(out)
except StackError as ex:
- raise analysis_error(ex.args[0], prototype.body[0]) # from None
+ raise analysis_error(ex.args[0], prototype.body.open) # from None
SKIPS = ("_EXTENDED_ARG",)
InputEffect,
OpName,
AstNode,
+ Stmt,
+ SimpleStmt,
+ IfStmt,
+ ForStmt,
+ WhileStmt,
+ BlockStmt,
+ MacroIfStmt,
)
+import pprint
+
CodeDef = InstDef | LabelDef
def prettify_filename(filename: str) -> str:
assert node is not None
result.append(node) # type: ignore[arg-type]
if not psr.eof():
+ pprint.pprint(result)
psr.backup()
raise psr.make_syntax_error(
f"Extra stuff at the end of {filename}", psr.next(True)
"""Parser for bytecodes.inst."""
from dataclasses import dataclass, field
-from typing import NamedTuple, Callable, TypeVar, Literal, cast
+from typing import NamedTuple, Callable, TypeVar, Literal, cast, Iterator
+from io import StringIO
import lexer as lx
from plexer import PLexer
+from cwriter import CWriter
P = TypeVar("P", bound="Parser")
assert context is not None
return context.owner.tokens[context.begin]
+# Statements
+
+Visitor = Callable[["Stmt"], None]
+
+class Stmt:
+
+ def __repr__(self) -> str:
+ io = StringIO()
+ out = CWriter(io, 0, False)
+ self.print(out)
+ return io.getvalue()
+
+ def print(self, out:CWriter) -> None:
+ raise NotImplementedError
+
+ def accept(self, visitor: Visitor) -> None:
+ raise NotImplementedError
+
+ def tokens(self) -> Iterator[lx.Token]:
+ raise NotImplementedError
+
+
+@dataclass
+class IfStmt(Stmt):
+ if_: lx.Token
+ condition: list[lx.Token]
+ body: Stmt
+ else_: lx.Token | None
+ else_body: Stmt | None
+
+ def print(self, out:CWriter) -> None:
+ out.emit(self.if_)
+ for tkn in self.condition:
+ out.emit(tkn)
+ self.body.print(out)
+ if self.else_ is not None:
+ out.emit(self.else_)
+ self.body.print(out)
+ if self.else_body is not None:
+ self.else_body.print(out)
+
+ def accept(self, visitor: Visitor) -> None:
+ visitor(self)
+ self.body.accept(visitor)
+ if self.else_body is not None:
+ self.else_body.accept(visitor)
+
+ def tokens(self) -> Iterator[lx.Token]:
+ yield self.if_
+ yield from self.condition
+ yield from self.body.tokens()
+ if self.else_ is not None:
+ yield self.else_
+ if self.else_body is not None:
+ yield from self.else_body.tokens()
+
+
+@dataclass
+class ForStmt(Stmt):
+ for_: lx.Token
+ header: list[lx.Token]
+ body: Stmt
+
+ def print(self, out:CWriter) -> None:
+ out.emit(self.for_)
+ for tkn in self.header:
+ out.emit(tkn)
+ self.body.print(out)
+
+ def accept(self, visitor: Visitor) -> None:
+ visitor(self)
+ self.body.accept(visitor)
+
+ def tokens(self) -> Iterator[lx.Token]:
+ yield self.for_
+ yield from self.header
+ yield from self.body.tokens()
+
+
+@dataclass
+class WhileStmt(Stmt):
+ while_: lx.Token
+ condition: list[lx.Token]
+ body: Stmt
+
+ def print(self, out:CWriter) -> None:
+ out.emit(self.while_)
+ for tkn in self.condition:
+ out.emit(tkn)
+ self.body.print(out)
+
+ def accept(self, visitor: Visitor) -> None:
+ visitor(self)
+ self.body.accept(visitor)
+
+ def tokens(self) -> Iterator[lx.Token]:
+ yield self.while_
+ yield from self.condition
+ yield from self.body.tokens()
+
+
+@dataclass
+class MacroIfStmt(Stmt):
+ condition: lx.Token
+ body: list[Stmt]
+ else_: lx.Token | None
+ else_body: list[Stmt] | None
+ endif: lx.Token
+
+ def print(self, out:CWriter) -> None:
+ out.emit(self.condition)
+ for stmt in self.body:
+ stmt.print(out)
+ if self.else_body is not None:
+ out.emit("#else\n")
+ for stmt in self.else_body:
+ stmt.print(out)
+
+ def accept(self, visitor: Visitor) -> None:
+ visitor(self)
+ for stmt in self.body:
+ stmt.accept(visitor)
+ if self.else_body is not None:
+ for stmt in self.else_body:
+ stmt.accept(visitor)
+
+ def tokens(self) -> Iterator[lx.Token]:
+ yield self.condition
+ for stmt in self.body:
+ yield from stmt.tokens()
+ if self.else_body is not None:
+ for stmt in self.else_body:
+ yield from stmt.tokens()
+
@dataclass
-class Block(Node):
- # This just holds a context which has the list of tokens.
- pass
+class BlockStmt(Stmt):
+ open: lx.Token
+ body: list[Stmt]
+ close: lx.Token
+
+ def print(self, out:CWriter) -> None:
+ out.emit(self.open)
+ for stmt in self.body:
+ stmt.print(out)
+ out.start_line()
+ out.emit(self.close)
+
+ def accept(self, visitor: Visitor) -> None:
+ visitor(self)
+ for stmt in self.body:
+ stmt.accept(visitor)
+
+ def tokens(self) -> Iterator[lx.Token]:
+ yield self.open
+ for stmt in self.body:
+ yield from stmt.tokens()
+ yield self.close
+
+
+@dataclass
+class SimpleStmt(Stmt):
+ contents: list[lx.Token]
+
+ def print(self, out:CWriter) -> None:
+ for tkn in self.contents:
+ out.emit(tkn)
+
+ def tokens(self) -> Iterator[lx.Token]:
+ yield from self.contents
+
+ def accept(self, visitor: Visitor) -> None:
+ visitor(self)
+ __hash__ = object.__hash__
@dataclass
class StackEffect(Node):
name: str
inputs: list[InputEffect]
outputs: list[OutputEffect]
- block: Block
+ block: BlockStmt
@dataclass
class LabelDef(Node):
name: str
spilled: bool
- block: Block
+ block: BlockStmt
AstNode = InstDef | Macro | Pseudo | Family | LabelDef
if self.expect(lx.LPAREN):
if tkn := self.expect(lx.IDENTIFIER):
if self.expect(lx.RPAREN):
- if block := self.block():
- return LabelDef(tkn.text, spilled, block)
+ block = self.block()
+ return LabelDef(tkn.text, spilled, block)
return None
@contextual
def inst_def(self) -> InstDef | None:
if hdr := self.inst_header():
- if block := self.block():
- return InstDef(
- hdr.annotations,
- hdr.kind,
- hdr.name,
- hdr.inputs,
- hdr.outputs,
- block,
- )
- raise self.make_syntax_error("Expected block")
+ block = self.block()
+ return InstDef(
+ hdr.annotations,
+ hdr.kind,
+ hdr.name,
+ hdr.inputs,
+ hdr.outputs,
+ block,
+ )
return None
@contextual
self.setpos(here)
return None
- @contextual
- def block(self) -> Block | None:
- if self.c_blob():
- return Block()
- return None
+ def block(self) -> BlockStmt:
+ open = self.require(lx.LBRACE)
+ stmts: list[Stmt] = []
+ while not (close := self.expect(lx.RBRACE)):
+ stmts.append(self.stmt())
+ return BlockStmt(open, stmts, close)
+
+ def stmt(self) -> Stmt:
+ if tkn := self.expect(lx.IF):
+ return self.if_stmt(tkn)
+ elif self.expect(lx.LBRACE):
+ self.backup()
+ return self.block()
+ elif tkn := self.expect(lx.FOR):
+ return self.for_stmt(tkn)
+ elif tkn := self.expect(lx.WHILE):
+ return self.while_stmt(tkn)
+ elif tkn := self.expect(lx.CMACRO_IF):
+ return self.macro_if(tkn)
+ elif tkn := self.expect(lx.CMACRO_ELSE):
+ msg = "Unexpected #else"
+ raise self.make_syntax_error(msg)
+ elif tkn := self.expect(lx.CMACRO_ENDIF):
+ msg = "Unexpected #endif"
+ raise self.make_syntax_error(msg)
+ elif tkn := self.expect(lx.CMACRO_OTHER):
+ return SimpleStmt([tkn])
+ elif tkn := self.expect(lx.SWITCH):
+ msg = "switch statements are not supported due to their complex flow control. Sorry."
+ raise self.make_syntax_error(msg)
+ tokens = self.consume_to(lx.SEMI)
+ return SimpleStmt(tokens)
+
+ def if_stmt(self, if_: lx.Token) -> IfStmt:
+ lparen = self.require(lx.LPAREN)
+ condition = [lparen] + self.consume_to(lx.RPAREN)
+ body = self.block()
+ else_body: Stmt | None = None
+ else_: lx.Token | None = None
+ if else_ := self.expect(lx.ELSE):
+ if inner := self.expect(lx.IF):
+ else_body = self.if_stmt(inner)
+ else:
+ else_body = self.block()
+ return IfStmt(if_, condition, body, else_, else_body)
+
+ def macro_if(self, cond: lx.Token) -> MacroIfStmt:
+ else_ = None
+ body: list[Stmt] = []
+ else_body: list[Stmt] | None = None
+ part = body
+ while True:
+ if tkn := self.expect(lx.CMACRO_ENDIF):
+ return MacroIfStmt(cond, body, else_, else_body, tkn)
+ elif tkn := self.expect(lx.CMACRO_ELSE):
+ if part is else_body:
+ raise self.make_syntax_error("Multiple #else")
+ else_ = tkn
+ else_body = []
+ part = else_body
+ else:
+ part.append(self.stmt())
- def c_blob(self) -> list[lx.Token]:
- tokens: list[lx.Token] = []
- level = 0
- while tkn := self.next(raw=True):
- tokens.append(tkn)
- if tkn.kind in (lx.LBRACE, lx.LPAREN, lx.LBRACKET):
- level += 1
- elif tkn.kind in (lx.RBRACE, lx.RPAREN, lx.RBRACKET):
- level -= 1
- if level <= 0:
- break
- return tokens
+ def for_stmt(self, for_: lx.Token) -> ForStmt:
+ lparen = self.require(lx.LPAREN)
+ header = [lparen] + self.consume_to(lx.RPAREN)
+ body = self.block()
+ return ForStmt(for_, header, body)
+
+ def while_stmt(self, while_: lx.Token) -> WhileStmt:
+ lparen = self.require(lx.LPAREN)
+ cond = [lparen] + self.consume_to(lx.RPAREN)
+ body = self.block()
+ return WhileStmt(while_, cond, body)
if __name__ == "__main__":
import sys
+ import pprint
if sys.argv[1:]:
filename = sys.argv[1]
filename = "<default>"
src = "if (x) { x.foo; // comment\n}"
parser = Parser(src, filename)
- x = parser.definition()
- print(x)
+ while node := parser.definition():
+ pprint.pprint(node)
f"Expected {kind!r} but got {tkn and tkn.text!r}", tkn
)
+ def consume_to(self, end: str) -> list[Token]:
+ res: list[Token] = []
+ parens = 0
+ while tkn := self.next(raw=True):
+ res.append(tkn)
+ if tkn.kind == end and parens == 0:
+ return res
+ if tkn.kind == "LPAREN":
+ parens += 1
+ if tkn.kind == "RPAREN":
+ parens -= 1
+ raise self.make_syntax_error(
+ f"Expected {end!r} but reached EOF", tkn)
+
def extract_line(self, lineno: int) -> str:
# Return source line `lineno` (1-based)
lines = self.src.splitlines()
emitter.emit(f"// flush\n")
stack.flush(emitter.out)
return offset, stack
- try:
- locals: dict[str, Local] = {}
- emitter.out.start_line()
- if braces:
- emitter.out.emit(f"// {uop.name}\n")
- emitter.emit("{\n")
- storage = Storage.for_uop(stack, uop, emitter.out)
- emitter._print_storage(storage)
+ locals: dict[str, Local] = {}
+ emitter.out.start_line()
+ if braces:
+ emitter.out.emit(f"// {uop.name}\n")
+ emitter.emit("{\n")
+ storage = Storage.for_uop(stack, uop, emitter.out)
+ emitter._print_storage(storage)
+
+ for cache in uop.caches:
+ if cache.name != "unused":
+ if cache.size == 4:
+ type = "PyObject *"
+ reader = "read_obj"
+ else:
+ type = f"uint{cache.size*16}_t "
+ reader = f"read_u{cache.size*16}"
+ emitter.emit(
+ f"{type}{cache.name} = {reader}(&this_instr[{offset}].cache);\n"
+ )
+ if inst.family is None:
+ emitter.emit(f"(void){cache.name};\n")
+ offset += cache.size
- for cache in uop.caches:
- if cache.name != "unused":
- if cache.size == 4:
- type = "PyObject *"
- reader = "read_obj"
- else:
- type = f"uint{cache.size*16}_t "
- reader = f"read_u{cache.size*16}"
- emitter.emit(
- f"{type}{cache.name} = {reader}(&this_instr[{offset}].cache);\n"
- )
- if inst.family is None:
- emitter.emit(f"(void){cache.name};\n")
- offset += cache.size
-
- storage = emitter.emit_tokens(uop, storage, inst)
- if braces:
- emitter.out.start_line()
- emitter.emit("}\n")
- # emitter.emit(stack.as_comment() + "\n")
- return offset, storage.stack
- except StackError as ex:
- raise analysis_error(ex.args[0], uop.body[0])
+ 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
def uses_this(inst: Instruction) -> bool:
for uop in inst.parts:
if not isinstance(uop, Uop):
continue
- for tkn in uop.body:
+ for tkn in uop.body.tokens():
if (tkn.kind == "IDENTIFIER"
and (tkn.text in {"DEOPT_IF", "EXIT_IF"})):
return True
# Emit tail-callable labels as function defintions
for name, label in analysis.labels.items():
emitter.emit(f"LABEL({name})\n")
- emitter.emit("{\n")
storage = Storage(Stack(), [], [], False)
if label.spilled:
storage.spilled = 1
- emitter.emit("/* STACK SPILLED */\n")
emitter.emit_tokens(label, storage, None)
- emitter.emit("\n")
- emitter.emit("}\n")
- emitter.emit("\n")
+ emitter.emit("\n\n")
def generate_tier1_cases(
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)
+ storage = emitter.emit_tokens(uop, storage, None, False)
storage.flush(emitter.out)
except StackError as ex:
- raise analysis_error(ex.args[0], uop.body[0]) from None
+ raise analysis_error(ex.args[0], uop.body.open) from None
return storage.stack
SKIPS = ("_EXTENDED_ARG",)