]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
GH-122155: Track local variables between pops and pushes in cases generator (GH-122286)
authorMark Shannon <mark@hotpy.org>
Thu, 1 Aug 2024 08:27:26 +0000 (09:27 +0100)
committerGitHub <noreply@github.com>
Thu, 1 Aug 2024 08:27:26 +0000 (09:27 +0100)
13 files changed:
Include/internal/pycore_opcode_metadata.h
Lib/test/test_generated_cases.py
Python/bytecodes.c
Python/executor_cases.c.h
Python/generated_cases.c.h
Python/optimizer_cases.c.h
Tools/cases_generator/analyzer.py
Tools/cases_generator/generators_common.py
Tools/cases_generator/optimizer_generator.py
Tools/cases_generator/parsing.py
Tools/cases_generator/stack.py
Tools/cases_generator/tier1_generator.py
Tools/cases_generator/tier2_generator.py

index eaba280f1bf1cd02d55879eaac2c55e49f41b452..464d3500890e53a52962b071516e31f5acd1fc07 100644 (file)
@@ -903,7 +903,7 @@ int _PyOpcode_num_pushed(int opcode, int oparg)  {
         case UNARY_NOT:
             return 1;
         case UNPACK_EX:
-            return 1 + (oparg >> 8) + (oparg & 0xFF);
+            return 1 + (oparg & 0xFF) + (oparg >> 8);
         case UNPACK_SEQUENCE:
             return oparg;
         case UNPACK_SEQUENCE_LIST:
index a4cbdecdc9db1185ee6cd3d010d481c527e90fb8..beafa544aaacb7708defbef193ba1e964687cfe9 100644 (file)
@@ -31,7 +31,7 @@ test_tools.skip_if_missing("cases_generator")
 with test_tools.imports_under_tool("cases_generator"):
     from analyzer import StackItem
     import parser
-    from stack import Stack
+    from stack import Local, Stack
     import tier1_generator
     import optimizer_generator
 
@@ -60,9 +60,9 @@ class TestEffects(unittest.TestCase):
         stack.pop(y)
         stack.pop(x)
         for out in outputs:
-            stack.push(out)
-        self.assertEqual(stack.base_offset.to_c(), "-1 - oparg*2 - oparg")
-        self.assertEqual(stack.top_offset.to_c(), "1 - oparg*2 - oparg + oparg*4")
+            stack.push(Local.local(out))
+        self.assertEqual(stack.base_offset.to_c(), "-1 - oparg - oparg*2")
+        self.assertEqual(stack.top_offset.to_c(), "1 - oparg - oparg*2 + oparg*4")
 
 
 class TestGeneratedCases(unittest.TestCase):
@@ -602,7 +602,11 @@ class TestGeneratedCases(unittest.TestCase):
             frame->instr_ptr = next_instr;
             next_instr += 1;
             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;
             assert(WITHIN_STACK_BOUNDS());
             DISPATCH();
@@ -908,7 +912,6 @@ class TestGeneratedCases(unittest.TestCase):
             next_instr += 1;
             INSTRUCTION_STATS(TEST);
             _PyStackRef w;
-            _PyStackRef x;
             _PyStackRef y;
             // FIRST
             w = stack_pointer[-1];
@@ -916,11 +919,10 @@ class TestGeneratedCases(unittest.TestCase):
                 use(w);
             }
             // SECOND
-            x = w;
             {
             }
             // THIRD
-            y = x;
+            y = w;
             {
                 use(y);
             }
@@ -1024,6 +1026,7 @@ class TestGeneratedCases(unittest.TestCase):
         }
 
         op(THIRD, (j, k --)) {
+            j,k; // Mark j and k as used
             ERROR_IF(cond, error);
         }
 
@@ -1054,6 +1057,7 @@ class TestGeneratedCases(unittest.TestCase):
             k = b;
             j = a;
             {
+                j,k; // Mark j and k as used
                 if (cond) goto pop_2_error;
             }
             stack_pointer += -2;
@@ -1063,6 +1067,51 @@ class TestGeneratedCases(unittest.TestCase):
         """
         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):
     def setUp(self) -> None:
index 4afce2cc3bea9d2f42049c6219372e9b6289be71..abfd8039b293a140aea8d79aeb0fd8b174d8577b 100644 (file)
@@ -1357,8 +1357,8 @@ dummy_func(
             (void)counter;
         }
 
-        op(_UNPACK_SEQUENCE, (seq -- unused[oparg])) {
-            _PyStackRef *top = stack_pointer + oparg - 1;
+        op(_UNPACK_SEQUENCE, (seq -- output[oparg])) {
+            _PyStackRef *top = output + oparg;
             int res = _PyEval_UnpackIterableStackRef(tstate, seq, oparg, -1, top);
             DECREF_INPUTS();
             ERROR_IF(res == 0, error);
@@ -1401,9 +1401,8 @@ dummy_func(
             DECREF_INPUTS();
         }
 
-        inst(UNPACK_EX, (seq -- unused[oparg & 0xFF], unused, unused[oparg >> 8])) {
-            int totalargs = 1 + (oparg & 0xFF) + (oparg >> 8);
-            _PyStackRef *top = stack_pointer + totalargs - 1;
+        inst(UNPACK_EX, (seq -- left[oparg & 0xFF], unused, right[oparg >> 8])) {
+            _PyStackRef *top = right + (oparg >> 8);
             int res = _PyEval_UnpackIterableStackRef(tstate, seq, oparg & 0xFF, oparg >> 8, top);
             DECREF_INPUTS();
             ERROR_IF(res == 0, error);
index 62654035e80f5052b134aeb5e536d34350d6b867..f0acc3b6ea2ef40a49759c1af12bff8013fe89d9 100644 (file)
 
         case _UNPACK_SEQUENCE: {
             _PyStackRef seq;
+            _PyStackRef *output;
             oparg = CURRENT_OPARG();
             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);
             PyStackRef_CLOSE(seq);
             if (res == 0) JUMP_TO_ERROR();
 
         case _UNPACK_EX: {
             _PyStackRef seq;
+            _PyStackRef *right;
             oparg = CURRENT_OPARG();
             seq = stack_pointer[-1];
-            int totalargs = 1 + (oparg & 0xFF) + (oparg >> 8);
-            _PyStackRef *top = stack_pointer + totalargs - 1;
+            right = &stack_pointer[(oparg & 0xFF)];
+            _PyStackRef *top = right + (oparg >> 8);
             int res = _PyEval_UnpackIterableStackRef(tstate, seq, oparg & 0xFF, oparg >> 8, top);
             PyStackRef_CLOSE(seq);
             if (res == 0) JUMP_TO_ERROR();
-            stack_pointer += (oparg >> 8) + (oparg & 0xFF);
+            stack_pointer += (oparg & 0xFF) + (oparg >> 8);
             assert(WITHIN_STACK_BOUNDS());
             break;
         }
             args = &stack_pointer[-oparg];
             self_or_null = stack_pointer[-1 - oparg];
             callable = stack_pointer[-2 - oparg];
+            args = &stack_pointer[-oparg];
             if (PyStackRef_TYPE(callable) == &PyMethod_Type && PyStackRef_IsNull(self_or_null)) {
                 PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable);
                 PyObject *self = ((PyMethodObject *)callable_o)->im_self;
index 3c643f637ab09567aae866ec0fa718863581a921..ff8c4eab58f32438db0d3b428e7ae9b73f0c29c4 100644 (file)
                 for (int _i = oparg; --_i >= 0;) {
                     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);
             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);
             stack_pointer[-oparg] = list;
             stack_pointer += 1 - oparg;
                 for (int _i = oparg*2; --_i >= 0;) {
                     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(
                 values_o, 2,
             for (int _i = oparg*2; --_i >= 0;) {
                 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);
             stack_pointer[-oparg*2] = map;
             stack_pointer += 1 - oparg*2;
                 for (int _i = oparg; --_i >= 0;) {
                     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;
             for (int i = 0; i < oparg; i++) {
             }
             if (err != 0) {
                 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);
             stack_pointer[-oparg] = set;
             PyStackRef_CLOSE(start);
             PyStackRef_CLOSE(stop);
             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);
             stack_pointer[-2 - ((oparg == 3) ? 1 : 0)] = slice;
             stack_pointer += -1 - ((oparg == 3) ? 1 : 0);
                 for (int _i = oparg; --_i >= 0;) {
                     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);
             STACKREFS_TO_PYOBJECTS_CLEANUP(pieces_o);
             for (int _i = oparg; --_i >= 0;) {
                 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);
             stack_pointer[-oparg] = str;
             stack_pointer += 1 - oparg;
             _PyStackRef tup;
             values = &stack_pointer[-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);
             stack_pointer[-oparg] = tup;
             stack_pointer += 1 - oparg;
             self_or_null = stack_pointer[-1 - oparg];
             callable = stack_pointer[-2 - oparg];
             {
-                args = &stack_pointer[-oparg];
                 uint16_t counter = read_u16(&this_instr[1].cache);
                 (void)counter;
                 #if ENABLE_SPECIALIZATION
             }
             /* Skip 2 cache entries */
             // _MAYBE_EXPAND_METHOD
+            args = &stack_pointer[-oparg];
             {
+                args = &stack_pointer[-oparg];
                 if (PyStackRef_TYPE(callable) == &PyMethod_Type && PyStackRef_IsNull(self_or_null)) {
                     PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable);
                     PyObject *self = ((PyMethodObject *)callable_o)->im_self;
                 }
             }
             // _DO_CALL
+            args = &stack_pointer[-oparg];
             self_or_null = maybe_self;
             callable = func;
             {
                     for (int _i = oparg; --_i >= 0;) {
                         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(
                     callable_o, args_o,
                 for (int i = 0; i < total_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);
             }
             // _CHECK_PERIODIC
                     for (int _i = oparg; --_i >= 0;) {
                         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);
                 STACKREFS_TO_PYOBJECTS_CLEANUP(args_o);
                     PyStackRef_CLOSE(args[i]);
                 }
                 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);
             }
             // _CHECK_PERIODIC
                     for (int _i = oparg; --_i >= 0;) {
                         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)(
                     PyCFunction_GET_SELF(callable_o),
                     PyStackRef_CLOSE(args[i]);
                 }
                 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);
             }
             // _CHECK_PERIODIC
                     for (int _i = oparg; --_i >= 0;) {
                         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);
                 STACKREFS_TO_PYOBJECTS_CLEANUP(args_o);
                     PyStackRef_CLOSE(args[i]);
                 }
                 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);
             }
             // _CHECK_PERIODIC
                 assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
                 PyStackRef_CLOSE(arg);
                 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);
             }
             // _CHECK_PERIODIC
             PyStackRef_CLOSE(callargs_st);
             PyStackRef_XCLOSE(kwargs_st);
             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 += -2 - (oparg & 1);
             assert(WITHIN_STACK_BOUNDS());
                     PyStackRef_CLOSE(args[_i]);
                 }
                 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(
                 callable_o, args_o,
             for (int i = 0; i < total_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);
             stack_pointer[-3 - oparg] = res;
             stack_pointer += -2 - oparg;
                     for (int _i = oparg; --_i >= 0;) {
                         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);
                 STACKREFS_TO_PYOBJECTS_CLEANUP(args_o);
                     PyStackRef_CLOSE(args[i]);
                 }
                 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);
             }
             // _CHECK_PERIODIC
                     for (int _i = oparg; --_i >= 0;) {
                         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);
                 STACKREFS_TO_PYOBJECTS_CLEANUP(args_o);
                     PyStackRef_CLOSE(args[i]);
                 }
                 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);
             }
             // _CHECK_PERIODIC
                 assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
                 PyStackRef_CLOSE(self_stackref);
                 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);
             }
             // _CHECK_PERIODIC
                 PyStackRef_CLOSE(self_stackref);
                 PyStackRef_CLOSE(arg_stackref);
                 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);
             }
             // _CHECK_PERIODIC
                     for (int _i = oparg; --_i >= 0;) {
                         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(
                     callable_o, args_o,
                 for (int i = 0; i < total_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);
             }
             // _CHECK_PERIODIC
             self_or_null = stack_pointer[-1 - oparg];
             callable = stack_pointer[-2 - oparg];
             {
+                args = &stack_pointer[-oparg];
                 if (PyStackRef_TYPE(callable) == &PyMethod_Type && PyStackRef_IsNull(self_or_null)) {
                     PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable);
                     PyObject *self = ((PyMethodObject *)callable_o)->im_self;
                 }
             }
             // _MONITOR_CALL
+            args = &stack_pointer[-oparg];
             {
                 int is_meth = !PyStackRef_IsNull(maybe_self);
                 PyObject *function = PyStackRef_AsPyObjectBorrow(func);
                 if (err) goto error;
             }
             // _DO_CALL
+            args = &stack_pointer[-oparg];
             self_or_null = maybe_self;
             callable = func;
             {
                     for (int _i = oparg; --_i >= 0;) {
                         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(
                     callable_o, args_o,
                 for (int i = 0; i < total_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);
             }
             // _CHECK_PERIODIC
                                  "bad RAISE_VARARGS oparg");
                 break;
             }
-            if (true) { stack_pointer += -oparg; goto error; }
+            if (true) {
+                stack_pointer += -oparg;
+                assert(WITHIN_STACK_BOUNDS());
+                goto error;
+            }
         }
 
         TARGET(RERAISE) {
             next_instr += 1;
             INSTRUCTION_STATS(UNPACK_EX);
             _PyStackRef seq;
+            _PyStackRef *right;
             seq = stack_pointer[-1];
-            int totalargs = 1 + (oparg & 0xFF) + (oparg >> 8);
-            _PyStackRef *top = stack_pointer + totalargs - 1;
+            right = &stack_pointer[(oparg & 0xFF)];
+            _PyStackRef *top = right + (oparg >> 8);
             int res = _PyEval_UnpackIterableStackRef(tstate, seq, oparg & 0xFF, oparg >> 8, top);
             PyStackRef_CLOSE(seq);
             if (res == 0) goto pop_1_error;
-            stack_pointer += (oparg >> 8) + (oparg & 0xFF);
+            stack_pointer += (oparg & 0xFF) + (oparg >> 8);
             assert(WITHIN_STACK_BOUNDS());
             DISPATCH();
         }
             _Py_CODEUNIT *this_instr = next_instr - 2;
             (void)this_instr;
             _PyStackRef seq;
+            _PyStackRef *output;
             // _SPECIALIZE_UNPACK_SEQUENCE
             seq = stack_pointer[-1];
             {
             }
             // _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);
                 PyStackRef_CLOSE(seq);
                 if (res == 0) goto pop_1_error;
index 4fa40ff861ba70d2613463d412b6f0ad28ca51dc..b704c9e77319e442d4da5befddc1c9854396bace 100644 (file)
             for (int i = 0; i < totalargs; i++) {
                 values[i] = sym_new_unknown(ctx);
             }
-            stack_pointer += (oparg >> 8) + (oparg & 0xFF);
+            stack_pointer += (oparg & 0xFF) + (oparg >> 8);
             assert(WITHIN_STACK_BOUNDS());
             break;
         }
             args = &stack_pointer[-oparg];
             self_or_null = stack_pointer[-1 - oparg];
             callable = stack_pointer[-2 - oparg];
+            args = &stack_pointer[-oparg];
             (void)callable;
             (void)self_or_null;
             (void)args;
index 675dc0b9acaf4519263f9243257ba50ad064df71..f6625a3f7322d5cd6f8eed3ea039ff804d4eae98 100644 (file)
@@ -216,6 +216,7 @@ Part = Uop | Skip | Flush
 
 @dataclass
 class Instruction:
+    where: lexer.Token
     name: str
     parts: list[Part]
     _properties: Properties | None
@@ -690,9 +691,10 @@ def add_op(op: parser.InstDef, uops: dict[str, Uop]) -> None:
 
 
 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:
-    instructions[name] = Instruction(name, parts, None)
+    instructions[name] = Instruction(where, name, parts, None)
 
 
 def desugar_inst(
@@ -720,7 +722,7 @@ def desugar_inst(
         parts.append(uop)
     else:
         parts[uop_index] = uop
-    add_instruction(name, parts, instructions)
+    add_instruction(inst.first_token, name, parts, instructions)
 
 
 def add_macro(
@@ -741,7 +743,7 @@ def add_macro(
             case _:
                 assert False
     assert parts
-    add_instruction(macro.name, parts, instructions)
+    add_instruction(macro.first_token, macro.name, parts, instructions)
 
 
 def add_family(
index 587dc0d03eded5e4bc336873062bd255bf8d0756..ab8c99f1e25f978623ceace2ccfe5fd063f54fb0 100644 (file)
@@ -8,7 +8,7 @@ from analyzer import (
     StackItem,
 )
 from cwriter import CWriter
-from typing import Callable, Mapping, TextIO, Iterator, Tuple
+from typing import Callable, Mapping, TextIO, Iterator
 from lexer import Token
 from stack import Stack
 
@@ -25,7 +25,7 @@ def root_relative_path(filename: str) -> str:
         return filename
 
 
-def type_and_null(var: StackItem) -> Tuple[str, str]:
+def type_and_null(var: StackItem) -> tuple[str, str]:
     if var.type:
         return var.type, "NULL"
     elif var.is_array():
@@ -95,16 +95,23 @@ def replace_error(
     c_offset = stack.peek_offset()
     try:
         offset = -int(c_offset)
-        close = ";\n"
     except ValueError:
-        offset = None
-        out.emit(f"{{ stack_pointer += {c_offset}; ")
-        close = "; }\n"
-    out.emit("goto ")
-    if offset:
-        out.emit(f"pop_{offset}_")
-    out.emit(label)
-    out.emit(close)
+        offset = -1
+    if offset > 0:
+        out.emit(f"goto pop_{offset}_")
+        out.emit(label)
+        out.emit(";\n")
+    elif offset == 0:
+        out.emit("goto ")
+        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(
index 6a66693b93305d278de1502b9978eaf249ccd650..f6c2fea40f0dbb77025a5fbbe87565386794e6c4 100644 (file)
@@ -23,7 +23,7 @@ from generators_common import (
 from cwriter import CWriter
 from typing import TextIO, Iterator
 from lexer import Token
-from stack import Stack, StackError
+from stack import Local, Stack, StackError
 
 DEFAULT_OUTPUT = ROOT / "Python/optimizer_cases.c.h"
 DEFAULT_ABSTRACT_INPUT = (ROOT / "Python/optimizer_bytecodes.c").absolute().as_posix()
@@ -98,19 +98,18 @@ def write_uop(
     debug: bool,
     skip_inputs: bool,
 ) -> None:
+    locals: dict[str, Local] = {}
     try:
         prototype = override if override else uop
         is_override = override is not None
         out.start_line()
         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:
-                out.emit(res)
-        if not prototype.properties.stores_sp:
-            for i, var in enumerate(prototype.stack.outputs):
-                res = stack.push(var)
-                if not var.peek or is_override:
-                    out.emit(res)
+                out.emit(code)
+            if local.defined:
+                locals[local.name] = local
+        out.emit(stack.define_output_arrays(prototype.stack.outputs))
         if debug:
             args = []
             for var in prototype.stack.inputs:
@@ -135,10 +134,12 @@ def write_uop(
         else:
             emit_default(out, uop)
 
-        if prototype.properties.stores_sp:
-            for i, var in enumerate(prototype.stack.outputs):
-                if not var.peek or is_override:
-                    out.emit(stack.push(var))
+        for var in prototype.stack.outputs:
+            if var.name in locals:
+                local = locals[var.name]
+            else:
+                local = Local.local(var)
+            out.emit(stack.push(local))
         out.start_line()
         stack.flush(out, cast_type="_Py_UopsSymbol *", extract_bits=True)
     except StackError as ex:
index 8957838f7a90a112695bb6bbbda750a19089a399..5acad57f395ea6e36f29f388b2ac07422fd2eb40 100644 (file)
@@ -60,6 +60,11 @@ class Node:
         end = context.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
 class Block(Node):
index 61dcfd3e30a5101ff76be50c0c8674ecafe4dcd7..d2d598a120892d96dbc97a708630c331299e7f13 100644 (file)
@@ -38,6 +38,43 @@ def var_size(var: StackItem) -> str:
     else:
         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
 class StackOffset:
     "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:
         "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
         # Sort the list so the lexically largest element is last.
         popped = sorted(self.popped)
@@ -87,6 +128,8 @@ class StackOffset:
                 popped.append(pop)
         self.popped.extend(popped)
         self.pushed.extend(pushed)
+        self.pushed.sort()
+        self.popped.sort()
 
     def to_c(self) -> str:
         self.simplify()
@@ -125,10 +168,10 @@ class Stack:
     def __init__(self) -> None:
         self.top_offset = StackOffset.empty()
         self.base_offset = StackOffset.empty()
-        self.variables: list[StackItem] = []
+        self.variables: list[Local] = []
         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)
         indirect = "&" if var.is_array() else ""
         if self.variables:
@@ -141,21 +184,32 @@ class Stack:
             if var.name in UNUSED:
                 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")
-                return ""
-            if popped.name in UNUSED or popped.name not in self.defined:
-                self.defined.add(var.name)
+                return "", popped
+            if not var.used:
+                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 (
+                    f"{var.name} = &stack_pointer[{self.top_offset.to_c()}];\n",
+                    Local.redefinition(var, popped)
+                )
+            if not popped.defined:
                 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)
                 )
             else:
-                self.defined.add(var.name)
                 if popped.name == var.name:
-                    return ""
+                    return "", popped
                 else:
-                    return f"{var.name} = {popped.name};\n"
+                    return (
+                        f"{var.name} = {popped.name};\n",
+                        Local.redefinition(var, popped)
+                    )
         self.base_offset.pop(var)
         if var.name in UNUSED or not var.used:
-            return ""
+            return "", Local.unused(var)
         self.defined.add(var.name)
         cast = f"({var.type})" if (not indirect and var.type) else ""
         bits = ".bits" if cast and not extract_bits else ""
@@ -164,61 +218,80 @@ class Stack:
         )
         if var.condition:
             if var.condition == "1":
-                return f"{assign}\n"
+                assign = f"{assign}\n"
             elif var.condition == "0":
-                return ""
+               return "", Local.unused(var)
             else:
-                return f"if ({var.condition}) {{ {assign} }}\n"
-        return f"{assign}\n"
+                assign = f"if ({var.condition}) {{ {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)
-        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()
-            self.top_offset.push(var)
+            self.top_offset.push(var.item)
             self.defined.add(var.name)
+            var.defined = True
             return f"{var.name} = &stack_pointer[{c_offset}];\n"
         else:
-            self.top_offset.push(var)
-            if var.used:
+            self.top_offset.push(var.item)
+            if var.item.used:
                 self.defined.add(var.name)
             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()
-        for var in self.variables:
-            if not var.peek:
-                cast = f"({cast_type})" if var.type else ""
+        for var in variables:
+            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.item.type else ""
                 bits = ".bits" if cast and not extract_bits else ""
-                if var.name not in UNUSED and not var.is_array():
-                    if var.condition:
-                        if var.condition == "0":
-                            continue
-                        elif var.condition != "1":
-                            out.emit(f"if ({var.condition}) ")
-                    out.emit(
-                        f"stack_pointer[{self.base_offset.to_c()}]{bits} = {cast}{var.name};\n"
-                    )
-            self.base_offset.push(var)
-        if self.base_offset.to_c() != self.top_offset.to_c():
-            print("base", self.base_offset.to_c(), "top", self.top_offset.to_c())
+                if var.condition == "0":
+                    continue
+                if var.condition and var.condition != "1":
+                    out.emit(f"if ({var.condition}) ")
+                out.emit(
+                    f"stack_pointer[{base_offset.to_c()}]{bits} = {cast}{var.name};\n"
+                )
+            base_offset.push(var.item)
+        if base_offset.to_c() != top_offset.to_c():
+            print("base", base_offset, "top", top_offset)
             assert False
-        number = self.base_offset.to_c()
+        number = base_offset.to_c()
         if number != "0":
             out.emit(f"stack_pointer += {number};\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.base_offset.clear()
         self.top_offset.clear()
-        out.start_line()
 
     def peek_offset(self) -> str:
-        peek = self.base_offset.copy()
-        for var in self.variables:
-            if not var.peek:
-                break
-            peek.push(var)
-        return peek.to_c()
+        return self.top_offset.to_c()
 
     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()} */"
@@ -236,8 +309,15 @@ def get_stack_effect(inst: Instruction | PseudoInstruction) -> Stack:
             yield inst.stack
 
     for s in stacks(inst):
+        locals: dict[str, Local] = {}
         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:
-            stack.push(var)
+            if var.name in locals:
+                local = locals[var.name]
+            else:
+                local = Local.unused(var)
+            stack.push(local)
     return stack
index 118f4b3a6eaa1c54bf2835ff5ef1ad906e9fbf0b..1cdafbd35caea3382881aa7216e416aea4ca041c 100644 (file)
@@ -25,7 +25,7 @@ from generators_common import (
 )
 from cwriter import CWriter
 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"
@@ -43,18 +43,12 @@ def declare_variable(var: StackItem, out: CWriter) -> None:
 
 
 def declare_variables(inst: Instruction, out: CWriter) -> None:
-    stack = Stack()
-    for part in inst.parts:
-        if not isinstance(part, Uop):
-            continue
-        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
+    try:
+        stack = get_stack_effect(inst)
+    except StackError as ex:
+        raise analysis_error(ex.args[0], inst.where)
     required = set(stack.defined)
+    required.discard("unused")
     for part in inst.parts:
         if not isinstance(part, Uop):
             continue
@@ -80,16 +74,26 @@ def write_uop(
         stack.flush(out)
         return offset
     try:
+        locals: dict[str, Local] = {}
         out.start_line()
         if braces:
             out.emit(f"// {uop.name}\n")
+        peeks: list[Local] = []
         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:
             out.emit("{\n")
-        if not uop.properties.stores_sp:
-            for i, var in enumerate(uop.stack.outputs):
-                out.emit(stack.push(var))
+        out.emit(stack.define_output_arrays(uop.stack.outputs))
+
         for cache in uop.caches:
             if cache.name != "unused":
                 if cache.size == 4:
@@ -105,16 +109,22 @@ def write_uop(
                     out.emit(f"(void){cache.name};\n")
             offset += cache.size
         emit_tokens(out, uop, stack, inst)
-        if uop.properties.stores_sp:
-            for i, var in enumerate(uop.stack.outputs):
-                out.emit(stack.push(var))
+        for i, var in enumerate(uop.stack.outputs):
+            if not var.peek:
+                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:
             out.start_line()
             out.emit("}\n")
         # out.emit(stack.as_comment() + "\n")
         return offset
     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:
index 88ad0fd797f0ccacbbb81ea9c6f522cf7f9171f3..18bab2c13e7eb787334f05fe727d4243a1af516f 100644 (file)
@@ -25,7 +25,7 @@ from generators_common import (
 from cwriter import CWriter
 from typing import TextIO, Iterator
 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"
 
@@ -53,8 +53,9 @@ def declare_variables(uop: Uop, out: CWriter) -> None:
     for var in reversed(uop.stack.inputs):
         stack.pop(var)
     for var in uop.stack.outputs:
-            stack.push(var)
+        stack.push(Local.unused(var))
     required = set(stack.defined)
+    required.discard("unused")
     for var in reversed(uop.stack.inputs):
         declare_variable(var, uop, required, out)
     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:
+    locals: dict[str, Local] = {}
     try:
         out.start_line()
         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"assert(oparg == CURRENT_OPARG());\n")
         for var in reversed(uop.stack.inputs):
-            out.emit(stack.pop(var))
-        if not uop.properties.stores_sp:
-            for i, var in enumerate(uop.stack.outputs):
-                out.emit(stack.push(var))
+            code, local = stack.pop(var)
+            out.emit(code)
+            if local.defined:
+                locals[local.name] = local
+        out.emit(stack.define_output_arrays(uop.stack.outputs))
         for cache in uop.caches:
             if cache.name != "unused":
                 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"
                 out.emit(f"{type}{cache.name} = ({cast})CURRENT_OPERAND();\n")
         emit_tokens(out, uop, stack, None, TIER2_REPLACEMENT_FUNCTIONS)
-        if uop.properties.stores_sp:
-            for i, var in enumerate(uop.stack.outputs):
-                out.emit(stack.push(var))
+        for i, var in enumerate(uop.stack.outputs):
+            if var.name in locals:
+                local = locals[var.name]
+            else:
+                local = Local.local(var)
+            out.emit(stack.push(local))
     except StackError as ex:
         raise analysis_error(ex.args[0], uop.body[0]) from None