GH-122155: Track local variables between pops and pushes in cases generator (GH-122286)

This commit is contained in:
Mark Shannon 2024-08-01 09:27:26 +01:00 committed by GitHub
parent 46f5a4f9e1
commit a9d56e38a0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 463 additions and 159 deletions

View file

@ -903,7 +903,7 @@ int _PyOpcode_num_pushed(int opcode, int oparg) {
case UNARY_NOT: case UNARY_NOT:
return 1; return 1;
case UNPACK_EX: case UNPACK_EX:
return 1 + (oparg >> 8) + (oparg & 0xFF); return 1 + (oparg & 0xFF) + (oparg >> 8);
case UNPACK_SEQUENCE: case UNPACK_SEQUENCE:
return oparg; return oparg;
case UNPACK_SEQUENCE_LIST: case UNPACK_SEQUENCE_LIST:

View file

@ -31,7 +31,7 @@ test_tools.skip_if_missing("cases_generator")
with test_tools.imports_under_tool("cases_generator"): with test_tools.imports_under_tool("cases_generator"):
from analyzer import StackItem from analyzer import StackItem
import parser import parser
from stack import Stack from stack import Local, Stack
import tier1_generator import tier1_generator
import optimizer_generator import optimizer_generator
@ -60,9 +60,9 @@ class TestEffects(unittest.TestCase):
stack.pop(y) stack.pop(y)
stack.pop(x) stack.pop(x)
for out in outputs: for out in outputs:
stack.push(out) stack.push(Local.local(out))
self.assertEqual(stack.base_offset.to_c(), "-1 - oparg*2 - oparg") self.assertEqual(stack.base_offset.to_c(), "-1 - oparg - oparg*2")
self.assertEqual(stack.top_offset.to_c(), "1 - oparg*2 - oparg + oparg*4") self.assertEqual(stack.top_offset.to_c(), "1 - oparg - oparg*2 + oparg*4")
class TestGeneratedCases(unittest.TestCase): class TestGeneratedCases(unittest.TestCase):
@ -602,7 +602,11 @@ class TestGeneratedCases(unittest.TestCase):
frame->instr_ptr = next_instr; frame->instr_ptr = next_instr;
next_instr += 1; next_instr += 1;
INSTRUCTION_STATS(OP); INSTRUCTION_STATS(OP);
if (oparg == 0) { stack_pointer += -1 - oparg; goto somewhere; } if (oparg == 0) {
stack_pointer += -1 - oparg;
assert(WITHIN_STACK_BOUNDS());
goto somewhere;
}
stack_pointer += -1 - oparg; stack_pointer += -1 - oparg;
assert(WITHIN_STACK_BOUNDS()); assert(WITHIN_STACK_BOUNDS());
DISPATCH(); DISPATCH();
@ -908,7 +912,6 @@ class TestGeneratedCases(unittest.TestCase):
next_instr += 1; next_instr += 1;
INSTRUCTION_STATS(TEST); INSTRUCTION_STATS(TEST);
_PyStackRef w; _PyStackRef w;
_PyStackRef x;
_PyStackRef y; _PyStackRef y;
// FIRST // FIRST
w = stack_pointer[-1]; w = stack_pointer[-1];
@ -916,11 +919,10 @@ class TestGeneratedCases(unittest.TestCase):
use(w); use(w);
} }
// SECOND // SECOND
x = w;
{ {
} }
// THIRD // THIRD
y = x; y = w;
{ {
use(y); use(y);
} }
@ -1024,6 +1026,7 @@ class TestGeneratedCases(unittest.TestCase):
} }
op(THIRD, (j, k --)) { op(THIRD, (j, k --)) {
j,k; // Mark j and k as used
ERROR_IF(cond, error); ERROR_IF(cond, error);
} }
@ -1054,6 +1057,7 @@ class TestGeneratedCases(unittest.TestCase):
k = b; k = b;
j = a; j = a;
{ {
j,k; // Mark j and k as used
if (cond) goto pop_2_error; if (cond) goto pop_2_error;
} }
stack_pointer += -2; stack_pointer += -2;
@ -1063,6 +1067,51 @@ class TestGeneratedCases(unittest.TestCase):
""" """
self.run_cases_test(input, output) self.run_cases_test(input, output)
def test_push_then_error(self):
input = """
op(FIRST, ( -- a)) {
a = 1;
}
op(SECOND, (a -- a, b)) {
b = 1;
ERROR_IF(cond, error);
}
macro(TEST) = FIRST + SECOND;
"""
output = """
TARGET(TEST) {
frame->instr_ptr = next_instr;
next_instr += 1;
INSTRUCTION_STATS(TEST);
_PyStackRef a;
_PyStackRef b;
// FIRST
{
a = 1;
}
// SECOND
{
b = 1;
if (cond) {
stack_pointer[0] = a;
stack_pointer += 1;
assert(WITHIN_STACK_BOUNDS());
goto error;
}
}
stack_pointer[0] = a;
stack_pointer[1] = b;
stack_pointer += 2;
assert(WITHIN_STACK_BOUNDS());
DISPATCH();
}
"""
self.run_cases_test(input, output)
class TestGeneratedAbstractCases(unittest.TestCase): class TestGeneratedAbstractCases(unittest.TestCase):
def setUp(self) -> None: def setUp(self) -> None:

View file

@ -1357,8 +1357,8 @@ dummy_func(
(void)counter; (void)counter;
} }
op(_UNPACK_SEQUENCE, (seq -- unused[oparg])) { op(_UNPACK_SEQUENCE, (seq -- output[oparg])) {
_PyStackRef *top = stack_pointer + oparg - 1; _PyStackRef *top = output + oparg;
int res = _PyEval_UnpackIterableStackRef(tstate, seq, oparg, -1, top); int res = _PyEval_UnpackIterableStackRef(tstate, seq, oparg, -1, top);
DECREF_INPUTS(); DECREF_INPUTS();
ERROR_IF(res == 0, error); ERROR_IF(res == 0, error);
@ -1401,9 +1401,8 @@ dummy_func(
DECREF_INPUTS(); DECREF_INPUTS();
} }
inst(UNPACK_EX, (seq -- unused[oparg & 0xFF], unused, unused[oparg >> 8])) { inst(UNPACK_EX, (seq -- left[oparg & 0xFF], unused, right[oparg >> 8])) {
int totalargs = 1 + (oparg & 0xFF) + (oparg >> 8); _PyStackRef *top = right + (oparg >> 8);
_PyStackRef *top = stack_pointer + totalargs - 1;
int res = _PyEval_UnpackIterableStackRef(tstate, seq, oparg & 0xFF, oparg >> 8, top); int res = _PyEval_UnpackIterableStackRef(tstate, seq, oparg & 0xFF, oparg >> 8, top);
DECREF_INPUTS(); DECREF_INPUTS();
ERROR_IF(res == 0, error); ERROR_IF(res == 0, error);

View file

@ -1440,9 +1440,11 @@
case _UNPACK_SEQUENCE: { case _UNPACK_SEQUENCE: {
_PyStackRef seq; _PyStackRef seq;
_PyStackRef *output;
oparg = CURRENT_OPARG(); oparg = CURRENT_OPARG();
seq = stack_pointer[-1]; seq = stack_pointer[-1];
_PyStackRef *top = stack_pointer + oparg - 1; output = &stack_pointer[-1];
_PyStackRef *top = output + oparg;
int res = _PyEval_UnpackIterableStackRef(tstate, seq, oparg, -1, top); int res = _PyEval_UnpackIterableStackRef(tstate, seq, oparg, -1, top);
PyStackRef_CLOSE(seq); PyStackRef_CLOSE(seq);
if (res == 0) JUMP_TO_ERROR(); if (res == 0) JUMP_TO_ERROR();
@ -1532,14 +1534,15 @@
case _UNPACK_EX: { case _UNPACK_EX: {
_PyStackRef seq; _PyStackRef seq;
_PyStackRef *right;
oparg = CURRENT_OPARG(); oparg = CURRENT_OPARG();
seq = stack_pointer[-1]; seq = stack_pointer[-1];
int totalargs = 1 + (oparg & 0xFF) + (oparg >> 8); right = &stack_pointer[(oparg & 0xFF)];
_PyStackRef *top = stack_pointer + totalargs - 1; _PyStackRef *top = right + (oparg >> 8);
int res = _PyEval_UnpackIterableStackRef(tstate, seq, oparg & 0xFF, oparg >> 8, top); int res = _PyEval_UnpackIterableStackRef(tstate, seq, oparg & 0xFF, oparg >> 8, top);
PyStackRef_CLOSE(seq); PyStackRef_CLOSE(seq);
if (res == 0) JUMP_TO_ERROR(); if (res == 0) JUMP_TO_ERROR();
stack_pointer += (oparg >> 8) + (oparg & 0xFF); stack_pointer += (oparg & 0xFF) + (oparg >> 8);
assert(WITHIN_STACK_BOUNDS()); assert(WITHIN_STACK_BOUNDS());
break; break;
} }
@ -3595,6 +3598,7 @@
args = &stack_pointer[-oparg]; args = &stack_pointer[-oparg];
self_or_null = stack_pointer[-1 - oparg]; self_or_null = stack_pointer[-1 - oparg];
callable = stack_pointer[-2 - oparg]; callable = stack_pointer[-2 - oparg];
args = &stack_pointer[-oparg];
if (PyStackRef_TYPE(callable) == &PyMethod_Type && PyStackRef_IsNull(self_or_null)) { if (PyStackRef_TYPE(callable) == &PyMethod_Type && PyStackRef_IsNull(self_or_null)) {
PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable);
PyObject *self = ((PyMethodObject *)callable_o)->im_self; PyObject *self = ((PyMethodObject *)callable_o)->im_self;

View file

@ -610,11 +610,19 @@
for (int _i = oparg; --_i >= 0;) { for (int _i = oparg; --_i >= 0;) {
PyStackRef_CLOSE(values[_i]); PyStackRef_CLOSE(values[_i]);
} }
if (true) { stack_pointer += -oparg; goto error; } if (true) {
stack_pointer += -oparg;
assert(WITHIN_STACK_BOUNDS());
goto error;
}
} }
PyObject *list_o = _PyList_FromArraySteal(values_o, oparg); PyObject *list_o = _PyList_FromArraySteal(values_o, oparg);
STACKREFS_TO_PYOBJECTS_CLEANUP(values_o); STACKREFS_TO_PYOBJECTS_CLEANUP(values_o);
if (list_o == NULL) { stack_pointer += -oparg; goto error; } if (list_o == NULL) {
stack_pointer += -oparg;
assert(WITHIN_STACK_BOUNDS());
goto error;
}
list = PyStackRef_FromPyObjectSteal(list_o); list = PyStackRef_FromPyObjectSteal(list_o);
stack_pointer[-oparg] = list; stack_pointer[-oparg] = list;
stack_pointer += 1 - oparg; stack_pointer += 1 - oparg;
@ -634,7 +642,11 @@
for (int _i = oparg*2; --_i >= 0;) { for (int _i = oparg*2; --_i >= 0;) {
PyStackRef_CLOSE(values[_i]); PyStackRef_CLOSE(values[_i]);
} }
if (true) { stack_pointer += -oparg*2; goto error; } if (true) {
stack_pointer += -oparg*2;
assert(WITHIN_STACK_BOUNDS());
goto error;
}
} }
PyObject *map_o = _PyDict_FromItems( PyObject *map_o = _PyDict_FromItems(
values_o, 2, values_o, 2,
@ -644,7 +656,11 @@
for (int _i = oparg*2; --_i >= 0;) { for (int _i = oparg*2; --_i >= 0;) {
PyStackRef_CLOSE(values[_i]); PyStackRef_CLOSE(values[_i]);
} }
if (map_o == NULL) { stack_pointer += -oparg*2; goto error; } if (map_o == NULL) {
stack_pointer += -oparg*2;
assert(WITHIN_STACK_BOUNDS());
goto error;
}
map = PyStackRef_FromPyObjectSteal(map_o); map = PyStackRef_FromPyObjectSteal(map_o);
stack_pointer[-oparg*2] = map; stack_pointer[-oparg*2] = map;
stack_pointer += 1 - oparg*2; stack_pointer += 1 - oparg*2;
@ -664,7 +680,11 @@
for (int _i = oparg; --_i >= 0;) { for (int _i = oparg; --_i >= 0;) {
PyStackRef_CLOSE(values[_i]); PyStackRef_CLOSE(values[_i]);
} }
if (true) { stack_pointer += -oparg; goto error; } if (true) {
stack_pointer += -oparg;
assert(WITHIN_STACK_BOUNDS());
goto error;
}
} }
int err = 0; int err = 0;
for (int i = 0; i < oparg; i++) { for (int i = 0; i < oparg; i++) {
@ -676,7 +696,11 @@
} }
if (err != 0) { if (err != 0) {
Py_DECREF(set_o); Py_DECREF(set_o);
if (true) { stack_pointer += -oparg; goto error; } if (true) {
stack_pointer += -oparg;
assert(WITHIN_STACK_BOUNDS());
goto error;
}
} }
set = PyStackRef_FromPyObjectSteal(set_o); set = PyStackRef_FromPyObjectSteal(set_o);
stack_pointer[-oparg] = set; stack_pointer[-oparg] = set;
@ -703,7 +727,11 @@
PyStackRef_CLOSE(start); PyStackRef_CLOSE(start);
PyStackRef_CLOSE(stop); PyStackRef_CLOSE(stop);
PyStackRef_XCLOSE(step); PyStackRef_XCLOSE(step);
if (slice_o == NULL) { stack_pointer += -2 - ((oparg == 3) ? 1 : 0); goto error; } if (slice_o == NULL) {
stack_pointer += -2 - ((oparg == 3) ? 1 : 0);
assert(WITHIN_STACK_BOUNDS());
goto error;
}
slice = PyStackRef_FromPyObjectSteal(slice_o); slice = PyStackRef_FromPyObjectSteal(slice_o);
stack_pointer[-2 - ((oparg == 3) ? 1 : 0)] = slice; stack_pointer[-2 - ((oparg == 3) ? 1 : 0)] = slice;
stack_pointer += -1 - ((oparg == 3) ? 1 : 0); stack_pointer += -1 - ((oparg == 3) ? 1 : 0);
@ -723,14 +751,22 @@
for (int _i = oparg; --_i >= 0;) { for (int _i = oparg; --_i >= 0;) {
PyStackRef_CLOSE(pieces[_i]); PyStackRef_CLOSE(pieces[_i]);
} }
if (true) { stack_pointer += -oparg; goto error; } if (true) {
stack_pointer += -oparg;
assert(WITHIN_STACK_BOUNDS());
goto error;
}
} }
PyObject *str_o = _PyUnicode_JoinArray(&_Py_STR(empty), pieces_o, oparg); PyObject *str_o = _PyUnicode_JoinArray(&_Py_STR(empty), pieces_o, oparg);
STACKREFS_TO_PYOBJECTS_CLEANUP(pieces_o); STACKREFS_TO_PYOBJECTS_CLEANUP(pieces_o);
for (int _i = oparg; --_i >= 0;) { for (int _i = oparg; --_i >= 0;) {
PyStackRef_CLOSE(pieces[_i]); PyStackRef_CLOSE(pieces[_i]);
} }
if (str_o == NULL) { stack_pointer += -oparg; goto error; } if (str_o == NULL) {
stack_pointer += -oparg;
assert(WITHIN_STACK_BOUNDS());
goto error;
}
str = PyStackRef_FromPyObjectSteal(str_o); str = PyStackRef_FromPyObjectSteal(str_o);
stack_pointer[-oparg] = str; stack_pointer[-oparg] = str;
stack_pointer += 1 - oparg; stack_pointer += 1 - oparg;
@ -746,7 +782,11 @@
_PyStackRef tup; _PyStackRef tup;
values = &stack_pointer[-oparg]; values = &stack_pointer[-oparg];
PyObject *tup_o = _PyTuple_FromStackRefSteal(values, oparg); PyObject *tup_o = _PyTuple_FromStackRefSteal(values, oparg);
if (tup_o == NULL) { stack_pointer += -oparg; goto error; } if (tup_o == NULL) {
stack_pointer += -oparg;
assert(WITHIN_STACK_BOUNDS());
goto error;
}
tup = PyStackRef_FromPyObjectSteal(tup_o); tup = PyStackRef_FromPyObjectSteal(tup_o);
stack_pointer[-oparg] = tup; stack_pointer[-oparg] = tup;
stack_pointer += 1 - oparg; stack_pointer += 1 - oparg;
@ -780,7 +820,6 @@
self_or_null = stack_pointer[-1 - oparg]; self_or_null = stack_pointer[-1 - oparg];
callable = stack_pointer[-2 - oparg]; callable = stack_pointer[-2 - oparg];
{ {
args = &stack_pointer[-oparg];
uint16_t counter = read_u16(&this_instr[1].cache); uint16_t counter = read_u16(&this_instr[1].cache);
(void)counter; (void)counter;
#if ENABLE_SPECIALIZATION #if ENABLE_SPECIALIZATION
@ -795,7 +834,9 @@
} }
/* Skip 2 cache entries */ /* Skip 2 cache entries */
// _MAYBE_EXPAND_METHOD // _MAYBE_EXPAND_METHOD
args = &stack_pointer[-oparg];
{ {
args = &stack_pointer[-oparg];
if (PyStackRef_TYPE(callable) == &PyMethod_Type && PyStackRef_IsNull(self_or_null)) { if (PyStackRef_TYPE(callable) == &PyMethod_Type && PyStackRef_IsNull(self_or_null)) {
PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable);
PyObject *self = ((PyMethodObject *)callable_o)->im_self; PyObject *self = ((PyMethodObject *)callable_o)->im_self;
@ -813,6 +854,7 @@
} }
} }
// _DO_CALL // _DO_CALL
args = &stack_pointer[-oparg];
self_or_null = maybe_self; self_or_null = maybe_self;
callable = func; callable = func;
{ {
@ -852,7 +894,11 @@
for (int _i = oparg; --_i >= 0;) { for (int _i = oparg; --_i >= 0;) {
PyStackRef_CLOSE(args[_i]); PyStackRef_CLOSE(args[_i]);
} }
if (true) { stack_pointer += -2 - oparg; goto error; } if (true) {
stack_pointer += -2 - oparg;
assert(WITHIN_STACK_BOUNDS());
goto error;
}
} }
PyObject *res_o = PyObject_Vectorcall( PyObject *res_o = PyObject_Vectorcall(
callable_o, args_o, callable_o, args_o,
@ -881,7 +927,11 @@
for (int i = 0; i < total_args; i++) { for (int i = 0; i < total_args; i++) {
PyStackRef_CLOSE(args[i]); PyStackRef_CLOSE(args[i]);
} }
if (res_o == NULL) { stack_pointer += -2 - oparg; goto error; } if (res_o == NULL) {
stack_pointer += -2 - oparg;
assert(WITHIN_STACK_BOUNDS());
goto error;
}
res = PyStackRef_FromPyObjectSteal(res_o); res = PyStackRef_FromPyObjectSteal(res_o);
} }
// _CHECK_PERIODIC // _CHECK_PERIODIC
@ -1190,7 +1240,11 @@
for (int _i = oparg; --_i >= 0;) { for (int _i = oparg; --_i >= 0;) {
PyStackRef_CLOSE(args[_i]); PyStackRef_CLOSE(args[_i]);
} }
if (true) { stack_pointer += -2 - oparg; goto error; } if (true) {
stack_pointer += -2 - oparg;
assert(WITHIN_STACK_BOUNDS());
goto error;
}
} }
PyObject *res_o = tp->tp_vectorcall((PyObject *)tp, args_o, total_args, NULL); PyObject *res_o = tp->tp_vectorcall((PyObject *)tp, args_o, total_args, NULL);
STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); STACKREFS_TO_PYOBJECTS_CLEANUP(args_o);
@ -1199,7 +1253,11 @@
PyStackRef_CLOSE(args[i]); PyStackRef_CLOSE(args[i]);
} }
PyStackRef_CLOSE(callable); PyStackRef_CLOSE(callable);
if (res_o == NULL) { stack_pointer += -2 - oparg; goto error; } if (res_o == NULL) {
stack_pointer += -2 - oparg;
assert(WITHIN_STACK_BOUNDS());
goto error;
}
res = PyStackRef_FromPyObjectSteal(res_o); res = PyStackRef_FromPyObjectSteal(res_o);
} }
// _CHECK_PERIODIC // _CHECK_PERIODIC
@ -1247,7 +1305,11 @@
for (int _i = oparg; --_i >= 0;) { for (int _i = oparg; --_i >= 0;) {
PyStackRef_CLOSE(args[_i]); PyStackRef_CLOSE(args[_i]);
} }
if (true) { stack_pointer += -2 - oparg; goto error; } if (true) {
stack_pointer += -2 - oparg;
assert(WITHIN_STACK_BOUNDS());
goto error;
}
} }
PyObject *res_o = ((PyCFunctionFast)(void(*)(void))cfunc)( PyObject *res_o = ((PyCFunctionFast)(void(*)(void))cfunc)(
PyCFunction_GET_SELF(callable_o), PyCFunction_GET_SELF(callable_o),
@ -1260,7 +1322,11 @@
PyStackRef_CLOSE(args[i]); PyStackRef_CLOSE(args[i]);
} }
PyStackRef_CLOSE(callable); PyStackRef_CLOSE(callable);
if (res_o == NULL) { stack_pointer += -2 - oparg; goto error; } if (res_o == NULL) {
stack_pointer += -2 - oparg;
assert(WITHIN_STACK_BOUNDS());
goto error;
}
res = PyStackRef_FromPyObjectSteal(res_o); res = PyStackRef_FromPyObjectSteal(res_o);
} }
// _CHECK_PERIODIC // _CHECK_PERIODIC
@ -1310,7 +1376,11 @@
for (int _i = oparg; --_i >= 0;) { for (int _i = oparg; --_i >= 0;) {
PyStackRef_CLOSE(args[_i]); PyStackRef_CLOSE(args[_i]);
} }
if (true) { stack_pointer += -2 - oparg; goto error; } if (true) {
stack_pointer += -2 - oparg;
assert(WITHIN_STACK_BOUNDS());
goto error;
}
} }
PyObject *res_o = cfunc(PyCFunction_GET_SELF(callable_o), args_o, total_args, NULL); PyObject *res_o = cfunc(PyCFunction_GET_SELF(callable_o), args_o, total_args, NULL);
STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); STACKREFS_TO_PYOBJECTS_CLEANUP(args_o);
@ -1320,7 +1390,11 @@
PyStackRef_CLOSE(args[i]); PyStackRef_CLOSE(args[i]);
} }
PyStackRef_CLOSE(callable); PyStackRef_CLOSE(callable);
if (res_o == NULL) { stack_pointer += -2 - oparg; goto error; } if (res_o == NULL) {
stack_pointer += -2 - oparg;
assert(WITHIN_STACK_BOUNDS());
goto error;
}
res = PyStackRef_FromPyObjectSteal(res_o); res = PyStackRef_FromPyObjectSteal(res_o);
} }
// _CHECK_PERIODIC // _CHECK_PERIODIC
@ -1370,7 +1444,11 @@
assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
PyStackRef_CLOSE(arg); PyStackRef_CLOSE(arg);
PyStackRef_CLOSE(callable); PyStackRef_CLOSE(callable);
if (res_o == NULL) { stack_pointer += -2 - oparg; goto error; } if (res_o == NULL) {
stack_pointer += -2 - oparg;
assert(WITHIN_STACK_BOUNDS());
goto error;
}
res = PyStackRef_FromPyObjectSteal(res_o); res = PyStackRef_FromPyObjectSteal(res_o);
} }
// _CHECK_PERIODIC // _CHECK_PERIODIC
@ -1467,7 +1545,11 @@
PyStackRef_CLOSE(callargs_st); PyStackRef_CLOSE(callargs_st);
PyStackRef_XCLOSE(kwargs_st); PyStackRef_XCLOSE(kwargs_st);
assert(PyStackRef_AsPyObjectBorrow(PEEK(2 + (oparg & 1))) == NULL); assert(PyStackRef_AsPyObjectBorrow(PEEK(2 + (oparg & 1))) == NULL);
if (PyStackRef_IsNull(result)) { stack_pointer += -3 - (oparg & 1); goto error; } if (PyStackRef_IsNull(result)) {
stack_pointer += -3 - (oparg & 1);
assert(WITHIN_STACK_BOUNDS());
goto error;
}
stack_pointer[-3 - (oparg & 1)] = result; stack_pointer[-3 - (oparg & 1)] = result;
stack_pointer += -2 - (oparg & 1); stack_pointer += -2 - (oparg & 1);
assert(WITHIN_STACK_BOUNDS()); assert(WITHIN_STACK_BOUNDS());
@ -1625,7 +1707,11 @@
PyStackRef_CLOSE(args[_i]); PyStackRef_CLOSE(args[_i]);
} }
PyStackRef_CLOSE(kwnames); PyStackRef_CLOSE(kwnames);
if (true) { stack_pointer += -3 - oparg; goto error; } if (true) {
stack_pointer += -3 - oparg;
assert(WITHIN_STACK_BOUNDS());
goto error;
}
} }
PyObject *res_o = PyObject_Vectorcall( PyObject *res_o = PyObject_Vectorcall(
callable_o, args_o, callable_o, args_o,
@ -1655,7 +1741,11 @@
for (int i = 0; i < total_args; i++) { for (int i = 0; i < total_args; i++) {
PyStackRef_CLOSE(args[i]); PyStackRef_CLOSE(args[i]);
} }
if (res_o == NULL) { stack_pointer += -3 - oparg; goto error; } if (res_o == NULL) {
stack_pointer += -3 - oparg;
assert(WITHIN_STACK_BOUNDS());
goto error;
}
res = PyStackRef_FromPyObjectSteal(res_o); res = PyStackRef_FromPyObjectSteal(res_o);
stack_pointer[-3 - oparg] = res; stack_pointer[-3 - oparg] = res;
stack_pointer += -2 - oparg; stack_pointer += -2 - oparg;
@ -1785,7 +1875,11 @@
for (int _i = oparg; --_i >= 0;) { for (int _i = oparg; --_i >= 0;) {
PyStackRef_CLOSE(args[_i]); PyStackRef_CLOSE(args[_i]);
} }
if (true) { stack_pointer += -2 - oparg; goto error; } if (true) {
stack_pointer += -2 - oparg;
assert(WITHIN_STACK_BOUNDS());
goto error;
}
} }
PyObject *res_o = cfunc(self, (args_o + 1), nargs); PyObject *res_o = cfunc(self, (args_o + 1), nargs);
STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); STACKREFS_TO_PYOBJECTS_CLEANUP(args_o);
@ -1795,7 +1889,11 @@
PyStackRef_CLOSE(args[i]); PyStackRef_CLOSE(args[i]);
} }
PyStackRef_CLOSE(callable); PyStackRef_CLOSE(callable);
if (res_o == NULL) { stack_pointer += -2 - oparg; goto error; } if (res_o == NULL) {
stack_pointer += -2 - oparg;
assert(WITHIN_STACK_BOUNDS());
goto error;
}
res = PyStackRef_FromPyObjectSteal(res_o); res = PyStackRef_FromPyObjectSteal(res_o);
} }
// _CHECK_PERIODIC // _CHECK_PERIODIC
@ -1848,7 +1946,11 @@
for (int _i = oparg; --_i >= 0;) { for (int _i = oparg; --_i >= 0;) {
PyStackRef_CLOSE(args[_i]); PyStackRef_CLOSE(args[_i]);
} }
if (true) { stack_pointer += -2 - oparg; goto error; } if (true) {
stack_pointer += -2 - oparg;
assert(WITHIN_STACK_BOUNDS());
goto error;
}
} }
PyObject *res_o = cfunc(self, (args_o + 1), nargs, NULL); PyObject *res_o = cfunc(self, (args_o + 1), nargs, NULL);
STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); STACKREFS_TO_PYOBJECTS_CLEANUP(args_o);
@ -1858,7 +1960,11 @@
PyStackRef_CLOSE(args[i]); PyStackRef_CLOSE(args[i]);
} }
PyStackRef_CLOSE(callable); PyStackRef_CLOSE(callable);
if (res_o == NULL) { stack_pointer += -2 - oparg; goto error; } if (res_o == NULL) {
stack_pointer += -2 - oparg;
assert(WITHIN_STACK_BOUNDS());
goto error;
}
res = PyStackRef_FromPyObjectSteal(res_o); res = PyStackRef_FromPyObjectSteal(res_o);
} }
// _CHECK_PERIODIC // _CHECK_PERIODIC
@ -1912,7 +2018,11 @@
assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
PyStackRef_CLOSE(self_stackref); PyStackRef_CLOSE(self_stackref);
PyStackRef_CLOSE(callable); PyStackRef_CLOSE(callable);
if (res_o == NULL) { stack_pointer += -2 - oparg; goto error; } if (res_o == NULL) {
stack_pointer += -2 - oparg;
assert(WITHIN_STACK_BOUNDS());
goto error;
}
res = PyStackRef_FromPyObjectSteal(res_o); res = PyStackRef_FromPyObjectSteal(res_o);
} }
// _CHECK_PERIODIC // _CHECK_PERIODIC
@ -1969,7 +2079,11 @@
PyStackRef_CLOSE(self_stackref); PyStackRef_CLOSE(self_stackref);
PyStackRef_CLOSE(arg_stackref); PyStackRef_CLOSE(arg_stackref);
PyStackRef_CLOSE(callable); PyStackRef_CLOSE(callable);
if (res_o == NULL) { stack_pointer += -2 - oparg; goto error; } if (res_o == NULL) {
stack_pointer += -2 - oparg;
assert(WITHIN_STACK_BOUNDS());
goto error;
}
res = PyStackRef_FromPyObjectSteal(res_o); res = PyStackRef_FromPyObjectSteal(res_o);
} }
// _CHECK_PERIODIC // _CHECK_PERIODIC
@ -2022,7 +2136,11 @@
for (int _i = oparg; --_i >= 0;) { for (int _i = oparg; --_i >= 0;) {
PyStackRef_CLOSE(args[_i]); PyStackRef_CLOSE(args[_i]);
} }
if (true) { stack_pointer += -2 - oparg; goto error; } if (true) {
stack_pointer += -2 - oparg;
assert(WITHIN_STACK_BOUNDS());
goto error;
}
} }
PyObject *res_o = PyObject_Vectorcall( PyObject *res_o = PyObject_Vectorcall(
callable_o, args_o, callable_o, args_o,
@ -2034,7 +2152,11 @@
for (int i = 0; i < total_args; i++) { for (int i = 0; i < total_args; i++) {
PyStackRef_CLOSE(args[i]); PyStackRef_CLOSE(args[i]);
} }
if (res_o == NULL) { stack_pointer += -2 - oparg; goto error; } if (res_o == NULL) {
stack_pointer += -2 - oparg;
assert(WITHIN_STACK_BOUNDS());
goto error;
}
res = PyStackRef_FromPyObjectSteal(res_o); res = PyStackRef_FromPyObjectSteal(res_o);
} }
// _CHECK_PERIODIC // _CHECK_PERIODIC
@ -3526,6 +3648,7 @@
self_or_null = stack_pointer[-1 - oparg]; self_or_null = stack_pointer[-1 - oparg];
callable = stack_pointer[-2 - oparg]; callable = stack_pointer[-2 - oparg];
{ {
args = &stack_pointer[-oparg];
if (PyStackRef_TYPE(callable) == &PyMethod_Type && PyStackRef_IsNull(self_or_null)) { if (PyStackRef_TYPE(callable) == &PyMethod_Type && PyStackRef_IsNull(self_or_null)) {
PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable);
PyObject *self = ((PyMethodObject *)callable_o)->im_self; PyObject *self = ((PyMethodObject *)callable_o)->im_self;
@ -3543,6 +3666,7 @@
} }
} }
// _MONITOR_CALL // _MONITOR_CALL
args = &stack_pointer[-oparg];
{ {
int is_meth = !PyStackRef_IsNull(maybe_self); int is_meth = !PyStackRef_IsNull(maybe_self);
PyObject *function = PyStackRef_AsPyObjectBorrow(func); PyObject *function = PyStackRef_AsPyObjectBorrow(func);
@ -3563,6 +3687,7 @@
if (err) goto error; if (err) goto error;
} }
// _DO_CALL // _DO_CALL
args = &stack_pointer[-oparg];
self_or_null = maybe_self; self_or_null = maybe_self;
callable = func; callable = func;
{ {
@ -3602,7 +3727,11 @@
for (int _i = oparg; --_i >= 0;) { for (int _i = oparg; --_i >= 0;) {
PyStackRef_CLOSE(args[_i]); PyStackRef_CLOSE(args[_i]);
} }
if (true) { stack_pointer += -2 - oparg; goto error; } if (true) {
stack_pointer += -2 - oparg;
assert(WITHIN_STACK_BOUNDS());
goto error;
}
} }
PyObject *res_o = PyObject_Vectorcall( PyObject *res_o = PyObject_Vectorcall(
callable_o, args_o, callable_o, args_o,
@ -3631,7 +3760,11 @@
for (int i = 0; i < total_args; i++) { for (int i = 0; i < total_args; i++) {
PyStackRef_CLOSE(args[i]); PyStackRef_CLOSE(args[i]);
} }
if (res_o == NULL) { stack_pointer += -2 - oparg; goto error; } if (res_o == NULL) {
stack_pointer += -2 - oparg;
assert(WITHIN_STACK_BOUNDS());
goto error;
}
res = PyStackRef_FromPyObjectSteal(res_o); res = PyStackRef_FromPyObjectSteal(res_o);
} }
// _CHECK_PERIODIC // _CHECK_PERIODIC
@ -5777,7 +5910,11 @@
"bad RAISE_VARARGS oparg"); "bad RAISE_VARARGS oparg");
break; break;
} }
if (true) { stack_pointer += -oparg; goto error; } if (true) {
stack_pointer += -oparg;
assert(WITHIN_STACK_BOUNDS());
goto error;
}
} }
TARGET(RERAISE) { TARGET(RERAISE) {
@ -6834,13 +6971,14 @@
next_instr += 1; next_instr += 1;
INSTRUCTION_STATS(UNPACK_EX); INSTRUCTION_STATS(UNPACK_EX);
_PyStackRef seq; _PyStackRef seq;
_PyStackRef *right;
seq = stack_pointer[-1]; seq = stack_pointer[-1];
int totalargs = 1 + (oparg & 0xFF) + (oparg >> 8); right = &stack_pointer[(oparg & 0xFF)];
_PyStackRef *top = stack_pointer + totalargs - 1; _PyStackRef *top = right + (oparg >> 8);
int res = _PyEval_UnpackIterableStackRef(tstate, seq, oparg & 0xFF, oparg >> 8, top); int res = _PyEval_UnpackIterableStackRef(tstate, seq, oparg & 0xFF, oparg >> 8, top);
PyStackRef_CLOSE(seq); PyStackRef_CLOSE(seq);
if (res == 0) goto pop_1_error; if (res == 0) goto pop_1_error;
stack_pointer += (oparg >> 8) + (oparg & 0xFF); stack_pointer += (oparg & 0xFF) + (oparg >> 8);
assert(WITHIN_STACK_BOUNDS()); assert(WITHIN_STACK_BOUNDS());
DISPATCH(); DISPATCH();
} }
@ -6853,6 +6991,7 @@
_Py_CODEUNIT *this_instr = next_instr - 2; _Py_CODEUNIT *this_instr = next_instr - 2;
(void)this_instr; (void)this_instr;
_PyStackRef seq; _PyStackRef seq;
_PyStackRef *output;
// _SPECIALIZE_UNPACK_SEQUENCE // _SPECIALIZE_UNPACK_SEQUENCE
seq = stack_pointer[-1]; seq = stack_pointer[-1];
{ {
@ -6872,7 +7011,8 @@
} }
// _UNPACK_SEQUENCE // _UNPACK_SEQUENCE
{ {
_PyStackRef *top = stack_pointer + oparg - 1; output = &stack_pointer[-1];
_PyStackRef *top = output + oparg;
int res = _PyEval_UnpackIterableStackRef(tstate, seq, oparg, -1, top); int res = _PyEval_UnpackIterableStackRef(tstate, seq, oparg, -1, top);
PyStackRef_CLOSE(seq); PyStackRef_CLOSE(seq);
if (res == 0) goto pop_1_error; if (res == 0) goto pop_1_error;

View file

@ -753,7 +753,7 @@
for (int i = 0; i < totalargs; i++) { for (int i = 0; i < totalargs; i++) {
values[i] = sym_new_unknown(ctx); values[i] = sym_new_unknown(ctx);
} }
stack_pointer += (oparg >> 8) + (oparg & 0xFF); stack_pointer += (oparg & 0xFF) + (oparg >> 8);
assert(WITHIN_STACK_BOUNDS()); assert(WITHIN_STACK_BOUNDS());
break; break;
} }
@ -1607,6 +1607,7 @@
args = &stack_pointer[-oparg]; args = &stack_pointer[-oparg];
self_or_null = stack_pointer[-1 - oparg]; self_or_null = stack_pointer[-1 - oparg];
callable = stack_pointer[-2 - oparg]; callable = stack_pointer[-2 - oparg];
args = &stack_pointer[-oparg];
(void)callable; (void)callable;
(void)self_or_null; (void)self_or_null;
(void)args; (void)args;

View file

@ -216,6 +216,7 @@ Part = Uop | Skip | Flush
@dataclass @dataclass
class Instruction: class Instruction:
where: lexer.Token
name: str name: str
parts: list[Part] parts: list[Part]
_properties: Properties | None _properties: Properties | None
@ -690,9 +691,10 @@ def add_op(op: parser.InstDef, uops: dict[str, Uop]) -> None:
def add_instruction( def add_instruction(
name: str, parts: list[Part], instructions: dict[str, Instruction] where: lexer.Token, name: str, parts: list[Part],
instructions: dict[str, Instruction]
) -> None: ) -> None:
instructions[name] = Instruction(name, parts, None) instructions[name] = Instruction(where, name, parts, None)
def desugar_inst( def desugar_inst(
@ -720,7 +722,7 @@ def desugar_inst(
parts.append(uop) parts.append(uop)
else: else:
parts[uop_index] = uop parts[uop_index] = uop
add_instruction(name, parts, instructions) add_instruction(inst.first_token, name, parts, instructions)
def add_macro( def add_macro(
@ -741,7 +743,7 @@ def add_macro(
case _: case _:
assert False assert False
assert parts assert parts
add_instruction(macro.name, parts, instructions) add_instruction(macro.first_token, macro.name, parts, instructions)
def add_family( def add_family(

View file

@ -8,7 +8,7 @@ from analyzer import (
StackItem, StackItem,
) )
from cwriter import CWriter from cwriter import CWriter
from typing import Callable, Mapping, TextIO, Iterator, Tuple from typing import Callable, Mapping, TextIO, Iterator
from lexer import Token from lexer import Token
from stack import Stack from stack import Stack
@ -25,7 +25,7 @@ def root_relative_path(filename: str) -> str:
return filename return filename
def type_and_null(var: StackItem) -> Tuple[str, str]: def type_and_null(var: StackItem) -> tuple[str, str]:
if var.type: if var.type:
return var.type, "NULL" return var.type, "NULL"
elif var.is_array(): elif var.is_array():
@ -95,16 +95,23 @@ def replace_error(
c_offset = stack.peek_offset() c_offset = stack.peek_offset()
try: try:
offset = -int(c_offset) offset = -int(c_offset)
close = ";\n"
except ValueError: except ValueError:
offset = None offset = -1
out.emit(f"{{ stack_pointer += {c_offset}; ") if offset > 0:
close = "; }\n" out.emit(f"goto pop_{offset}_")
out.emit("goto ") out.emit(label)
if offset: out.emit(";\n")
out.emit(f"pop_{offset}_") elif offset == 0:
out.emit(label) out.emit("goto ")
out.emit(close) out.emit(label)
out.emit(";\n")
else:
out.emit("{\n")
stack.flush_locally(out)
out.emit("goto ")
out.emit(label)
out.emit(";\n")
out.emit("}\n")
def replace_error_no_pop( def replace_error_no_pop(

View file

@ -23,7 +23,7 @@ from generators_common import (
from cwriter import CWriter from cwriter import CWriter
from typing import TextIO, Iterator from typing import TextIO, Iterator
from lexer import Token from lexer import Token
from stack import Stack, StackError from stack import Local, Stack, StackError
DEFAULT_OUTPUT = ROOT / "Python/optimizer_cases.c.h" DEFAULT_OUTPUT = ROOT / "Python/optimizer_cases.c.h"
DEFAULT_ABSTRACT_INPUT = (ROOT / "Python/optimizer_bytecodes.c").absolute().as_posix() DEFAULT_ABSTRACT_INPUT = (ROOT / "Python/optimizer_bytecodes.c").absolute().as_posix()
@ -98,19 +98,18 @@ def write_uop(
debug: bool, debug: bool,
skip_inputs: bool, skip_inputs: bool,
) -> None: ) -> None:
locals: dict[str, Local] = {}
try: try:
prototype = override if override else uop prototype = override if override else uop
is_override = override is not None is_override = override is not None
out.start_line() out.start_line()
for var in reversed(prototype.stack.inputs): for var in reversed(prototype.stack.inputs):
res = stack.pop(var, extract_bits=True) code, local = stack.pop(var, extract_bits=True)
if not skip_inputs: if not skip_inputs:
out.emit(res) out.emit(code)
if not prototype.properties.stores_sp: if local.defined:
for i, var in enumerate(prototype.stack.outputs): locals[local.name] = local
res = stack.push(var) out.emit(stack.define_output_arrays(prototype.stack.outputs))
if not var.peek or is_override:
out.emit(res)
if debug: if debug:
args = [] args = []
for var in prototype.stack.inputs: for var in prototype.stack.inputs:
@ -135,10 +134,12 @@ def write_uop(
else: else:
emit_default(out, uop) emit_default(out, uop)
if prototype.properties.stores_sp: for var in prototype.stack.outputs:
for i, var in enumerate(prototype.stack.outputs): if var.name in locals:
if not var.peek or is_override: local = locals[var.name]
out.emit(stack.push(var)) else:
local = Local.local(var)
out.emit(stack.push(local))
out.start_line() out.start_line()
stack.flush(out, cast_type="_Py_UopsSymbol *", extract_bits=True) stack.flush(out, cast_type="_Py_UopsSymbol *", extract_bits=True)
except StackError as ex: except StackError as ex:

View file

@ -60,6 +60,11 @@ class Node:
end = context.end end = context.end
return tokens[begin:end] return tokens[begin:end]
@property
def first_token(self) -> lx.Token:
context = self.context
assert context is not None
return context.owner.tokens[context.begin]
@dataclass @dataclass
class Block(Node): class Block(Node):

View file

@ -38,6 +38,43 @@ def var_size(var: StackItem) -> str:
else: else:
return "1" return "1"
@dataclass
class Local:
item: StackItem
cached: bool
in_memory: bool
defined: bool
@staticmethod
def unused(defn: StackItem) -> "Local":
return Local(defn, False, defn.is_array(), False)
@staticmethod
def local(defn: StackItem) -> "Local":
array = defn.is_array()
return Local(defn, not array, array, True)
@staticmethod
def redefinition(var: StackItem, prev: "Local") -> "Local":
assert var.is_array() == prev.is_array()
return Local(var, prev.cached, prev.in_memory, True)
@property
def size(self) -> str:
return self.item.size
@property
def name(self) -> str:
return self.item.name
@property
def condition(self) -> str | None:
return self.item.condition
def is_array(self) -> bool:
return self.item.is_array()
@dataclass @dataclass
class StackOffset: class StackOffset:
"The stack offset of the virtual base of the stack from the physical stack pointer" "The stack offset of the virtual base of the stack from the physical stack pointer"
@ -66,7 +103,11 @@ class StackOffset:
def simplify(self) -> None: def simplify(self) -> None:
"Remove matching values from both the popped and pushed list" "Remove matching values from both the popped and pushed list"
if not self.popped or not self.pushed: if not self.popped:
self.pushed.sort()
return
if not self.pushed:
self.popped.sort()
return return
# Sort the list so the lexically largest element is last. # Sort the list so the lexically largest element is last.
popped = sorted(self.popped) popped = sorted(self.popped)
@ -87,6 +128,8 @@ class StackOffset:
popped.append(pop) popped.append(pop)
self.popped.extend(popped) self.popped.extend(popped)
self.pushed.extend(pushed) self.pushed.extend(pushed)
self.pushed.sort()
self.popped.sort()
def to_c(self) -> str: def to_c(self) -> str:
self.simplify() self.simplify()
@ -125,10 +168,10 @@ class Stack:
def __init__(self) -> None: def __init__(self) -> None:
self.top_offset = StackOffset.empty() self.top_offset = StackOffset.empty()
self.base_offset = StackOffset.empty() self.base_offset = StackOffset.empty()
self.variables: list[StackItem] = [] self.variables: list[Local] = []
self.defined: set[str] = set() self.defined: set[str] = set()
def pop(self, var: StackItem, extract_bits: bool = False) -> str: def pop(self, var: StackItem, extract_bits: bool = False) -> tuple[str, Local]:
self.top_offset.pop(var) self.top_offset.pop(var)
indirect = "&" if var.is_array() else "" indirect = "&" if var.is_array() else ""
if self.variables: if self.variables:
@ -141,21 +184,32 @@ class Stack:
if var.name in UNUSED: if var.name in UNUSED:
if popped.name not in UNUSED and popped.name in self.defined: if popped.name not in UNUSED and popped.name in self.defined:
raise StackError(f"Value is declared unused, but is already cached by prior operation") raise StackError(f"Value is declared unused, but is already cached by prior operation")
return "" return "", popped
if popped.name in UNUSED or popped.name not in self.defined: if not var.used:
self.defined.add(var.name) return "", popped
self.defined.add(var.name)
# Always define array variables as it is free, and their offset might have changed
if var.is_array():
return ( return (
f"{var.name} = {indirect}stack_pointer[{self.top_offset.to_c()}];\n" f"{var.name} = &stack_pointer[{self.top_offset.to_c()}];\n",
Local.redefinition(var, popped)
)
if not popped.defined:
return (
f"{var.name} = stack_pointer[{self.top_offset.to_c()}];\n",
Local.redefinition(var, popped)
) )
else: else:
self.defined.add(var.name)
if popped.name == var.name: if popped.name == var.name:
return "" return "", popped
else: else:
return f"{var.name} = {popped.name};\n" return (
f"{var.name} = {popped.name};\n",
Local.redefinition(var, popped)
)
self.base_offset.pop(var) self.base_offset.pop(var)
if var.name in UNUSED or not var.used: if var.name in UNUSED or not var.used:
return "" return "", Local.unused(var)
self.defined.add(var.name) self.defined.add(var.name)
cast = f"({var.type})" if (not indirect and var.type) else "" cast = f"({var.type})" if (not indirect and var.type) else ""
bits = ".bits" if cast and not extract_bits else "" bits = ".bits" if cast and not extract_bits else ""
@ -164,61 +218,80 @@ class Stack:
) )
if var.condition: if var.condition:
if var.condition == "1": if var.condition == "1":
return f"{assign}\n" assign = f"{assign}\n"
elif var.condition == "0": elif var.condition == "0":
return "" return "", Local.unused(var)
else: else:
return f"if ({var.condition}) {{ {assign} }}\n" assign = f"if ({var.condition}) {{ {assign} }}\n"
return f"{assign}\n" else:
assign = f"{assign}\n"
in_memory = var.is_array() or var.peek
return assign, Local(var, not var.is_array(), in_memory, True)
def push(self, var: StackItem) -> str: def push(self, var: Local) -> str:
self.variables.append(var) self.variables.append(var)
if var.is_array() and var.name not in self.defined and var.name not in UNUSED: if var.is_array() and not var.defined and var.item.used:
assert var.in_memory
assert not var.cached
c_offset = self.top_offset.to_c() c_offset = self.top_offset.to_c()
self.top_offset.push(var) self.top_offset.push(var.item)
self.defined.add(var.name) self.defined.add(var.name)
var.defined = True
return f"{var.name} = &stack_pointer[{c_offset}];\n" return f"{var.name} = &stack_pointer[{c_offset}];\n"
else: else:
self.top_offset.push(var) self.top_offset.push(var.item)
if var.used: if var.item.used:
self.defined.add(var.name) self.defined.add(var.name)
return "" return ""
def flush(self, out: CWriter, cast_type: str = "uintptr_t", extract_bits: bool = False) -> None: def define_output_arrays(self, outputs: list[StackItem]) -> str:
res = []
top_offset = self.top_offset.copy()
for var in outputs:
if var.is_array() and var.used and not var.peek:
c_offset = top_offset.to_c()
top_offset.push(var)
res.append(f"{var.name} = &stack_pointer[{c_offset}];\n")
else:
top_offset.push(var)
return "\n".join(res)
@staticmethod
def _do_flush(out: CWriter, variables: list[Local], base_offset: StackOffset, top_offset: StackOffset,
cast_type: str = "uintptr_t", extract_bits: bool = False) -> None:
out.start_line() out.start_line()
for var in self.variables: for var in variables:
if not var.peek: if var.cached and not var.in_memory and not var.item.peek and not var.name in UNUSED:
cast = f"({cast_type})" if var.type else "" cast = f"({cast_type})" if var.item.type else ""
bits = ".bits" if cast and not extract_bits else "" bits = ".bits" if cast and not extract_bits else ""
if var.name not in UNUSED and not var.is_array(): if var.condition == "0":
if var.condition: continue
if var.condition == "0": if var.condition and var.condition != "1":
continue out.emit(f"if ({var.condition}) ")
elif var.condition != "1": out.emit(
out.emit(f"if ({var.condition}) ") f"stack_pointer[{base_offset.to_c()}]{bits} = {cast}{var.name};\n"
out.emit( )
f"stack_pointer[{self.base_offset.to_c()}]{bits} = {cast}{var.name};\n" base_offset.push(var.item)
) if base_offset.to_c() != top_offset.to_c():
self.base_offset.push(var) print("base", base_offset, "top", top_offset)
if self.base_offset.to_c() != self.top_offset.to_c():
print("base", self.base_offset.to_c(), "top", self.top_offset.to_c())
assert False assert False
number = self.base_offset.to_c() number = base_offset.to_c()
if number != "0": if number != "0":
out.emit(f"stack_pointer += {number};\n") out.emit(f"stack_pointer += {number};\n")
out.emit("assert(WITHIN_STACK_BOUNDS());\n") out.emit("assert(WITHIN_STACK_BOUNDS());\n")
out.start_line()
def flush_locally(self, out: CWriter, cast_type: str = "uintptr_t", extract_bits: bool = False) -> None:
self._do_flush(out, self.variables[:], self.base_offset.copy(), self.top_offset.copy(), cast_type, extract_bits)
def flush(self, out: CWriter, cast_type: str = "uintptr_t", extract_bits: bool = False) -> None:
self._do_flush(out, self.variables, self.base_offset, self.top_offset, cast_type, extract_bits)
self.variables = [] self.variables = []
self.base_offset.clear() self.base_offset.clear()
self.top_offset.clear() self.top_offset.clear()
out.start_line()
def peek_offset(self) -> str: def peek_offset(self) -> str:
peek = self.base_offset.copy() return self.top_offset.to_c()
for var in self.variables:
if not var.peek:
break
peek.push(var)
return peek.to_c()
def as_comment(self) -> str: def as_comment(self) -> str:
return f"/* Variables: {[v.name for v in self.variables]}. Base offset: {self.base_offset.to_c()}. Top offset: {self.top_offset.to_c()} */" return f"/* Variables: {[v.name for v in self.variables]}. Base offset: {self.base_offset.to_c()}. Top offset: {self.top_offset.to_c()} */"
@ -236,8 +309,15 @@ def get_stack_effect(inst: Instruction | PseudoInstruction) -> Stack:
yield inst.stack yield inst.stack
for s in stacks(inst): for s in stacks(inst):
locals: dict[str, Local] = {}
for var in reversed(s.inputs): for var in reversed(s.inputs):
stack.pop(var) _, local = stack.pop(var)
if var.name != "unused":
locals[local.name] = local
for var in s.outputs: for var in s.outputs:
stack.push(var) if var.name in locals:
local = locals[var.name]
else:
local = Local.unused(var)
stack.push(local)
return stack return stack

View file

@ -25,7 +25,7 @@ from generators_common import (
) )
from cwriter import CWriter from cwriter import CWriter
from typing import TextIO from typing import TextIO
from stack import Stack, StackError from stack import Local, Stack, StackError, get_stack_effect
DEFAULT_OUTPUT = ROOT / "Python/generated_cases.c.h" DEFAULT_OUTPUT = ROOT / "Python/generated_cases.c.h"
@ -43,18 +43,12 @@ def declare_variable(var: StackItem, out: CWriter) -> None:
def declare_variables(inst: Instruction, out: CWriter) -> None: def declare_variables(inst: Instruction, out: CWriter) -> None:
stack = Stack() try:
for part in inst.parts: stack = get_stack_effect(inst)
if not isinstance(part, Uop): except StackError as ex:
continue raise analysis_error(ex.args[0], inst.where)
try:
for var in reversed(part.stack.inputs):
stack.pop(var)
for var in part.stack.outputs:
stack.push(var)
except StackError as ex:
raise analysis_error(ex.args[0], part.body[0]) from None
required = set(stack.defined) required = set(stack.defined)
required.discard("unused")
for part in inst.parts: for part in inst.parts:
if not isinstance(part, Uop): if not isinstance(part, Uop):
continue continue
@ -80,16 +74,26 @@ def write_uop(
stack.flush(out) stack.flush(out)
return offset return offset
try: try:
locals: dict[str, Local] = {}
out.start_line() out.start_line()
if braces: if braces:
out.emit(f"// {uop.name}\n") out.emit(f"// {uop.name}\n")
peeks: list[Local] = []
for var in reversed(uop.stack.inputs): for var in reversed(uop.stack.inputs):
out.emit(stack.pop(var)) code, local = stack.pop(var)
out.emit(code)
if var.peek:
peeks.append(local)
if local.defined:
locals[local.name] = local
# Push back the peeks, so that they remain on the logical
# stack, but their values are cached.
while peeks:
stack.push(peeks.pop())
if braces: if braces:
out.emit("{\n") out.emit("{\n")
if not uop.properties.stores_sp: out.emit(stack.define_output_arrays(uop.stack.outputs))
for i, var in enumerate(uop.stack.outputs):
out.emit(stack.push(var))
for cache in uop.caches: for cache in uop.caches:
if cache.name != "unused": if cache.name != "unused":
if cache.size == 4: if cache.size == 4:
@ -105,16 +109,22 @@ def write_uop(
out.emit(f"(void){cache.name};\n") out.emit(f"(void){cache.name};\n")
offset += cache.size offset += cache.size
emit_tokens(out, uop, stack, inst) emit_tokens(out, uop, stack, inst)
if uop.properties.stores_sp: for i, var in enumerate(uop.stack.outputs):
for i, var in enumerate(uop.stack.outputs): if not var.peek:
out.emit(stack.push(var)) if var.name in locals:
local = locals[var.name]
elif var.name == "unused":
local = Local.unused(var)
else:
local = Local.local(var)
out.emit(stack.push(local))
if braces: if braces:
out.start_line() out.start_line()
out.emit("}\n") out.emit("}\n")
# out.emit(stack.as_comment() + "\n") # out.emit(stack.as_comment() + "\n")
return offset return offset
except StackError as ex: except StackError as ex:
raise analysis_error(ex.args[0], uop.body[0]) from None raise analysis_error(ex.args[0], uop.body[0])
def uses_this(inst: Instruction) -> bool: def uses_this(inst: Instruction) -> bool:

View file

@ -25,7 +25,7 @@ from generators_common import (
from cwriter import CWriter from cwriter import CWriter
from typing import TextIO, Iterator from typing import TextIO, Iterator
from lexer import Token from lexer import Token
from stack import Stack, StackError from stack import Local, Stack, StackError, get_stack_effect
DEFAULT_OUTPUT = ROOT / "Python/executor_cases.c.h" DEFAULT_OUTPUT = ROOT / "Python/executor_cases.c.h"
@ -53,8 +53,9 @@ def declare_variables(uop: Uop, out: CWriter) -> None:
for var in reversed(uop.stack.inputs): for var in reversed(uop.stack.inputs):
stack.pop(var) stack.pop(var)
for var in uop.stack.outputs: for var in uop.stack.outputs:
stack.push(var) stack.push(Local.unused(var))
required = set(stack.defined) required = set(stack.defined)
required.discard("unused")
for var in reversed(uop.stack.inputs): for var in reversed(uop.stack.inputs):
declare_variable(var, uop, required, out) declare_variable(var, uop, required, out)
for var in uop.stack.outputs: for var in uop.stack.outputs:
@ -156,6 +157,7 @@ TIER2_REPLACEMENT_FUNCTIONS["EXIT_IF"] = tier2_replace_exit_if
def write_uop(uop: Uop, out: CWriter, stack: Stack) -> None: def write_uop(uop: Uop, out: CWriter, stack: Stack) -> None:
locals: dict[str, Local] = {}
try: try:
out.start_line() out.start_line()
if uop.properties.oparg: if uop.properties.oparg:
@ -165,10 +167,11 @@ def write_uop(uop: Uop, out: CWriter, stack: Stack) -> None:
out.emit(f"oparg = {uop.properties.const_oparg};\n") out.emit(f"oparg = {uop.properties.const_oparg};\n")
out.emit(f"assert(oparg == CURRENT_OPARG());\n") out.emit(f"assert(oparg == CURRENT_OPARG());\n")
for var in reversed(uop.stack.inputs): for var in reversed(uop.stack.inputs):
out.emit(stack.pop(var)) code, local = stack.pop(var)
if not uop.properties.stores_sp: out.emit(code)
for i, var in enumerate(uop.stack.outputs): if local.defined:
out.emit(stack.push(var)) locals[local.name] = local
out.emit(stack.define_output_arrays(uop.stack.outputs))
for cache in uop.caches: for cache in uop.caches:
if cache.name != "unused": if cache.name != "unused":
if cache.size == 4: if cache.size == 4:
@ -178,9 +181,12 @@ def write_uop(uop: Uop, out: CWriter, stack: Stack) -> None:
cast = f"uint{cache.size*16}_t" cast = f"uint{cache.size*16}_t"
out.emit(f"{type}{cache.name} = ({cast})CURRENT_OPERAND();\n") out.emit(f"{type}{cache.name} = ({cast})CURRENT_OPERAND();\n")
emit_tokens(out, uop, stack, None, TIER2_REPLACEMENT_FUNCTIONS) emit_tokens(out, uop, stack, None, TIER2_REPLACEMENT_FUNCTIONS)
if uop.properties.stores_sp: for i, var in enumerate(uop.stack.outputs):
for i, var in enumerate(uop.stack.outputs): if var.name in locals:
out.emit(stack.push(var)) local = locals[var.name]
else:
local = Local.local(var)
out.emit(stack.push(local))
except StackError as ex: except StackError as ex:
raise analysis_error(ex.args[0], uop.body[0]) from None raise analysis_error(ex.args[0], uop.body[0]) from None