mirror of
https://github.com/python/cpython.git
synced 2025-07-07 19:35:27 +00:00
GH-131498: Cases generator: Parse down to C statement level. (GH-131948)
* Parse down to statement level in the cases generator * Add handling for #if macros, treating them much like normal ifs.
This commit is contained in:
parent
6e91d1f9aa
commit
ad053d8d6a
16 changed files with 795 additions and 959 deletions
|
@ -457,7 +457,6 @@ class TestGeneratedCases(unittest.TestCase):
|
|||
if (cond) {
|
||||
JUMP_TO_LABEL(label);
|
||||
}
|
||||
// Comment is ok
|
||||
DISPATCH();
|
||||
}
|
||||
"""
|
||||
|
@ -586,7 +585,6 @@ class TestGeneratedCases(unittest.TestCase):
|
|||
|
||||
LABEL(somewhere)
|
||||
{
|
||||
|
||||
}
|
||||
"""
|
||||
self.run_cases_test(input, output)
|
||||
|
@ -1351,7 +1349,6 @@ class TestGeneratedCases(unittest.TestCase):
|
|||
}
|
||||
// THIRD
|
||||
{
|
||||
// Mark j and k as used
|
||||
if (cond) {
|
||||
JUMP_TO_LABEL(pop_2_error);
|
||||
}
|
||||
|
@ -1757,17 +1754,14 @@ class TestGeneratedCases(unittest.TestCase):
|
|||
output = """
|
||||
LABEL(other_label)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
LABEL(other_label2)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
LABEL(my_label)
|
||||
{
|
||||
// Comment
|
||||
_PyFrame_SetStackPointer(frame, stack_pointer);
|
||||
do_thing();
|
||||
stack_pointer = _PyFrame_GetStackPointer(frame);
|
||||
|
@ -1795,7 +1789,6 @@ class TestGeneratedCases(unittest.TestCase):
|
|||
output = """
|
||||
LABEL(one)
|
||||
{
|
||||
/* STACK SPILLED */
|
||||
stack_pointer = _PyFrame_GetStackPointer(frame);
|
||||
JUMP_TO_LABEL(two);
|
||||
}
|
||||
|
@ -1851,7 +1844,6 @@ class TestGeneratedCases(unittest.TestCase):
|
|||
output = """
|
||||
LABEL(my_label_1)
|
||||
{
|
||||
// Comment
|
||||
_PyFrame_SetStackPointer(frame, stack_pointer);
|
||||
do_thing1();
|
||||
stack_pointer = _PyFrame_GetStackPointer(frame);
|
||||
|
@ -1860,7 +1852,6 @@ class TestGeneratedCases(unittest.TestCase):
|
|||
|
||||
LABEL(my_label_2)
|
||||
{
|
||||
// Comment
|
||||
_PyFrame_SetStackPointer(frame, stack_pointer);
|
||||
do_thing2();
|
||||
stack_pointer = _PyFrame_GetStackPointer(frame);
|
||||
|
|
|
@ -163,7 +163,7 @@ dummy_func(
|
|||
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);
|
||||
|
@ -2245,7 +2245,8 @@ dummy_func(
|
|||
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
|
||||
|
@ -2322,7 +2323,8 @@ dummy_func(
|
|||
}
|
||||
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
|
||||
|
|
184
Python/executor_cases.c.h
generated
184
Python/executor_cases.c.h
generated
|
@ -30,7 +30,7 @@
|
|||
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);
|
||||
|
@ -601,11 +601,6 @@
|
|||
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);
|
||||
|
@ -749,7 +744,6 @@
|
|||
_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();
|
||||
|
@ -1111,17 +1105,6 @@
|
|||
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);
|
||||
|
@ -1139,8 +1122,7 @@
|
|||
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
|
||||
|
@ -1213,8 +1195,6 @@
|
|||
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;
|
||||
}
|
||||
|
@ -1303,7 +1283,6 @@
|
|||
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();
|
||||
|
@ -1364,7 +1343,6 @@
|
|||
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);
|
||||
|
@ -1398,7 +1376,6 @@
|
|||
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();
|
||||
|
@ -1461,7 +1438,6 @@
|
|||
if (rc <= 0) {
|
||||
JUMP_TO_ERROR();
|
||||
}
|
||||
// not found or error
|
||||
res = PyStackRef_FromPyObjectSteal(res_o);
|
||||
stack_pointer[0] = res;
|
||||
stack_pointer += 1;
|
||||
|
@ -1567,7 +1543,6 @@
|
|||
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;
|
||||
|
@ -1605,7 +1580,6 @@
|
|||
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();
|
||||
|
@ -1615,7 +1589,6 @@
|
|||
UOP_STAT_INC(uopcode, miss);
|
||||
JUMP_TO_JUMP_TARGET();
|
||||
}
|
||||
// Ensure index < len(list)
|
||||
if (index >= PyList_GET_SIZE(list)) {
|
||||
UNLOCK_OBJECT(list);
|
||||
if (true) {
|
||||
|
@ -1628,7 +1601,7 @@
|
|||
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());
|
||||
|
@ -1673,7 +1646,6 @@
|
|||
_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));
|
||||
|
@ -1762,7 +1734,6 @@
|
|||
_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);
|
||||
|
@ -1906,9 +1877,6 @@
|
|||
_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);
|
||||
|
@ -1925,7 +1893,6 @@
|
|||
_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 ||
|
||||
|
@ -1962,7 +1929,6 @@
|
|||
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;
|
||||
|
@ -2049,7 +2015,6 @@
|
|||
_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,
|
||||
|
@ -2268,7 +2233,6 @@
|
|||
_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();
|
||||
}
|
||||
|
@ -2459,8 +2423,6 @@
|
|||
|
||||
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) {
|
||||
|
@ -2477,8 +2439,6 @@
|
|||
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);
|
||||
|
@ -2568,7 +2528,6 @@
|
|||
|
||||
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);
|
||||
|
@ -2825,7 +2784,6 @@
|
|||
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);
|
||||
|
@ -2936,8 +2894,6 @@
|
|||
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,
|
||||
|
@ -3039,7 +2995,7 @@
|
|||
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());
|
||||
|
@ -3082,26 +3038,15 @@
|
|||
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);
|
||||
|
@ -3116,7 +3061,6 @@
|
|||
}
|
||||
}
|
||||
else {
|
||||
/* Classic, pushes one value. */
|
||||
_PyFrame_SetStackPointer(frame, stack_pointer);
|
||||
attr_o = PyObject_GetAttr(PyStackRef_AsPyObjectBorrow(owner), name);
|
||||
stack_pointer = _PyFrame_GetStackPointer(frame);
|
||||
|
@ -3198,7 +3142,8 @@
|
|||
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();
|
||||
|
@ -3311,7 +3256,8 @@
|
|||
}
|
||||
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();
|
||||
|
@ -3529,8 +3475,6 @@
|
|||
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());
|
||||
|
@ -3623,12 +3567,10 @@
|
|||
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());
|
||||
|
@ -3657,12 +3599,10 @@
|
|||
_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());
|
||||
|
@ -3687,7 +3627,6 @@
|
|||
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());
|
||||
|
@ -3767,7 +3706,6 @@
|
|||
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;
|
||||
|
@ -4002,7 +3940,6 @@
|
|||
_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);
|
||||
|
@ -4029,8 +3966,6 @@
|
|||
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,
|
||||
|
@ -4053,15 +3988,14 @@
|
|||
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;
|
||||
|
@ -4099,7 +4033,6 @@
|
|||
_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));
|
||||
|
@ -4118,7 +4051,6 @@
|
|||
_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);
|
||||
|
@ -4141,13 +4073,9 @@
|
|||
_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 "
|
||||
|
@ -4157,26 +4085,23 @@
|
|||
}
|
||||
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;
|
||||
|
@ -4188,7 +4113,6 @@
|
|||
_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);
|
||||
|
@ -4206,15 +4130,12 @@
|
|||
_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());
|
||||
|
@ -4290,8 +4211,6 @@
|
|||
_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();
|
||||
|
@ -4440,10 +4359,7 @@
|
|||
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();
|
||||
|
@ -4460,7 +4376,6 @@
|
|||
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;
|
||||
|
@ -4513,15 +4428,6 @@
|
|||
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);
|
||||
|
@ -4532,7 +4438,7 @@
|
|||
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);
|
||||
|
@ -4607,7 +4513,6 @@
|
|||
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));
|
||||
|
@ -4690,7 +4595,6 @@
|
|||
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();
|
||||
|
@ -4755,7 +4659,6 @@
|
|||
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--;
|
||||
|
@ -4770,7 +4673,6 @@
|
|||
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) {
|
||||
|
@ -4895,7 +4797,6 @@
|
|||
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);
|
||||
|
@ -5174,8 +5075,6 @@
|
|||
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;
|
||||
|
@ -5365,7 +5264,6 @@
|
|||
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(
|
||||
|
@ -5381,9 +5279,6 @@
|
|||
}
|
||||
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;
|
||||
|
@ -5493,7 +5388,6 @@
|
|||
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])) {
|
||||
|
@ -5512,7 +5406,6 @@
|
|||
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();
|
||||
|
@ -5552,7 +5445,6 @@
|
|||
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;
|
||||
|
@ -5570,7 +5462,6 @@
|
|||
}
|
||||
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);
|
||||
|
@ -5634,7 +5525,6 @@
|
|||
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;
|
||||
|
@ -5651,7 +5541,6 @@
|
|||
JUMP_TO_JUMP_TARGET();
|
||||
}
|
||||
STAT_INC(CALL, hit);
|
||||
/* res = func(self, arguments, nargs, kwnames) */
|
||||
_PyFrame_SetStackPointer(frame, stack_pointer);
|
||||
PyCFunctionFastWithKeywords cfunc =
|
||||
(PyCFunctionFastWithKeywords)(void(*)(void))
|
||||
|
@ -5717,7 +5606,6 @@
|
|||
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])) {
|
||||
|
@ -5771,7 +5659,6 @@
|
|||
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;
|
||||
|
@ -5860,8 +5747,7 @@
|
|||
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
|
||||
|
@ -5898,7 +5784,6 @@
|
|||
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();
|
||||
|
@ -6068,7 +5953,6 @@
|
|||
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();
|
||||
|
@ -6115,7 +5999,6 @@
|
|||
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();
|
||||
|
@ -6229,7 +6112,6 @@
|
|||
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])) {
|
||||
|
@ -6252,8 +6134,6 @@
|
|||
_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) {
|
||||
|
@ -6368,7 +6248,6 @@
|
|||
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);
|
||||
|
@ -6617,8 +6496,6 @@
|
|||
_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);
|
||||
|
@ -7012,7 +6889,6 @@
|
|||
|
||||
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);
|
||||
}
|
||||
|
|
350
Python/generated_cases.c.h
generated
350
Python/generated_cases.c.h
generated
File diff suppressed because it is too large
Load diff
|
@ -367,34 +367,39 @@ dummy_func(void) {
|
|||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -415,7 +420,8 @@ dummy_func(void) {
|
|||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
153
Python/optimizer_cases.c.h
generated
153
Python/optimizer_cases.c.h
generated
|
@ -28,7 +28,6 @@
|
|||
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;
|
||||
}
|
||||
|
@ -162,7 +161,8 @@
|
|||
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;
|
||||
|
@ -173,7 +173,8 @@
|
|||
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);
|
||||
}
|
||||
|
@ -185,7 +186,8 @@
|
|||
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);
|
||||
}
|
||||
|
@ -197,7 +199,8 @@
|
|||
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);
|
||||
}
|
||||
|
@ -209,7 +212,8 @@
|
|||
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);
|
||||
}
|
||||
|
@ -241,7 +245,8 @@
|
|||
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;
|
||||
|
@ -301,8 +306,6 @@
|
|||
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);
|
||||
|
@ -332,8 +335,6 @@
|
|||
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);
|
||||
|
@ -363,8 +364,6 @@
|
|||
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);
|
||||
|
@ -415,8 +414,6 @@
|
|||
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);
|
||||
|
@ -447,8 +444,6 @@
|
|||
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);
|
||||
|
@ -479,8 +474,6 @@
|
|||
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);
|
||||
|
@ -538,7 +531,6 @@
|
|||
else {
|
||||
res = sym_new_type(ctx, &PyUnicode_Type);
|
||||
}
|
||||
// _STORE_FAST:
|
||||
GETLOCAL(this_instr->operand0) = res;
|
||||
stack_pointer += -2;
|
||||
assert(WITHIN_STACK_BOUNDS());
|
||||
|
@ -690,7 +682,6 @@
|
|||
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;
|
||||
|
@ -699,7 +690,6 @@
|
|||
curr_space -= framesize;
|
||||
co = get_code(this_instr);
|
||||
if (co == NULL) {
|
||||
// might be impossible, but bailing is still safe
|
||||
ctx->done = true;
|
||||
}
|
||||
res = temp;
|
||||
|
@ -735,7 +725,6 @@
|
|||
/* _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;
|
||||
}
|
||||
|
@ -784,7 +773,6 @@
|
|||
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);
|
||||
}
|
||||
|
@ -834,7 +822,6 @@
|
|||
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);
|
||||
|
@ -1097,15 +1084,8 @@
|
|||
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);
|
||||
|
@ -1156,7 +1136,6 @@
|
|||
}
|
||||
}
|
||||
if (attr == NULL) {
|
||||
/* No conversion made. We don't know what `attr` is. */
|
||||
attr = sym_new_not_null(ctx);
|
||||
}
|
||||
stack_pointer[-1] = attr;
|
||||
|
@ -1507,7 +1486,6 @@
|
|||
}
|
||||
|
||||
case _FOR_ITER_GEN_FRAME: {
|
||||
/* We are about to hit the end of the trace */
|
||||
ctx->done = true;
|
||||
break;
|
||||
}
|
||||
|
@ -1712,8 +1690,6 @@
|
|||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -1761,7 +1737,6 @@
|
|||
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++;
|
||||
}
|
||||
|
@ -1787,16 +1762,13 @@
|
|||
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;
|
||||
}
|
||||
|
@ -1804,11 +1776,8 @@
|
|||
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;
|
||||
|
@ -2049,7 +2018,6 @@
|
|||
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;
|
||||
|
@ -2061,7 +2029,6 @@
|
|||
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;
|
||||
|
@ -2123,64 +2090,34 @@
|
|||
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());
|
||||
|
@ -2253,11 +2190,9 @@
|
|||
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;
|
||||
|
@ -2273,11 +2208,9 @@
|
|||
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());
|
||||
|
@ -2296,8 +2229,6 @@
|
|||
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;
|
||||
}
|
||||
|
|
|
@ -3,18 +3,19 @@ import itertools
|
|||
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
|
||||
|
@ -48,7 +49,7 @@ class Properties:
|
|||
|
||||
@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(
|
||||
|
@ -176,9 +177,8 @@ class Uop:
|
|||
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
|
||||
|
@ -221,7 +221,7 @@ class Uop:
|
|||
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
|
||||
|
@ -229,7 +229,7 @@ class Uop:
|
|||
|
||||
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
|
||||
|
@ -421,100 +421,102 @@ def analyze_caches(inputs: list[parser.InputEffect]) -> list[CacheEntry]:
|
|||
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)
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
#def analyze_deferred_refs(node: parser.InstDef) -> dict[lexer.Token, str | None]:
|
||||
#"""Look for PyStackRef_FromPyObjectNew() calls"""
|
||||
|
||||
refs: dict[lexer.Token, str | None] = {}
|
||||
for idx, tkn in enumerate(node.block.tokens):
|
||||
if tkn.kind != "IDENTIFIER" or tkn.text != "PyStackRef_FromPyObjectNew":
|
||||
continue
|
||||
#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
|
||||
|
||||
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)
|
||||
#refs: dict[lexer.Token, str | None] = {}
|
||||
#for idx, tkn in enumerate(node.block.tokens):
|
||||
#if tkn.kind != "IDENTIFIER" or tkn.text != "PyStackRef_FromPyObjectNew":
|
||||
#continue
|
||||
|
||||
lhs = find_assignment_target(node, idx - 1)
|
||||
if len(lhs) == 0:
|
||||
raise analysis_error(
|
||||
"PyStackRef_FromPyObjectNew() must be assigned to an output", tkn
|
||||
)
|
||||
#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)
|
||||
|
||||
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
|
||||
#lhs = find_assignment_target(node, idx - 1)
|
||||
#if len(lhs) == 0:
|
||||
#raise analysis_error(
|
||||
#"PyStackRef_FromPyObjectNew() must be assigned to an output", tkn
|
||||
#)
|
||||
|
||||
if len(lhs) != 1 or lhs[0].kind != "IDENTIFIER":
|
||||
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
|
||||
|
||||
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,
|
||||
)
|
||||
#if len(lhs) != 1 or lhs[0].kind != "IDENTIFIER":
|
||||
#raise analysis_error(
|
||||
#"PyStackRef_FromPyObjectNew() must be assigned to an output", tkn
|
||||
#)
|
||||
|
||||
refs[tkn] = name
|
||||
#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,
|
||||
#)
|
||||
|
||||
return refs
|
||||
#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()
|
||||
)
|
||||
|
||||
|
||||
|
@ -678,93 +680,86 @@ NON_ESCAPING_FUNCTIONS = (
|
|||
"_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
|
||||
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
|
||||
#if not tkn.text.startswith(("Py", "_Py", "monitor")):
|
||||
# continue
|
||||
if tkn.text.startswith(("sym_", "optimize_")):
|
||||
# Optimize functions
|
||||
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
|
||||
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
|
||||
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
|
||||
|
||||
|
@ -876,9 +871,8 @@ def make_uop(
|
|||
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:
|
||||
|
@ -898,9 +892,8 @@ def make_uop(
|
|||
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
|
||||
|
@ -1015,7 +1008,7 @@ def add_label(
|
|||
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(
|
||||
|
@ -1109,9 +1102,9 @@ def get_instruction_size_for_uop(instructions: dict[str, Instruction], uop: Uop)
|
|||
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
|
||||
|
||||
|
|
|
@ -99,7 +99,7 @@ class CWriter:
|
|||
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)
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ from cwriter import CWriter
|
|||
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
|
||||
|
@ -160,7 +161,7 @@ class Emitter:
|
|||
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")
|
||||
|
@ -458,119 +459,38 @@ class Emitter:
|
|||
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
|
||||
) -> 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_block(
|
||||
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
|
||||
|
@ -597,34 +517,161 @@ class Emitter:
|
|||
self._print_storage(storage)
|
||||
reachable = False
|
||||
self.out.emit(tkn)
|
||||
elif tkn.kind == "IF":
|
||||
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)
|
||||
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 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:
|
||||
|
|
|
@ -80,7 +80,10 @@ opmap = {pattern.replace("\\", "") or "\\": op for op, pattern in operators.item
|
|||
|
||||
# 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"
|
||||
|
@ -292,6 +295,7 @@ def tokenize(src: str, line: int = 1, filename: str = "") -> Iterator[Token]:
|
|||
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]
|
||||
|
@ -316,7 +320,15 @@ def tokenize(src: str, line: int = 1, filename: str = "") -> Iterator[Token]:
|
|||
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:
|
||||
|
@ -338,7 +350,7 @@ def tokenize(src: str, line: int = 1, filename: str = "") -> Iterator[Token]:
|
|||
line += newlines
|
||||
else:
|
||||
begin = line, start - linestart
|
||||
if kind == CMACRO:
|
||||
if macro_body:
|
||||
linestart = end
|
||||
line += 1
|
||||
if kind != "\n":
|
||||
|
|
|
@ -145,7 +145,7 @@ def write_uop(
|
|||
# No reference management of inputs needed.
|
||||
for var in storage.inputs: # type: ignore[possibly-undefined]
|
||||
var.in_local = False
|
||||
storage = emitter.emit_tokens(override, storage, None)
|
||||
storage = emitter.emit_tokens(override, storage, None, False)
|
||||
out.start_line()
|
||||
storage.flush(out)
|
||||
else:
|
||||
|
@ -153,7 +153,7 @@ def write_uop(
|
|||
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",)
|
||||
|
|
|
@ -11,8 +11,17 @@ from parsing import ( # noqa: F401
|
|||
InputEffect,
|
||||
OpName,
|
||||
AstNode,
|
||||
Stmt,
|
||||
SimpleStmt,
|
||||
IfStmt,
|
||||
ForStmt,
|
||||
WhileStmt,
|
||||
BlockStmt,
|
||||
MacroIfStmt,
|
||||
)
|
||||
|
||||
import pprint
|
||||
|
||||
CodeDef = InstDef | LabelDef
|
||||
|
||||
def prettify_filename(filename: str) -> str:
|
||||
|
@ -61,6 +70,7 @@ def parse_files(filenames: list[str]) -> list[AstNode]:
|
|||
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)
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
"""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")
|
||||
|
@ -66,12 +68,181 @@ class Node:
|
|||
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 Block(Node):
|
||||
# This just holds a context which has the list of tokens.
|
||||
pass
|
||||
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 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):
|
||||
|
@ -124,7 +295,7 @@ class InstDef(Node):
|
|||
name: str
|
||||
inputs: list[InputEffect]
|
||||
outputs: list[OutputEffect]
|
||||
block: Block
|
||||
block: BlockStmt
|
||||
|
||||
|
||||
@dataclass
|
||||
|
@ -153,7 +324,7 @@ class Pseudo(Node):
|
|||
class LabelDef(Node):
|
||||
name: str
|
||||
spilled: bool
|
||||
block: Block
|
||||
block: BlockStmt
|
||||
|
||||
|
||||
AstNode = InstDef | Macro | Pseudo | Family | LabelDef
|
||||
|
@ -183,23 +354,22 @@ class Parser(PLexer):
|
|||
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
|
||||
|
@ -473,28 +643,85 @@ class Parser(PLexer):
|
|||
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 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 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 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]
|
||||
|
@ -512,5 +739,5 @@ if __name__ == "__main__":
|
|||
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)
|
||||
|
|
|
@ -69,6 +69,20 @@ class PLexer:
|
|||
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()
|
||||
|
|
|
@ -79,38 +79,35 @@ def write_uop(
|
|||
emitter.emit(f"// flush\n")
|
||||
stack.flush(emitter.out)
|
||||
return offset, stack
|
||||
try:
|
||||
locals: dict[str, Local] = {}
|
||||
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
|
||||
|
||||
storage = emitter.emit_tokens(uop, storage, inst, False)
|
||||
if braces:
|
||||
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
|
||||
|
||||
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])
|
||||
emitter.emit("}\n")
|
||||
# emitter.emit(stack.as_comment() + "\n")
|
||||
return offset, storage.stack
|
||||
|
||||
|
||||
def uses_this(inst: Instruction) -> bool:
|
||||
|
@ -127,7 +124,7 @@ 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
|
||||
|
@ -201,15 +198,11 @@ def generate_tier1_labels(
|
|||
# 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(
|
||||
|
|
|
@ -154,10 +154,10 @@ def write_uop(uop: Uop, emitter: Emitter, stack: Stack) -> Stack:
|
|||
cast = f"uint{cache.size*16}_t"
|
||||
emitter.emit(f"{type}{cache.name} = ({cast})CURRENT_OPERAND{idx}();\n")
|
||||
idx += 1
|
||||
storage = emitter.emit_tokens(uop, storage, None)
|
||||
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",)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue