https://github.com/python/cpython/commit/844596c09fc812a58ac1b381b51bee12d327da31
commit: 844596c09fc812a58ac1b381b51bee12d327da31
branch: main
author: Mark Shannon <m...@hotpy.org>
committer: markshannon <m...@hotpy.org>
date: 2025-04-14T12:19:53+01:00
summary:

GH-131498: Cases generator: Allow input and 'peek' variables to be modified 
(GH-132506)

files:
M Include/internal/pycore_uop_metadata.h
M Lib/test/test_generated_cases.py
M Python/bytecodes.c
M Python/executor_cases.c.h
M Python/generated_cases.c.h
M Python/optimizer_cases.c.h
M Tools/cases_generator/generators_common.py
M Tools/cases_generator/stack.py
M Tools/cases_generator/tier1_generator.py

diff --git a/Include/internal/pycore_uop_metadata.h 
b/Include/internal/pycore_uop_metadata.h
index 8fa50ff2c291c4..ab26543a26fa98 100644
--- a/Include/internal/pycore_uop_metadata.h
+++ b/Include/internal/pycore_uop_metadata.h
@@ -1081,7 +1081,7 @@ int _PyUop_num_popped(int opcode, int oparg)
         case _CALL_KW_NON_PY:
             return 3 + oparg;
         case _MAKE_CALLARGS_A_TUPLE:
-            return 2;
+            return 0;
         case _MAKE_FUNCTION:
             return 1;
         case _SET_FUNCTION_ATTRIBUTE:
diff --git a/Lib/test/test_generated_cases.py b/Lib/test/test_generated_cases.py
index 302e69de285ca8..5b120f28131d51 100644
--- a/Lib/test/test_generated_cases.py
+++ b/Lib/test/test_generated_cases.py
@@ -1862,13 +1862,28 @@ def test_multiple_labels(self):
 
     def test_reassigning_live_inputs(self):
         input = """
-        inst(OP, (in -- )) {
+        inst(OP, (in -- in)) {
             in = 0;
-            DEAD(in);
         }
         """
-        with self.assertRaises(SyntaxError):
-            self.run_cases_test(input, "")
+
+        output = """
+        TARGET(OP) {
+            #if Py_TAIL_CALL_INTERP
+            int opcode = OP;
+            (void)(opcode);
+            #endif
+            frame->instr_ptr = next_instr;
+            next_instr += 1;
+            INSTRUCTION_STATS(OP);
+            _PyStackRef in;
+            in = stack_pointer[-1];
+            in = 0;
+            stack_pointer[-1] = in;
+            DISPATCH();
+        }
+        """
+        self.run_cases_test(input, output)
 
     def test_reassigning_dead_inputs(self):
         input = """
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index e9dced654d1492..95786c91371e98 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -4725,15 +4725,9 @@ dummy_func(
             _CALL_KW_NON_PY +
             _CHECK_PERIODIC;
 
-        op(_MAKE_CALLARGS_A_TUPLE, (func, unused, callargs, kwargs_in -- func, 
unused, tuple, kwargs_out)) {
+        op(_MAKE_CALLARGS_A_TUPLE, (func, unused, callargs, kwargs -- func, 
unused, callargs, kwargs)) {
             PyObject *callargs_o = PyStackRef_AsPyObjectBorrow(callargs);
-            if (PyTuple_CheckExact(callargs_o)) {
-                tuple = callargs;
-                kwargs_out = kwargs_in;
-                DEAD(kwargs_in);
-                DEAD(callargs);
-            }
-            else {
+            if (!PyTuple_CheckExact(callargs_o)) {
                 int err = _Py_Check_ArgsIterable(tstate, 
PyStackRef_AsPyObjectBorrow(func), callargs_o);
                 if (err < 0) {
                     ERROR_NO_POP();
@@ -4742,10 +4736,9 @@ dummy_func(
                 if (tuple_o == NULL) {
                     ERROR_NO_POP();
                 }
-                kwargs_out = kwargs_in;
-                DEAD(kwargs_in);
-                PyStackRef_CLOSE(callargs);
-                tuple = PyStackRef_FromPyObjectSteal(tuple_o);
+                _PyStackRef temp = callargs;
+                callargs = PyStackRef_FromPyObjectSteal(tuple_o);
+                PyStackRef_CLOSE(temp);
             }
         }
 
@@ -4965,11 +4958,11 @@ dummy_func(
 
         macro(BINARY_OP) = _SPECIALIZE_BINARY_OP + unused/4 + _BINARY_OP;
 
-        pure inst(SWAP, (bottom[1], unused[oparg-2], top[1] --
-                    bottom[1], unused[oparg-2], top[1])) {
-            _PyStackRef temp = bottom[0];
-            bottom[0] = top[0];
-            top[0] = temp;
+        pure inst(SWAP, (bottom, unused[oparg-2], top --
+                    bottom, unused[oparg-2], top)) {
+            _PyStackRef temp = bottom;
+            bottom = top;
+            top = temp;
             assert(oparg >= 2);
         }
 
diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h
index 938a80fe665c0b..9bfb13e2d9773f 100644
--- a/Python/executor_cases.c.h
+++ b/Python/executor_cases.c.h
@@ -6350,20 +6350,12 @@
         }
 
         case _MAKE_CALLARGS_A_TUPLE: {
-            _PyStackRef kwargs_in;
             _PyStackRef callargs;
             _PyStackRef func;
-            _PyStackRef tuple;
-            _PyStackRef kwargs_out;
-            kwargs_in = stack_pointer[-1];
             callargs = stack_pointer[-2];
             func = stack_pointer[-4];
             PyObject *callargs_o = PyStackRef_AsPyObjectBorrow(callargs);
-            if (PyTuple_CheckExact(callargs_o)) {
-                tuple = callargs;
-                kwargs_out = kwargs_in;
-            }
-            else {
+            if (!PyTuple_CheckExact(callargs_o)) {
                 _PyFrame_SetStackPointer(frame, stack_pointer);
                 int err = _Py_Check_ArgsIterable(tstate, 
PyStackRef_AsPyObjectBorrow(func), callargs_o);
                 stack_pointer = _PyFrame_GetStackPointer(frame);
@@ -6376,17 +6368,14 @@
                 if (tuple_o == NULL) {
                     JUMP_TO_ERROR();
                 }
-                kwargs_out = kwargs_in;
-                stack_pointer += -2;
-                assert(WITHIN_STACK_BOUNDS());
+                _PyStackRef temp = callargs;
+                callargs = PyStackRef_FromPyObjectSteal(tuple_o);
+                stack_pointer[-2] = callargs;
                 _PyFrame_SetStackPointer(frame, stack_pointer);
-                PyStackRef_CLOSE(callargs);
+                PyStackRef_CLOSE(temp);
                 stack_pointer = _PyFrame_GetStackPointer(frame);
-                tuple = PyStackRef_FromPyObjectSteal(tuple_o);
-                stack_pointer += 2;
             }
-            stack_pointer[-2] = tuple;
-            stack_pointer[-1] = kwargs_out;
+            stack_pointer[-2] = callargs;
             break;
         }
 
@@ -6631,15 +6620,17 @@
         }
 
         case _SWAP: {
-            _PyStackRef *top;
-            _PyStackRef *bottom;
+            _PyStackRef top;
+            _PyStackRef bottom;
             oparg = CURRENT_OPARG();
-            top = &stack_pointer[-1];
-            bottom = &stack_pointer[-2 - (oparg-2)];
-            _PyStackRef temp = bottom[0];
-            bottom[0] = top[0];
-            top[0] = temp;
+            top = stack_pointer[-1];
+            bottom = stack_pointer[-2 - (oparg-2)];
+            _PyStackRef temp = bottom;
+            bottom = top;
+            top = temp;
             assert(oparg >= 2);
+            stack_pointer[-2 - (oparg-2)] = bottom;
+            stack_pointer[-1] = top;
             break;
         }
 
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 97bffce8d82767..6fe647d6197a07 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -2330,9 +2330,6 @@
             opcode = CALL_FUNCTION_EX;
             _PyStackRef func;
             _PyStackRef callargs;
-            _PyStackRef kwargs_in;
-            _PyStackRef tuple;
-            _PyStackRef kwargs_out;
             _PyStackRef func_st;
             _PyStackRef null;
             _PyStackRef callargs_st;
@@ -2340,15 +2337,10 @@
             _PyStackRef result;
             // _MAKE_CALLARGS_A_TUPLE
             {
-                kwargs_in = stack_pointer[-1];
                 callargs = stack_pointer[-2];
                 func = stack_pointer[-4];
                 PyObject *callargs_o = PyStackRef_AsPyObjectBorrow(callargs);
-                if (PyTuple_CheckExact(callargs_o)) {
-                    tuple = callargs;
-                    kwargs_out = kwargs_in;
-                }
-                else {
+                if (!PyTuple_CheckExact(callargs_o)) {
                     _PyFrame_SetStackPointer(frame, stack_pointer);
                     int err = _Py_Check_ArgsIterable(tstate, 
PyStackRef_AsPyObjectBorrow(func), callargs_o);
                     stack_pointer = _PyFrame_GetStackPointer(frame);
@@ -2361,20 +2353,18 @@
                     if (tuple_o == NULL) {
                         JUMP_TO_LABEL(error);
                     }
-                    kwargs_out = kwargs_in;
-                    stack_pointer += -2;
-                    assert(WITHIN_STACK_BOUNDS());
+                    _PyStackRef temp = callargs;
+                    callargs = PyStackRef_FromPyObjectSteal(tuple_o);
+                    stack_pointer[-2] = callargs;
                     _PyFrame_SetStackPointer(frame, stack_pointer);
-                    PyStackRef_CLOSE(callargs);
+                    PyStackRef_CLOSE(temp);
                     stack_pointer = _PyFrame_GetStackPointer(frame);
-                    tuple = PyStackRef_FromPyObjectSteal(tuple_o);
-                    stack_pointer += 2;
                 }
             }
             // _DO_CALL_FUNCTION_EX
             {
-                kwargs_st = kwargs_out;
-                callargs_st = tuple;
+                kwargs_st = stack_pointer[-1];
+                callargs_st = callargs;
                 null = stack_pointer[-3];
                 func_st = func;
                 (void)null;
@@ -2390,7 +2380,6 @@
                     PyObject *arg = PyTuple_GET_SIZE(callargs) > 0 ?
                     PyTuple_GET_ITEM(callargs, 0) : 
&_PyInstrumentation_MISSING;
                     stack_pointer[-2] = callargs_st;
-                    stack_pointer[-1] = kwargs_st;
                     _PyFrame_SetStackPointer(frame, stack_pointer);
                     int err = _Py_call_instrumentation_2args(
                         tstate, PY_MONITORING_EVENT_CALL,
@@ -2456,7 +2445,6 @@
                     PyObject *kwargs = PyStackRef_AsPyObjectBorrow(kwargs_st);
                     assert(kwargs == NULL || PyDict_CheckExact(kwargs));
                     stack_pointer[-2] = callargs_st;
-                    stack_pointer[-1] = kwargs_st;
                     _PyFrame_SetStackPointer(frame, stack_pointer);
                     result_o = PyObject_Call(func, callargs, kwargs);
                     stack_pointer = _PyFrame_GetStackPointer(frame);
@@ -6289,9 +6277,6 @@
             opcode = INSTRUMENTED_CALL_FUNCTION_EX;
             _PyStackRef func;
             _PyStackRef callargs;
-            _PyStackRef kwargs_in;
-            _PyStackRef tuple;
-            _PyStackRef kwargs_out;
             _PyStackRef func_st;
             _PyStackRef null;
             _PyStackRef callargs_st;
@@ -6299,15 +6284,10 @@
             _PyStackRef result;
             // _MAKE_CALLARGS_A_TUPLE
             {
-                kwargs_in = stack_pointer[-1];
                 callargs = stack_pointer[-2];
                 func = stack_pointer[-4];
                 PyObject *callargs_o = PyStackRef_AsPyObjectBorrow(callargs);
-                if (PyTuple_CheckExact(callargs_o)) {
-                    tuple = callargs;
-                    kwargs_out = kwargs_in;
-                }
-                else {
+                if (!PyTuple_CheckExact(callargs_o)) {
                     _PyFrame_SetStackPointer(frame, stack_pointer);
                     int err = _Py_Check_ArgsIterable(tstate, 
PyStackRef_AsPyObjectBorrow(func), callargs_o);
                     stack_pointer = _PyFrame_GetStackPointer(frame);
@@ -6320,20 +6300,18 @@
                     if (tuple_o == NULL) {
                         JUMP_TO_LABEL(error);
                     }
-                    kwargs_out = kwargs_in;
-                    stack_pointer += -2;
-                    assert(WITHIN_STACK_BOUNDS());
+                    _PyStackRef temp = callargs;
+                    callargs = PyStackRef_FromPyObjectSteal(tuple_o);
+                    stack_pointer[-2] = callargs;
                     _PyFrame_SetStackPointer(frame, stack_pointer);
-                    PyStackRef_CLOSE(callargs);
+                    PyStackRef_CLOSE(temp);
                     stack_pointer = _PyFrame_GetStackPointer(frame);
-                    tuple = PyStackRef_FromPyObjectSteal(tuple_o);
-                    stack_pointer += 2;
                 }
             }
             // _DO_CALL_FUNCTION_EX
             {
-                kwargs_st = kwargs_out;
-                callargs_st = tuple;
+                kwargs_st = stack_pointer[-1];
+                callargs_st = callargs;
                 null = stack_pointer[-3];
                 func_st = func;
                 (void)null;
@@ -6349,7 +6327,6 @@
                     PyObject *arg = PyTuple_GET_SIZE(callargs) > 0 ?
                     PyTuple_GET_ITEM(callargs, 0) : 
&_PyInstrumentation_MISSING;
                     stack_pointer[-2] = callargs_st;
-                    stack_pointer[-1] = kwargs_st;
                     _PyFrame_SetStackPointer(frame, stack_pointer);
                     int err = _Py_call_instrumentation_2args(
                         tstate, PY_MONITORING_EVENT_CALL,
@@ -6415,7 +6392,6 @@
                     PyObject *kwargs = PyStackRef_AsPyObjectBorrow(kwargs_st);
                     assert(kwargs == NULL || PyDict_CheckExact(kwargs));
                     stack_pointer[-2] = callargs_st;
-                    stack_pointer[-1] = kwargs_st;
                     _PyFrame_SetStackPointer(frame, stack_pointer);
                     result_o = PyObject_Call(func, callargs, kwargs);
                     stack_pointer = _PyFrame_GetStackPointer(frame);
@@ -11358,14 +11334,16 @@
             frame->instr_ptr = next_instr;
             next_instr += 1;
             INSTRUCTION_STATS(SWAP);
-            _PyStackRef *bottom;
-            _PyStackRef *top;
-            top = &stack_pointer[-1];
-            bottom = &stack_pointer[-2 - (oparg-2)];
-            _PyStackRef temp = bottom[0];
-            bottom[0] = top[0];
-            top[0] = temp;
+            _PyStackRef bottom;
+            _PyStackRef top;
+            top = stack_pointer[-1];
+            bottom = stack_pointer[-2 - (oparg-2)];
+            _PyStackRef temp = bottom;
+            bottom = top;
+            top = temp;
             assert(oparg >= 2);
+            stack_pointer[-2 - (oparg-2)] = bottom;
+            stack_pointer[-1] = top;
             DISPATCH();
         }
 
diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h
index 0c617137a8893d..6a20cef906242b 100644
--- a/Python/optimizer_cases.c.h
+++ b/Python/optimizer_cases.c.h
@@ -2047,12 +2047,6 @@
         }
 
         case _MAKE_CALLARGS_A_TUPLE: {
-            JitOptSymbol *tuple;
-            JitOptSymbol *kwargs_out;
-            tuple = sym_new_not_null(ctx);
-            kwargs_out = sym_new_not_null(ctx);
-            stack_pointer[-2] = tuple;
-            stack_pointer[-1] = kwargs_out;
             break;
         }
 
diff --git a/Tools/cases_generator/generators_common.py 
b/Tools/cases_generator/generators_common.py
index ca6104705fb3f2..9ba0767cba35a0 100644
--- a/Tools/cases_generator/generators_common.py
+++ b/Tools/cases_generator/generators_common.py
@@ -500,9 +500,6 @@ def emit_SimpleStmt(
                         if tkn in local_stores:
                             for var in storage.inputs:
                                 if var.name == tkn.text:
-                                    if var.in_local or var.in_memory():
-                                        msg = f"Cannot assign to already 
defined input variable '{tkn.text}'"
-                                        raise analysis_error(msg, tkn)
                                     var.in_local = True
                                     var.memory_offset = None
                                     break
diff --git a/Tools/cases_generator/stack.py b/Tools/cases_generator/stack.py
index 14d06948ba1602..ff1e2ea74dbab8 100644
--- a/Tools/cases_generator/stack.py
+++ b/Tools/cases_generator/stack.py
@@ -283,7 +283,7 @@ def clear(self, out: CWriter) -> None:
         self.base_offset = self.logical_sp
 
     def push(self, var: Local) -> None:
-        assert(var not in self.variables)
+        assert(var not in self.variables), var
         self.variables.append(var)
         self.logical_sp = self.logical_sp.push(var.item)
 
@@ -325,6 +325,7 @@ def save_variables(self, out: CWriter) -> None:
             var_offset = var_offset.push(var.item)
 
     def flush(self, out: CWriter) -> None:
+        self._print(out)
         self.save_variables(out)
         self._save_physical_sp(out)
         out.start_line()
@@ -432,12 +433,14 @@ class Storage:
     stack: Stack
     inputs: list[Local]
     outputs: list[Local]
+    peeks: int
     check_liveness: bool
     spilled: int = 0
 
     @staticmethod
     def needs_defining(var: Local) -> bool:
         return (
+            not var.item.peek and
             not var.in_local and
             not var.is_array() and
             var.name != "unused"
@@ -454,7 +457,7 @@ def is_live(var: Local) -> bool:
         )
 
     def clear_inputs(self, reason:str) -> None:
-        while self.inputs:
+        while len(self.inputs) > self.peeks:
             tos = self.inputs.pop()
             if self.is_live(tos) and self.check_liveness:
                 raise StackError(
@@ -464,14 +467,14 @@ def clear_inputs(self, reason:str) -> None:
 
     def clear_dead_inputs(self) -> None:
         live = ""
-        while self.inputs:
+        while len(self.inputs) > self.peeks:
             tos = self.inputs[-1]
             if self.is_live(tos):
                 live = tos.name
                 break
             self.inputs.pop()
             self.stack.drop(tos.item, self.check_liveness)
-        for var in self.inputs:
+        for var in self.inputs[self.peeks:]:
             if not self.is_live(var):
                 raise StackError(
                     f"Input '{var.name}' is not live, but '{live}' is"
@@ -493,8 +496,8 @@ def _push_defined_outputs(self) -> None:
                     f"Expected '{undefined}' to be defined before '{out.name}'"
             else:
                 undefined = out.name
-        while self.outputs and not self.needs_defining(self.outputs[0]):
-            out = self.outputs.pop(0)
+        while len(self.outputs) > self.peeks and not 
self.needs_defining(self.outputs[0]):
+            out = self.outputs.pop(self.peeks)
             self.stack.push(out)
 
     def locals_cached(self) -> bool:
@@ -541,12 +544,9 @@ def for_uop(stack: Stack, uop: Uop, out: CWriter, 
check_liveness: bool = True) -
             local = stack.pop(input, out)
             if input.peek:
                 peeks.append(local)
-            else:
-                inputs.append(local)
+            inputs.append(local)
         inputs.reverse()
         peeks.reverse()
-        for peek in peeks:
-            stack.push(peek)
         offset = stack.logical_sp - stack.physical_sp
         for ouput in uop.stack.outputs:
             if ouput.is_array() and ouput.used and not ouput.peek:
@@ -555,8 +555,8 @@ def for_uop(stack: Stack, uop: Uop, out: CWriter, 
check_liveness: bool = True) -
             offset = offset.push(ouput)
         for var in inputs:
             stack.push(var)
-        outputs = [ Local.undefined(var) for var in uop.stack.outputs if not 
var.peek ]
-        return Storage(stack, inputs, outputs, check_liveness)
+        outputs = peeks + [ Local.undefined(var) for var in uop.stack.outputs 
if not var.peek ]
+        return Storage(stack, inputs, outputs, len(peeks), check_liveness)
 
     @staticmethod
     def copy_list(arg: list[Local]) -> list[Local]:
@@ -568,7 +568,7 @@ def copy(self) -> "Storage":
         inputs = [ variables[var.name] for var in self.inputs]
         assert [v.name for v in inputs] == [v.name for v in self.inputs], 
(inputs, self.inputs)
         return Storage(
-            new_stack, inputs, self.copy_list(self.outputs),
+            new_stack, inputs, self.copy_list(self.outputs), self.peeks,
             self.check_liveness, self.spilled
         )
 
@@ -602,6 +602,8 @@ def merge(self, other: "Storage", out: CWriter) -> None:
             other.clear_dead_inputs()
         if len(self.inputs) != len(other.inputs) and self.check_liveness:
             diff = self.inputs[-1] if len(self.inputs) > len(other.inputs) 
else other.inputs[-1]
+            self._print(out)
+            other._print(out)
             raise StackError(f"Unmergeable inputs. Differing state of 
'{diff.name}'")
         for var, other_var in zip(self.inputs, other.inputs):
             if var.in_local != other_var.in_local:
@@ -624,11 +626,11 @@ def push_outputs(self) -> None:
         if self.spilled:
             raise StackError(f"Unbalanced stack spills")
         self.clear_inputs("at the end of the micro-op")
-        if self.inputs and self.check_liveness:
+        if len(self.inputs) > self.peeks and self.check_liveness:
             raise StackError(f"Input variable '{self.inputs[-1].name}' is 
still live")
         self._push_defined_outputs()
         if self.outputs:
-            for out in self.outputs:
+            for out in self.outputs[self.peeks:]:
                 if self.needs_defining(out):
                     raise StackError(f"Output variable 
'{self.outputs[0].name}' is not defined")
                 self.stack.push(out)
@@ -641,6 +643,10 @@ def as_comment(self) -> str:
         outputs = ", ".join([var.compact_str() for var in self.outputs])
         return f"{stack_comment[:-2]}{next_line}inputs: {inputs} outputs: 
{outputs}*/"
 
+    def _print(self, out: CWriter) -> None:
+        if PRINT_STACKS:
+            out.emit(self.as_comment() + "\n")
+
     def close_inputs(self, out: CWriter) -> None:
 
         tmp_defined = False
diff --git a/Tools/cases_generator/tier1_generator.py 
b/Tools/cases_generator/tier1_generator.py
index 5a49c239ed1aa7..32dc346d5e891a 100644
--- a/Tools/cases_generator/tier1_generator.py
+++ b/Tools/cases_generator/tier1_generator.py
@@ -204,7 +204,7 @@ def generate_tier1_labels(
     # Emit tail-callable labels as function defintions
     for name, label in analysis.labels.items():
         emitter.emit(f"LABEL({name})\n")
-        storage = Storage(Stack(), [], [], False)
+        storage = Storage(Stack(), [], [], 0, False)
         if label.spilled:
             storage.spilled = 1
         emitter.emit_tokens(label, storage, None)

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com

Reply via email to