From 45199ae8c0b6118f89319d8d183df1e581eb2a4c Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 24 Mar 2025 09:51:46 +0000 Subject: [PATCH 01/12] Remove redundant attribute --- Tools/cases_generator/stack.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/Tools/cases_generator/stack.py b/Tools/cases_generator/stack.py index 67d0418a114977..f33f5ba4817bcd 100644 --- a/Tools/cases_generator/stack.py +++ b/Tools/cases_generator/stack.py @@ -32,7 +32,6 @@ def var_size(var: StackItem) -> str: @dataclass class Local: item: StackItem - cached: bool in_memory: bool defined: bool @@ -47,21 +46,21 @@ def compact_str(self) -> str: @staticmethod def unused(defn: StackItem) -> "Local": - return Local(defn, False, defn.is_array(), False) + return Local(defn, defn.is_array(), False) @staticmethod def undefined(defn: StackItem) -> "Local": array = defn.is_array() - return Local(defn, not array, array, False) + return Local(defn, array, False) @staticmethod def redefinition(var: StackItem, prev: "Local") -> "Local": assert var.is_array() == prev.is_array() - return Local(var, prev.cached, prev.in_memory, True) + return Local(var, prev.in_memory, True) @staticmethod def from_memory(defn: StackItem) -> "Local": - return Local(defn, True, True, True) + return Local(defn, True, True) def kill(self) -> None: self.defined = False @@ -70,7 +69,6 @@ def kill(self) -> None: def copy(self) -> "Local": return Local( self.item, - self.cached, self.in_memory, self.defined ) @@ -91,7 +89,6 @@ def __eq__(self, other: object) -> bool: return NotImplemented return ( self.item is other.item - and self.cached is other.cached and self.in_memory is other.in_memory and self.defined is other.defined ) From d786146fafb6c8894639b7da3c53c7e36685a13e Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 24 Mar 2025 10:19:02 +0000 Subject: [PATCH 02/12] Rename 'defined' attribute to 'in_local' to more accurately reflect how it is used --- Tools/cases_generator/generators_common.py | 8 +-- Tools/cases_generator/optimizer_generator.py | 4 +- Tools/cases_generator/stack.py | 52 ++++++++++---------- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/Tools/cases_generator/generators_common.py b/Tools/cases_generator/generators_common.py index cc1ea5c7da5455..b93dc232fb59f8 100644 --- a/Tools/cases_generator/generators_common.py +++ b/Tools/cases_generator/generators_common.py @@ -303,7 +303,7 @@ def stackref_kill( f"'{live}' is still live", name) var.kill() break - if var.defined: + if var.in_local: live = var.name return True @@ -577,15 +577,15 @@ def _emit_block( if tkn in local_stores: for var in storage.inputs: if var.name == tkn.text: - if var.defined or var.in_memory: + 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.defined = True + var.in_local = True var.in_memory = False break for var in storage.outputs: if var.name == tkn.text: - var.defined = True + var.in_local = True var.in_memory = False break if tkn.text.startswith("DISPATCH"): diff --git a/Tools/cases_generator/optimizer_generator.py b/Tools/cases_generator/optimizer_generator.py index 15be7608e93937..b93012fe1336b0 100644 --- a/Tools/cases_generator/optimizer_generator.py +++ b/Tools/cases_generator/optimizer_generator.py @@ -84,7 +84,7 @@ def emit_default(out: CWriter, uop: Uop, stack: Stack) -> None: local = Local.undefined(var) stack.push(local) if var.name != "unused" and not var.peek: - local.defined = True + local.in_local = True if var.is_array(): if var.size == "1": out.emit(f"{var.name}[0] = sym_new_not_null(ctx);\n") @@ -145,7 +145,7 @@ def write_uop( emitter = OptimizerEmitter(out, {}) # No reference management of inputs needed. for var in storage.inputs: # type: ignore[possibly-undefined] - var.defined = False + var.in_local = False storage = emitter.emit_tokens(override, storage, None) out.start_line() storage.flush(out) diff --git a/Tools/cases_generator/stack.py b/Tools/cases_generator/stack.py index f33f5ba4817bcd..4acff3b2ca5448 100644 --- a/Tools/cases_generator/stack.py +++ b/Tools/cases_generator/stack.py @@ -33,14 +33,14 @@ def var_size(var: StackItem) -> str: class Local: item: StackItem in_memory: bool - defined: bool + in_local: bool def __repr__(self) -> str: - return f"Local('{self.item.name}', mem={self.in_memory}, defined={self.defined}, array={self.is_array()})" + return f"Local('{self.item.name}', mem={self.in_memory}, local={self.in_local}, array={self.is_array()})" def compact_str(self) -> str: mtag = "M" if self.in_memory else "" - dtag = "D" if self.defined else "" + dtag = "D" if self.in_local else "" atag = "A" if self.is_array() else "" return f"'{self.item.name}'{mtag}{dtag}{atag}" @@ -63,14 +63,14 @@ def from_memory(defn: StackItem) -> "Local": return Local(defn, True, True) def kill(self) -> None: - self.defined = False + self.in_local = False self.in_memory = False def copy(self) -> "Local": return Local( self.item, self.in_memory, - self.defined + self.in_local ) @property @@ -90,7 +90,7 @@ def __eq__(self, other: object) -> bool: return ( self.item is other.item and self.in_memory is other.in_memory - and self.defined is other.defined + and self.in_local is other.in_local ) @@ -242,7 +242,7 @@ def pop(self, var: StackItem) -> tuple[str, Local]: if not var.used: return "", popped self.defined.add(var.name) - if popped.defined: + if popped.in_local: if popped.name == var.name: return "", popped else: @@ -295,7 +295,7 @@ def flush(self, out: CWriter) -> None: var_offset = self.base_offset.copy() for var in self.variables: if ( - var.defined and + var.in_local and not var.in_memory ): Stack._do_emit(out, var.item, var_offset, self.cast_type, self.extract_bits) @@ -355,7 +355,7 @@ def merge(self, other: "Stack", out: CWriter) -> None: for self_var, other_var in zip(self.variables, other.variables): if self_var.name != other_var.name: raise StackError(f"Mismatched variables on stack: {self_var.name} and {other_var.name}") - self_var.defined = self_var.defined and other_var.defined + self_var.in_local = self_var.in_local and other_var.in_local self_var.in_memory = self_var.in_memory and other_var.in_memory self.align(other, out) @@ -403,7 +403,7 @@ class Storage: @staticmethod def needs_defining(var: Local) -> bool: return ( - not var.defined and + not var.in_local and not var.is_array() and var.name != "unused" ) @@ -411,13 +411,13 @@ def needs_defining(var: Local) -> bool: @staticmethod def is_live(var: Local) -> bool: return ( - var.defined and + var.in_local and var.name != "unused" ) def first_input_not_cleared(self) -> str: for input in self.inputs: - if input.defined: + if input.in_local: return input.name return "" @@ -440,7 +440,7 @@ def clear_dead_inputs(self) -> None: self.inputs.pop() self.stack.pop(tos.item) for var in self.inputs: - if not var.defined and not var.is_array() and var.name != "unused": + if not var.in_local and not var.is_array() and var.name != "unused": raise StackError( f"Input '{var.name}' is not live, but '{live}' is" ) @@ -448,14 +448,14 @@ def clear_dead_inputs(self) -> None: def _push_defined_outputs(self) -> None: defined_output = "" for output in self.outputs: - if output.defined and not output.in_memory: + if output.in_local and not output.in_memory: defined_output = output.name if not defined_output: return self.clear_inputs(f"when output '{defined_output}' is defined") undefined = "" for out in self.outputs: - if out.defined: + if out.in_local: if undefined: f"Locals not defined in stack order. " f"Expected '{undefined}' to be defined before '{out.name}'" @@ -467,7 +467,7 @@ def _push_defined_outputs(self) -> None: def locals_cached(self) -> bool: for out in self.outputs: - if out.defined: + if out.in_local: return True return False @@ -564,7 +564,7 @@ def sanity_check(self) -> None: def is_flushed(self) -> bool: for var in self.outputs: - if var.defined and not var.in_memory: + if var.in_local and not var.in_memory: return False return self.stack.is_flushed() @@ -577,7 +577,7 @@ def merge(self, other: "Storage", out: CWriter) -> None: diff = self.inputs[-1] if len(self.inputs) > len(other.inputs) else other.inputs[-1] raise StackError(f"Unmergeable inputs. Differing state of '{diff.name}'") for var, other_var in zip(self.inputs, other.inputs): - if var.defined != other_var.defined: + if var.in_local != other_var.in_local: raise StackError(f"'{var.name}' is cleared on some paths, but not all") if len(self.outputs) != len(other.outputs): self._push_defined_outputs() @@ -653,7 +653,7 @@ def close_variable(var: Local, overwrite: str) -> None: if var.is_array(): if len(self.inputs) > 1: raise StackError("Cannot call DECREF_INPUTS with multiple live input(s) and array output") - elif var.defined: + elif var.in_local: if output is not None: raise StackError("Cannot call DECREF_INPUTS with more than one live output") output = var @@ -669,29 +669,29 @@ def close_variable(var: Local, overwrite: str) -> None: raise StackError("Cannot call DECREF_INPUTS with non fixed size array as lowest input on stack") if size > 1: raise StackError("Cannot call DECREF_INPUTS with array size > 1 as lowest input on stack") - output.defined = False + output.in_local = False close_variable(lowest, output.name) else: lowest.in_memory = False - output.defined = False + output.in_local = False close_variable(lowest, output.name) to_close = self.inputs[: 0 if output is not None else None: -1] if len(to_close) == 1 and not to_close[0].is_array(): self.reload(out) - to_close[0].defined = False + to_close[0].in_local = False self.flush(out) self.save_inputs(out) close_variable(to_close[0], "") self.reload(out) else: for var in to_close: - assert var.defined or var.is_array() + assert var.in_local or var.is_array() close_variable(var, "PyStackRef_NULL") self.reload(out) for var in self.inputs: - var.defined = False + var.in_local = False if output is not None: - output.defined = True + output.in_local = True # MyPy false positive - lowest.defined = False # type: ignore[possibly-undefined] + lowest.in_local = False # type: ignore[possibly-undefined] self.flush(out) From 2b21bf940249215a5d82ebeb75f7c58a0ba5356b Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 24 Mar 2025 10:40:14 +0000 Subject: [PATCH 03/12] Remove unused function --- Tools/cases_generator/stack.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Tools/cases_generator/stack.py b/Tools/cases_generator/stack.py index 4acff3b2ca5448..d2864076d7f02a 100644 --- a/Tools/cases_generator/stack.py +++ b/Tools/cases_generator/stack.py @@ -415,12 +415,6 @@ def is_live(var: Local) -> bool: var.name != "unused" ) - def first_input_not_cleared(self) -> str: - for input in self.inputs: - if input.in_local: - return input.name - return "" - def clear_inputs(self, reason:str) -> None: while self.inputs: tos = self.inputs.pop() From 66f5826aaccda3abdfe8009e3c8c9a654b46e7c1 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 24 Mar 2025 11:03:01 +0000 Subject: [PATCH 04/12] Make death of variables explicit even for array variables. --- Include/internal/pycore_uop_metadata.h | 6 +- Lib/test/test_generated_cases.py | 2 + Python/bytecodes.c | 31 ++++++---- Python/executor_cases.c.h | 37 ++++-------- Python/generated_cases.c.h | 78 +++++++------------------- Python/optimizer_cases.c.h | 11 ---- Tools/cases_generator/stack.py | 2 +- 7 files changed, 59 insertions(+), 108 deletions(-) diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index fa8ee5849275ea..7c2c8715fb4862 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -930,7 +930,7 @@ int _PyUop_num_popped(int opcode, int oparg) case _LOAD_ATTR_METHOD_LAZY_DICT: return 1; case _MAYBE_EXPAND_METHOD: - return 2 + oparg; + return 0; case _PY_FRAME_GENERAL: return 2 + oparg; case _CHECK_FUNCTION_VERSION: @@ -976,7 +976,7 @@ int _PyUop_num_popped(int opcode, int oparg) case _CALL_TUPLE_1: return 3; case _CHECK_AND_ALLOCATE_OBJECT: - return 2 + oparg; + return 0; case _CREATE_INIT_FRAME: return 2 + oparg; case _EXIT_INIT_CHECK: @@ -1004,7 +1004,7 @@ int _PyUop_num_popped(int opcode, int oparg) case _CALL_METHOD_DESCRIPTOR_FAST: return 2 + oparg; case _MAYBE_EXPAND_METHOD_KW: - return 3 + oparg; + return 1; case _PY_FRAME_KW: return 3 + oparg; case _CHECK_FUNCTION_VERSION_KW: diff --git a/Lib/test/test_generated_cases.py b/Lib/test/test_generated_cases.py index 405c23ad96c414..d240a17c1ae093 100644 --- a/Lib/test/test_generated_cases.py +++ b/Lib/test/test_generated_cases.py @@ -793,6 +793,7 @@ def test_array_input(self): input = """ inst(OP, (below, values[oparg*2], above --)) { SPAM(values, oparg); + DEAD(values); } """ output = """ @@ -1056,6 +1057,7 @@ def test_array_of_one(self): input = """ inst(OP, (arg[1] -- out[1])) { out[0] = arg[0]; + DEAD(arg); } """ output = """ diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 0db4da013ff40a..ca61fec0d37a58 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1927,6 +1927,7 @@ dummy_func( PyStackRef_CLOSE(value); } } + DEAD(values); if (err) { Py_DECREF(set_o); ERROR_IF(true, error); @@ -3583,15 +3584,15 @@ dummy_func( #endif /* ENABLE_SPECIALIZATION_FT */ } - op(_MAYBE_EXPAND_METHOD, (callable[1], self_or_null[1], args[oparg] -- func[1], maybe_self[1], args[oparg])) { + op(_MAYBE_EXPAND_METHOD, (callable[1], self_or_null[1], args[oparg] -- callable[1], self_or_null[1], args[oparg])) { (void)args; if (PyStackRef_TYPE(callable[0]) == &PyMethod_Type && PyStackRef_IsNull(self_or_null[0])) { PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); PyObject *self = ((PyMethodObject *)callable_o)->im_self; - maybe_self[0] = PyStackRef_FromPyObjectNew(self); + self_or_null[0] = PyStackRef_FromPyObjectNew(self); PyObject *method = ((PyMethodObject *)callable_o)->im_func; _PyStackRef temp = callable[0]; - func[0] = PyStackRef_FromPyObjectNew(method); + callable[0] = PyStackRef_FromPyObjectNew(method); PyStackRef_CLOSE(temp); } } @@ -3618,6 +3619,9 @@ dummy_func( tstate, callable[0], locals, arguments, total_args, NULL, frame ); + DEAD(args); + DEAD(self_or_null); + DEAD(callable); // Manipulate stack directly since we leave using DISPATCH_INLINED(). SYNC_SP(); // The frame has stolen all the arguments from the stack, @@ -3950,10 +3954,10 @@ dummy_func( _CALL_TUPLE_1 + _CHECK_PERIODIC; - op(_CHECK_AND_ALLOCATE_OBJECT, (type_version/2, callable[1], null[1], args[oparg] -- init[1], self[1], args[oparg])) { + op(_CHECK_AND_ALLOCATE_OBJECT, (type_version/2, callable[1], self_or_null[1], args[oparg] -- callable[1], self_or_null[1], args[oparg])) { (void)args; PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); - DEOPT_IF(!PyStackRef_IsNull(null[0])); + DEOPT_IF(!PyStackRef_IsNull(self_or_null[0])); DEOPT_IF(!PyType_Check(callable_o)); PyTypeObject *tp = (PyTypeObject *)callable_o; DEOPT_IF(FT_ATOMIC_LOAD_UINT32_RELAXED(tp->tp_version_tag) != type_version); @@ -3969,9 +3973,9 @@ dummy_func( if (self_o == NULL) { ERROR_NO_POP(); } - self[0] = PyStackRef_FromPyObjectSteal(self_o); + self_or_null[0] = PyStackRef_FromPyObjectSteal(self_o); _PyStackRef temp = callable[0]; - init[0] = PyStackRef_FromPyObjectNew(init_func); + callable[0] = PyStackRef_FromPyObjectNew(init_func); PyStackRef_CLOSE(temp); } @@ -3986,6 +3990,7 @@ dummy_func( DEAD(self); _PyInterpreterFrame *temp = _PyEvalFramePushAndInit( tstate, init[0], NULL, args-1, oparg+1, NULL, shim); + DEAD(args); SYNC_SP(); if (temp == NULL) { _PyEval_FrameClearAndPop(tstate, shim); @@ -4420,15 +4425,15 @@ dummy_func( ERROR_IF(err, error); } - op(_MAYBE_EXPAND_METHOD_KW, (callable[1], self_or_null[1], args[oparg], kwnames_in -- func[1], maybe_self[1], args[oparg], kwnames_out)) { + op(_MAYBE_EXPAND_METHOD_KW, (callable[1], self_or_null[1], args[oparg], kwnames_in -- callable[1], self_or_null[1], args[oparg], kwnames_out)) { (void)args; if (PyStackRef_TYPE(callable[0]) == &PyMethod_Type && PyStackRef_IsNull(self_or_null[0])) { PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); PyObject *self = ((PyMethodObject *)callable_o)->im_self; - maybe_self[0] = PyStackRef_FromPyObjectNew(self); + self_or_null[0] = PyStackRef_FromPyObjectNew(self); PyObject *method = ((PyMethodObject *)callable_o)->im_func; _PyStackRef temp = callable[0]; - func[0] = PyStackRef_FromPyObjectNew(method); + callable[0] = PyStackRef_FromPyObjectNew(method); PyStackRef_CLOSE(temp); } kwnames_out = kwnames_in; @@ -4458,6 +4463,9 @@ dummy_func( tstate, callable[0], locals, arguments, positional_args, kwnames_o, frame ); + DEAD(args); + DEAD(self_or_null); + DEAD(callable); PyStackRef_CLOSE(kwnames); // Sync stack explicitly since we leave using DISPATCH_INLINED(). SYNC_SP(); @@ -4525,6 +4533,9 @@ dummy_func( PyStackRef_CLOSE(kwnames); // The frame has stolen all the arguments from the stack, // so there is no need to clean them up. + DEAD(args); + DEAD(self_or_null); + DEAD(callable); SYNC_SP(); ERROR_IF(temp == NULL, error); new_frame = temp; diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 2942680a222fb2..ae75594e25246c 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -2701,11 +2701,11 @@ } } if (err) { + stack_pointer += -oparg; + assert(WITHIN_STACK_BOUNDS()); _PyFrame_SetStackPointer(frame, stack_pointer); Py_DECREF(set_o); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -oparg; - assert(WITHIN_STACK_BOUNDS()); JUMP_TO_ERROR(); } set = PyStackRef_FromPyObjectStealMortal(set_o); @@ -4667,23 +4667,18 @@ _PyStackRef *args; _PyStackRef *self_or_null; _PyStackRef *callable; - _PyStackRef *func; - _PyStackRef *maybe_self; oparg = CURRENT_OPARG(); args = &stack_pointer[-oparg]; self_or_null = &stack_pointer[-1 - oparg]; callable = &stack_pointer[-2 - oparg]; - func = &stack_pointer[-2 - oparg]; - maybe_self = &stack_pointer[-1 - oparg]; - args = &stack_pointer[-oparg]; (void)args; if (PyStackRef_TYPE(callable[0]) == &PyMethod_Type && PyStackRef_IsNull(self_or_null[0])) { PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); PyObject *self = ((PyMethodObject *)callable_o)->im_self; - maybe_self[0] = PyStackRef_FromPyObjectNew(self); + self_or_null[0] = PyStackRef_FromPyObjectNew(self); PyObject *method = ((PyMethodObject *)callable_o)->im_func; _PyStackRef temp = callable[0]; - func[0] = PyStackRef_FromPyObjectNew(method); + callable[0] = PyStackRef_FromPyObjectNew(method); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(temp); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -5252,21 +5247,16 @@ case _CHECK_AND_ALLOCATE_OBJECT: { _PyStackRef *args; - _PyStackRef *null; + _PyStackRef *self_or_null; _PyStackRef *callable; - _PyStackRef *init; - _PyStackRef *self; oparg = CURRENT_OPARG(); args = &stack_pointer[-oparg]; - null = &stack_pointer[-1 - oparg]; + self_or_null = &stack_pointer[-1 - oparg]; callable = &stack_pointer[-2 - oparg]; - init = &stack_pointer[-2 - oparg]; - self = &stack_pointer[-1 - oparg]; - args = &stack_pointer[-oparg]; uint32_t type_version = (uint32_t)CURRENT_OPERAND0(); (void)args; PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); - if (!PyStackRef_IsNull(null[0])) { + if (!PyStackRef_IsNull(self_or_null[0])) { UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); } @@ -5296,9 +5286,9 @@ if (self_o == NULL) { JUMP_TO_ERROR(); } - self[0] = PyStackRef_FromPyObjectSteal(self_o); + self_or_null[0] = PyStackRef_FromPyObjectSteal(self_o); _PyStackRef temp = callable[0]; - init[0] = PyStackRef_FromPyObjectNew(init_func); + callable[0] = PyStackRef_FromPyObjectNew(init_func); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(temp); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -6147,25 +6137,20 @@ _PyStackRef *args; _PyStackRef *self_or_null; _PyStackRef *callable; - _PyStackRef *func; - _PyStackRef *maybe_self; _PyStackRef kwnames_out; oparg = CURRENT_OPARG(); kwnames_in = stack_pointer[-1]; args = &stack_pointer[-1 - oparg]; self_or_null = &stack_pointer[-2 - oparg]; callable = &stack_pointer[-3 - oparg]; - func = &stack_pointer[-3 - oparg]; - maybe_self = &stack_pointer[-2 - oparg]; - args = &stack_pointer[-1 - oparg]; (void)args; if (PyStackRef_TYPE(callable[0]) == &PyMethod_Type && PyStackRef_IsNull(self_or_null[0])) { PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); PyObject *self = ((PyMethodObject *)callable_o)->im_self; - maybe_self[0] = PyStackRef_FromPyObjectNew(self); + self_or_null[0] = PyStackRef_FromPyObjectNew(self); PyObject *method = ((PyMethodObject *)callable_o)->im_func; _PyStackRef temp = callable[0]; - func[0] = PyStackRef_FromPyObjectNew(method); + callable[0] = PyStackRef_FromPyObjectNew(method); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(temp); stack_pointer = _PyFrame_GetStackPointer(frame); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index a2e6c474d29204..b8cba456fba49f 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -1125,11 +1125,11 @@ } } if (err) { + stack_pointer += -oparg; + assert(WITHIN_STACK_BOUNDS()); _PyFrame_SetStackPointer(frame, stack_pointer); Py_DECREF(set_o); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -oparg; - assert(WITHIN_STACK_BOUNDS()); JUMP_TO_LABEL(error); } set = PyStackRef_FromPyObjectStealMortal(set_o); @@ -1271,8 +1271,6 @@ _PyStackRef *callable; _PyStackRef *self_or_null; _PyStackRef *args; - _PyStackRef *func; - _PyStackRef *maybe_self; _PyStackRef res; // _SPECIALIZE_CALL { @@ -1295,18 +1293,15 @@ /* Skip 2 cache entries */ // _MAYBE_EXPAND_METHOD { - args = &stack_pointer[-oparg]; - func = &stack_pointer[-2 - oparg]; - maybe_self = &stack_pointer[-1 - oparg]; args = &stack_pointer[-oparg]; (void)args; if (PyStackRef_TYPE(callable[0]) == &PyMethod_Type && PyStackRef_IsNull(self_or_null[0])) { PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); PyObject *self = ((PyMethodObject *)callable_o)->im_self; - maybe_self[0] = PyStackRef_FromPyObjectNew(self); + self_or_null[0] = PyStackRef_FromPyObjectNew(self); PyObject *method = ((PyMethodObject *)callable_o)->im_func; _PyStackRef temp = callable[0]; - func[0] = PyStackRef_FromPyObjectNew(method); + callable[0] = PyStackRef_FromPyObjectNew(method); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(temp); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -1314,9 +1309,6 @@ } // _DO_CALL { - args = &stack_pointer[-oparg]; - self_or_null = &stack_pointer[-1 - oparg]; - callable = &stack_pointer[-2 - oparg]; PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); // oparg counts all of the args, but *not* self: int total_args = oparg; @@ -1458,7 +1450,7 @@ INSTRUCTION_STATS(CALL_ALLOC_AND_ENTER_INIT); static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); _PyStackRef *callable; - _PyStackRef *null; + _PyStackRef *self_or_null; _PyStackRef *args; _PyStackRef *init; _PyStackRef *self; @@ -1476,15 +1468,12 @@ // _CHECK_AND_ALLOCATE_OBJECT { args = &stack_pointer[-oparg]; - null = &stack_pointer[-1 - oparg]; + self_or_null = &stack_pointer[-1 - oparg]; callable = &stack_pointer[-2 - oparg]; - init = &stack_pointer[-2 - oparg]; - self = &stack_pointer[-1 - oparg]; - args = &stack_pointer[-oparg]; uint32_t type_version = read_u32(&this_instr[2].cache); (void)args; PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); - if (!PyStackRef_IsNull(null[0])) { + if (!PyStackRef_IsNull(self_or_null[0])) { UPDATE_MISS_STATS(CALL); assert(_PyOpcode_Deopt[opcode] == (CALL)); JUMP_TO_PREDICTED(CALL); @@ -1518,18 +1507,17 @@ if (self_o == NULL) { JUMP_TO_LABEL(error); } - self[0] = PyStackRef_FromPyObjectSteal(self_o); + self_or_null[0] = PyStackRef_FromPyObjectSteal(self_o); _PyStackRef temp = callable[0]; - init[0] = PyStackRef_FromPyObjectNew(init_func); + callable[0] = PyStackRef_FromPyObjectNew(init_func); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(temp); stack_pointer = _PyFrame_GetStackPointer(frame); } // _CREATE_INIT_FRAME { - args = &stack_pointer[-oparg]; - self = &stack_pointer[-1 - oparg]; - init = &stack_pointer[-2 - oparg]; + self = self_or_null; + init = callable; _PyFrame_SetStackPointer(frame, stack_pointer); _PyInterpreterFrame *shim = _PyFrame_PushTrampolineUnchecked( tstate, (PyCodeObject *)&_Py_InitCleanup, 1, frame); @@ -2622,8 +2610,6 @@ _PyStackRef *args; _PyStackRef kwnames; _PyStackRef kwnames_in; - _PyStackRef *func; - _PyStackRef *maybe_self; _PyStackRef kwnames_out; _PyStackRef res; // _SPECIALIZE_CALL_KW @@ -2649,17 +2635,14 @@ { kwnames_in = stack_pointer[-1]; args = &stack_pointer[-1 - oparg]; - func = &stack_pointer[-3 - oparg]; - maybe_self = &stack_pointer[-2 - oparg]; - args = &stack_pointer[-1 - oparg]; (void)args; if (PyStackRef_TYPE(callable[0]) == &PyMethod_Type && PyStackRef_IsNull(self_or_null[0])) { PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); PyObject *self = ((PyMethodObject *)callable_o)->im_self; - maybe_self[0] = PyStackRef_FromPyObjectNew(self); + self_or_null[0] = PyStackRef_FromPyObjectNew(self); PyObject *method = ((PyMethodObject *)callable_o)->im_func; _PyStackRef temp = callable[0]; - func[0] = PyStackRef_FromPyObjectNew(method); + callable[0] = PyStackRef_FromPyObjectNew(method); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(temp); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -2669,9 +2652,6 @@ // _DO_CALL_KW { kwnames = kwnames_out; - args = &stack_pointer[-1 - oparg]; - self_or_null = &stack_pointer[-2 - oparg]; - callable = &stack_pointer[-3 - oparg]; PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); PyObject *kwnames_o = PyStackRef_AsPyObjectBorrow(kwnames); // oparg counts all of the args, but *not* self: @@ -2696,14 +2676,12 @@ arguments, positional_args, kwnames_o, frame ); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; + stack_pointer += -3 - oparg; assert(WITHIN_STACK_BOUNDS()); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(kwnames); stack_pointer = _PyFrame_GetStackPointer(frame); // Sync stack explicitly since we leave using DISPATCH_INLINED(). - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); // The frame has stolen all the arguments from the stack, // so there is no need to clean them up. if (new_frame == NULL) { @@ -6121,17 +6099,14 @@ args = &stack_pointer[-oparg]; self_or_null = &stack_pointer[-1 - oparg]; callable = &stack_pointer[-2 - oparg]; - func = &stack_pointer[-2 - oparg]; - maybe_self = &stack_pointer[-1 - oparg]; - args = &stack_pointer[-oparg]; (void)args; if (PyStackRef_TYPE(callable[0]) == &PyMethod_Type && PyStackRef_IsNull(self_or_null[0])) { PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); PyObject *self = ((PyMethodObject *)callable_o)->im_self; - maybe_self[0] = PyStackRef_FromPyObjectNew(self); + self_or_null[0] = PyStackRef_FromPyObjectNew(self); PyObject *method = ((PyMethodObject *)callable_o)->im_func; _PyStackRef temp = callable[0]; - func[0] = PyStackRef_FromPyObjectNew(method); + callable[0] = PyStackRef_FromPyObjectNew(method); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(temp); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -6139,9 +6114,8 @@ } // _MONITOR_CALL { - args = &stack_pointer[-oparg]; - maybe_self = &stack_pointer[-1 - oparg]; - func = &stack_pointer[-2 - oparg]; + maybe_self = self_or_null; + func = callable; int is_meth = !PyStackRef_IsNull(maybe_self[0]); PyObject *function = PyStackRef_AsPyObjectBorrow(func[0]); PyObject *arg0; @@ -6506,8 +6480,6 @@ _PyStackRef *self_or_null; _PyStackRef *args; _PyStackRef kwnames_in; - _PyStackRef *func; - _PyStackRef *maybe_self; _PyStackRef kwnames_out; _PyStackRef kwnames; _PyStackRef res; @@ -6519,17 +6491,14 @@ args = &stack_pointer[-1 - oparg]; self_or_null = &stack_pointer[-2 - oparg]; callable = &stack_pointer[-3 - oparg]; - func = &stack_pointer[-3 - oparg]; - maybe_self = &stack_pointer[-2 - oparg]; - args = &stack_pointer[-1 - oparg]; (void)args; if (PyStackRef_TYPE(callable[0]) == &PyMethod_Type && PyStackRef_IsNull(self_or_null[0])) { PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); PyObject *self = ((PyMethodObject *)callable_o)->im_self; - maybe_self[0] = PyStackRef_FromPyObjectNew(self); + self_or_null[0] = PyStackRef_FromPyObjectNew(self); PyObject *method = ((PyMethodObject *)callable_o)->im_func; _PyStackRef temp = callable[0]; - func[0] = PyStackRef_FromPyObjectNew(method); + callable[0] = PyStackRef_FromPyObjectNew(method); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(temp); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -6538,9 +6507,6 @@ } // _MONITOR_CALL_KW { - args = &stack_pointer[-1 - oparg]; - self_or_null = &stack_pointer[-2 - oparg]; - callable = &stack_pointer[-3 - oparg]; int is_meth = !PyStackRef_IsNull(self_or_null[0]); PyObject *arg; if (is_meth) { @@ -6591,14 +6557,12 @@ arguments, positional_args, kwnames_o, frame ); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; + stack_pointer += -3 - oparg; assert(WITHIN_STACK_BOUNDS()); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(kwnames); stack_pointer = _PyFrame_GetStackPointer(frame); // Sync stack explicitly since we leave using DISPATCH_INLINED(). - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); // The frame has stolen all the arguments from the stack, // so there is no need to clean them up. if (new_frame == NULL) { diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index a1119171fa49ca..e9859459bb16b5 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -2008,18 +2008,7 @@ /* _MONITOR_CALL_KW is not a viable micro-op for tier 2 */ case _MAYBE_EXPAND_METHOD_KW: { - JitOptSymbol **func; - JitOptSymbol **maybe_self; - JitOptSymbol **args; JitOptSymbol *kwnames_out; - func = &stack_pointer[-3 - oparg]; - maybe_self = &stack_pointer[-2 - oparg]; - args = &stack_pointer[-1 - oparg]; - func[0] = sym_new_not_null(ctx); - maybe_self[0] = sym_new_not_null(ctx); - for (int _i = oparg; --_i >= 0;) { - args[_i] = sym_new_not_null(ctx); - } kwnames_out = sym_new_not_null(ctx); stack_pointer[-1] = kwnames_out; break; diff --git a/Tools/cases_generator/stack.py b/Tools/cases_generator/stack.py index d2864076d7f02a..c2aaa77cc9750a 100644 --- a/Tools/cases_generator/stack.py +++ b/Tools/cases_generator/stack.py @@ -418,7 +418,7 @@ def is_live(var: Local) -> bool: def clear_inputs(self, reason:str) -> None: while self.inputs: tos = self.inputs.pop() - if self.is_live(tos) and not tos.is_array(): + if self.is_live(tos): raise StackError( f"Input '{tos.name}' is still live {reason}" ) From 5d6a9f8344b7ed481c322efbf274a357a3830a59 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 24 Mar 2025 11:25:31 +0000 Subject: [PATCH 05/12] Remove used 'peeks' attribute --- Tools/cases_generator/stack.py | 8 +++----- Tools/cases_generator/tier1_generator.py | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/Tools/cases_generator/stack.py b/Tools/cases_generator/stack.py index c2aaa77cc9750a..25c3207588840d 100644 --- a/Tools/cases_generator/stack.py +++ b/Tools/cases_generator/stack.py @@ -397,7 +397,6 @@ class Storage: stack: Stack inputs: list[Local] outputs: list[Local] - peeks: list[Local] spilled: int = 0 @staticmethod @@ -523,7 +522,7 @@ def for_uop(stack: Stack, uop: Uop) -> tuple[list[str], "Storage"]: for var in inputs: stack.push(var) outputs = [ Local.undefined(var) for var in uop.stack.outputs if not var.peek ] - return code_list, Storage(stack, inputs, outputs, peeks) + return code_list, Storage(stack, inputs, outputs) @staticmethod def copy_list(arg: list[Local]) -> list[Local]: @@ -536,7 +535,7 @@ def copy(self) -> "Storage": 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), self.copy_list(self.peeks), self.spilled + self.copy_list(self.outputs), self.spilled ) def sanity_check(self) -> None: @@ -601,8 +600,7 @@ def as_comment(self) -> str: next_line = "\n " inputs = ", ".join([var.compact_str() for var in self.inputs]) outputs = ", ".join([var.compact_str() for var in self.outputs]) - peeks = ", ".join([var.name for var in self.peeks]) - return f"{stack_comment[:-2]}{next_line}inputs: {inputs}{next_line}outputs: {outputs}{next_line}peeks: {peeks} */" + return f"{stack_comment[:-2]}{next_line}inputs: {inputs}{next_line}outputs: {outputs}*/" 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 ee375681b50f0b..6aeabe42c09f32 100644 --- a/Tools/cases_generator/tier1_generator.py +++ b/Tools/cases_generator/tier1_generator.py @@ -205,7 +205,7 @@ def generate_tier1_labels( for name, label in analysis.labels.items(): emitter.emit(f"LABEL({name})\n") emitter.emit("{\n") - storage = Storage(Stack(), [], [], []) + storage = Storage(Stack(), [], []) if label.spilled: storage.spilled = 1 emitter.emit("/* STACK SPILLED */\n") From 6e2a1891a22f801e78302c267a05198887a261c5 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 24 Mar 2025 17:21:34 +0000 Subject: [PATCH 06/12] Convert in_memory from boolean to stack offset --- Lib/test/test_generated_cases.py | 29 ++- Python/bytecodes.c | 8 +- Python/executor_cases.c.h | 56 ++--- Python/generated_cases.c.h | 66 ++--- Python/optimizer_cases.c.h | 3 + Tools/cases_generator/generators_common.py | 13 +- Tools/cases_generator/stack.py | 274 +++++++++++---------- Tools/cases_generator/tier2_generator.py | 2 +- 8 files changed, 241 insertions(+), 210 deletions(-) diff --git a/Lib/test/test_generated_cases.py b/Lib/test/test_generated_cases.py index d240a17c1ae093..518a02633236ca 100644 --- a/Lib/test/test_generated_cases.py +++ b/Lib/test/test_generated_cases.py @@ -285,9 +285,11 @@ def test_overlap(self): def test_predictions(self): input = """ inst(OP1, (arg -- res)) { + DEAD(arg); res = Py_None; } inst(OP3, (arg -- res)) { + DEAD(arg); DEOPT_IF(xxx); res = Py_None; } @@ -303,7 +305,9 @@ def test_predictions(self): next_instr += 1; INSTRUCTION_STATS(OP1); PREDICTED_OP1:; + _PyStackRef arg; _PyStackRef res; + arg = stack_pointer[-1]; res = Py_None; stack_pointer[-1] = res; DISPATCH(); @@ -320,7 +324,9 @@ def test_predictions(self): next_instr += 1; INSTRUCTION_STATS(OP3); static_assert(INLINE_CACHE_ENTRIES_OP1 == 0, "incorrect cache size"); + _PyStackRef arg; _PyStackRef res; + arg = stack_pointer[-1]; if (xxx) { UPDATE_MISS_STATS(OP1); assert(_PyOpcode_Deopt[opcode] == (OP1)); @@ -336,11 +342,13 @@ def test_predictions(self): def test_sync_sp(self): input = """ inst(A, (arg -- res)) { + DEAD(arg); SYNC_SP(); escaping_call(); res = Py_None; } inst(B, (arg -- res)) { + DEAD(arg); res = Py_None; SYNC_SP(); escaping_call(); @@ -355,7 +363,9 @@ def test_sync_sp(self): frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(A); + _PyStackRef arg; _PyStackRef res; + arg = stack_pointer[-1]; stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); _PyFrame_SetStackPointer(frame, stack_pointer); @@ -376,7 +386,9 @@ def test_sync_sp(self): frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(B); + _PyStackRef arg; _PyStackRef res; + arg = stack_pointer[-1]; res = Py_None; stack_pointer[-1] = res; _PyFrame_SetStackPointer(frame, stack_pointer); @@ -522,6 +534,7 @@ def test_error_if_pop_with_result(self): def test_cache_effect(self): input = """ inst(OP, (counter/1, extra/2, value --)) { + DEAD(value); } """ output = """ @@ -535,6 +548,8 @@ def test_cache_effect(self): frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(OP); + _PyStackRef value; + value = stack_pointer[-1]; uint16_t counter = read_u16(&this_instr[1].cache); (void)counter; uint32_t extra = read_u32(&this_instr[2].cache); @@ -793,7 +808,9 @@ def test_array_input(self): input = """ inst(OP, (below, values[oparg*2], above --)) { SPAM(values, oparg); + DEAD(below); DEAD(values); + DEAD(above); } """ output = """ @@ -805,8 +822,12 @@ def test_array_input(self): frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); + _PyStackRef below; _PyStackRef *values; + _PyStackRef above; + above = stack_pointer[-1]; values = &stack_pointer[-1 - oparg*2]; + below = stack_pointer[-2 - oparg*2]; SPAM(values, oparg); stack_pointer += -2 - oparg*2; assert(WITHIN_STACK_BOUNDS()); @@ -880,6 +901,8 @@ def test_array_input_output(self): def test_array_error_if(self): input = """ inst(OP, (extra, values[oparg] --)) { + DEAD(extra); + DEAD(values); ERROR_IF(oparg == 0, somewhere); } """ @@ -892,6 +915,10 @@ def test_array_error_if(self): frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); + _PyStackRef extra; + _PyStackRef *values; + values = &stack_pointer[-oparg]; + extra = stack_pointer[-1 - oparg]; if (oparg == 0) { stack_pointer += -1 - oparg; assert(WITHIN_STACK_BOUNDS()); @@ -1223,7 +1250,7 @@ def test_unused_used_used(self): } // THIRD { - y = x; + y = stack_pointer[-1]; USE(y); } DISPATCH(); diff --git a/Python/bytecodes.c b/Python/bytecodes.c index ca61fec0d37a58..44e8ea2450800c 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -3986,10 +3986,10 @@ dummy_func( assert(_PyFrame_GetBytecode(shim)[1].op.code == RETURN_VALUE); /* Push self onto stack of shim */ shim->localsplus[0] = PyStackRef_DUP(self[0]); - DEAD(init); - DEAD(self); _PyInterpreterFrame *temp = _PyEvalFramePushAndInit( tstate, init[0], NULL, args-1, oparg+1, NULL, shim); + DEAD(init); + DEAD(self); DEAD(args); SYNC_SP(); if (temp == NULL) { @@ -4192,9 +4192,9 @@ dummy_func( res = PyStackRef_FromPyObjectSteal(res_o); } - inst(CALL_ISINSTANCE, (unused/1, unused/2, callable[1], self_or_null[1], args[oparg] -- res)) { + inst(CALL_ISINSTANCE, (unused/1, unused/2, callable, self_or_null[1], args[oparg] -- res)) { /* isinstance(o, o2) */ - PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); int total_args = oparg; _PyStackRef *arguments = args; diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index ae75594e25246c..43f9a81d2c70f8 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -635,7 +635,6 @@ stack_pointer[-1] = value; PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer[-1] = res; break; } @@ -1259,13 +1258,13 @@ list_st = res; stack_pointer[-2] = list_st; PyStackRef_CLOSE(tmp); + tmp = sub_st; + sub_st = PyStackRef_NULL; + stack_pointer[-1] = sub_st; + PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(sub_st); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer[-1] = res; break; } @@ -1354,7 +1353,6 @@ stack_pointer[-1] = tuple_st; PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer[-1] = res; break; } @@ -2054,6 +2052,7 @@ _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(seq); stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer[-1] = val0; break; } @@ -2077,13 +2076,11 @@ for (int i = oparg; --i >= 0; ) { *values++ = PyStackRef_FromPyObjectNew(items[i]); } - stack_pointer += -1; + stack_pointer += -1 + oparg; assert(WITHIN_STACK_BOUNDS()); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(seq); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += oparg; - assert(WITHIN_STACK_BOUNDS()); break; } @@ -2115,13 +2112,11 @@ *values++ = PyStackRef_FromPyObjectNew(items[i]); } UNLOCK_OBJECT(seq_o); - stack_pointer += -1; + stack_pointer += -1 + oparg; assert(WITHIN_STACK_BOUNDS()); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(seq); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += oparg; - assert(WITHIN_STACK_BOUNDS()); break; } @@ -3002,13 +2997,12 @@ global_super_st = self_or_null; stack_pointer[-2] = global_super_st; PyStackRef_CLOSE(tmp); + tmp = class_st; + class_st = PyStackRef_NULL; + stack_pointer[-1] = class_st; + PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(class_st); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; + stack_pointer += -2; assert(WITHIN_STACK_BOUNDS()); attr = PyStackRef_FromPyObjectSteal(attr_o); stack_pointer[0] = attr; @@ -3296,7 +3290,6 @@ stack_pointer[-1] = owner; PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer[-1] = attr; break; } @@ -3331,7 +3324,6 @@ stack_pointer[-1] = owner; PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer[-1] = attr; break; } @@ -3936,9 +3928,7 @@ stack_pointer[-1] = value; PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer[-1] = b; } - stack_pointer[-1] = b; break; } @@ -4120,10 +4110,8 @@ stack_pointer[-1] = iterable; PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer[-1] = iter; } } - stack_pointer[-1] = iter; break; } @@ -5710,14 +5698,14 @@ case _CALL_ISINSTANCE: { _PyStackRef *args; _PyStackRef *self_or_null; - _PyStackRef *callable; + _PyStackRef callable; _PyStackRef res; oparg = CURRENT_OPARG(); args = &stack_pointer[-oparg]; self_or_null = &stack_pointer[-1 - oparg]; - callable = &stack_pointer[-2 - oparg]; + callable = stack_pointer[-2 - oparg]; /* isinstance(o, o2) */ - PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); int total_args = oparg; _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null[0])) { @@ -5745,8 +5733,9 @@ res = retval ? PyStackRef_True : PyStackRef_False; assert((!PyStackRef_IsNull(res)) ^ (_PyErr_Occurred(tstate) != NULL)); _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp = callable[0]; - callable[0] = res; + _PyStackRef tmp = callable; + callable = res; + stack_pointer[-2 - oparg] = callable; PyStackRef_CLOSE(tmp); for (int _i = oparg; --_i >= 0;) { tmp = args[_i]; @@ -5757,7 +5746,6 @@ self_or_null[0] = PyStackRef_NULL; PyStackRef_XCLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer[-2 - oparg] = res; stack_pointer += -1 - oparg; assert(WITHIN_STACK_BOUNDS()); break; @@ -6653,13 +6641,13 @@ lhs = res; stack_pointer[-2] = lhs; PyStackRef_CLOSE(tmp); + tmp = rhs; + rhs = PyStackRef_NULL; + stack_pointer[-1] = rhs; + PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(rhs); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer[-1] = res; break; } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index b8cba456fba49f..6c923551d9bbed 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -70,13 +70,13 @@ lhs = res; stack_pointer[-2] = lhs; PyStackRef_CLOSE(tmp); + tmp = rhs; + rhs = PyStackRef_NULL; + stack_pointer[-1] = rhs; + PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(rhs); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer[-1] = res; } DISPATCH(); } @@ -708,13 +708,13 @@ list_st = res; stack_pointer[-2] = list_st; PyStackRef_CLOSE(tmp); + tmp = sub_st; + sub_st = PyStackRef_NULL; + stack_pointer[-1] = sub_st; + PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(sub_st); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer[-1] = res; DISPATCH(); } @@ -834,7 +834,6 @@ stack_pointer[-1] = tuple_st; PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer[-1] = res; DISPATCH(); } @@ -1309,6 +1308,7 @@ } // _DO_CALL { + args = &stack_pointer[-oparg]; PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); // oparg counts all of the args, but *not* self: int total_args = oparg; @@ -2535,7 +2535,7 @@ next_instr += 4; INSTRUCTION_STATS(CALL_ISINSTANCE); static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); - _PyStackRef *callable; + _PyStackRef callable; _PyStackRef *self_or_null; _PyStackRef *args; _PyStackRef res; @@ -2543,9 +2543,9 @@ /* Skip 2 cache entries */ args = &stack_pointer[-oparg]; self_or_null = &stack_pointer[-1 - oparg]; - callable = &stack_pointer[-2 - oparg]; + callable = stack_pointer[-2 - oparg]; /* isinstance(o, o2) */ - PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); int total_args = oparg; _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null[0])) { @@ -2575,8 +2575,9 @@ res = retval ? PyStackRef_True : PyStackRef_False; assert((!PyStackRef_IsNull(res)) ^ (_PyErr_Occurred(tstate) != NULL)); _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp = callable[0]; - callable[0] = res; + _PyStackRef tmp = callable; + callable = res; + stack_pointer[-2 - oparg] = callable; PyStackRef_CLOSE(tmp); for (int _i = oparg; --_i >= 0;) { tmp = args[_i]; @@ -2587,7 +2588,6 @@ self_or_null[0] = PyStackRef_NULL; PyStackRef_XCLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer[-2 - oparg] = res; stack_pointer += -1 - oparg; assert(WITHIN_STACK_BOUNDS()); DISPATCH(); @@ -2652,6 +2652,7 @@ // _DO_CALL_KW { kwnames = kwnames_out; + args = &stack_pointer[-1 - oparg]; PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); PyObject *kwnames_o = PyStackRef_AsPyObjectBorrow(kwnames); // oparg counts all of the args, but *not* self: @@ -2965,7 +2966,6 @@ _PyFrame_SetStackPointer(frame, stack_pointer); _PyStackRef tmp = kwnames; kwnames = PyStackRef_NULL; - stack_pointer[-1] = kwnames; PyStackRef_CLOSE(tmp); for (int _i = oparg; --_i >= 0;) { tmp = args[_i]; @@ -3936,6 +3936,7 @@ // _INIT_CALL_PY_EXACT_ARGS { args = &stack_pointer[-oparg]; + self_or_null = &stack_pointer[-1 - oparg]; int has_self = !PyStackRef_IsNull(self_or_null[0]); STAT_INC(CALL, hit); new_frame = _PyFrame_PushUnchecked(tstate, callable[0], oparg + has_self, frame); @@ -4713,7 +4714,6 @@ PyStackRef_CLOSE(tmp); tmp = left; left = PyStackRef_NULL; - stack_pointer[-2] = left; PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -2; @@ -6005,10 +6005,8 @@ stack_pointer[-1] = iterable; PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer[-1] = iter; } } - stack_pointer[-1] = iter; DISPATCH(); } @@ -7837,7 +7835,6 @@ stack_pointer[-1] = owner; PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer[-1] = attr; } // _PUSH_NULL_CONDITIONAL { @@ -7906,7 +7903,6 @@ stack_pointer[-1] = owner; PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer[-1] = attr; } // _PUSH_NULL_CONDITIONAL { @@ -8567,7 +8563,6 @@ stack_pointer[-1] = owner; PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer[-1] = attr; } /* Skip 5 cache entries */ // _PUSH_NULL_CONDITIONAL @@ -9407,7 +9402,6 @@ _PyFrame_SetStackPointer(frame, stack_pointer); _PyStackRef tmp = self_st; self_st = PyStackRef_NULL; - stack_pointer[-1] = self_st; PyStackRef_CLOSE(tmp); tmp = class_st; class_st = PyStackRef_NULL; @@ -9454,7 +9448,6 @@ _PyFrame_SetStackPointer(frame, stack_pointer); _PyStackRef tmp = self_st; self_st = PyStackRef_NULL; - stack_pointer[-1] = self_st; PyStackRef_CLOSE(tmp); tmp = class_st; class_st = PyStackRef_NULL; @@ -9619,13 +9612,12 @@ global_super_st = self_or_null; stack_pointer[-2] = global_super_st; PyStackRef_CLOSE(tmp); + tmp = class_st; + class_st = PyStackRef_NULL; + stack_pointer[-1] = class_st; + PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(class_st); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; + stack_pointer += -2; assert(WITHIN_STACK_BOUNDS()); attr = PyStackRef_FromPyObjectSteal(attr_o); stack_pointer[0] = attr; @@ -9956,7 +9948,6 @@ stack_pointer[-1] = value; PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer[-1] = b; } } // _POP_JUMP_IF_TRUE @@ -10000,7 +9991,6 @@ stack_pointer[-1] = value; PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer[-1] = b; } } // _POP_JUMP_IF_FALSE @@ -11182,11 +11172,9 @@ _PyFrame_SetStackPointer(frame, stack_pointer); _PyStackRef tmp = container; container = PyStackRef_NULL; - stack_pointer[-1] = container; PyStackRef_CLOSE(tmp); tmp = v; v = PyStackRef_NULL; - stack_pointer[-2] = v; PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -2; @@ -11575,7 +11563,6 @@ stack_pointer[-1] = value; PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer[-1] = res; DISPATCH(); } @@ -11844,13 +11831,11 @@ *values++ = PyStackRef_FromPyObjectNew(items[i]); } UNLOCK_OBJECT(seq_o); - stack_pointer += -1; + stack_pointer += -1 + oparg; assert(WITHIN_STACK_BOUNDS()); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(seq); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += oparg; - assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } @@ -11886,13 +11871,11 @@ for (int i = oparg; --i >= 0; ) { *values++ = PyStackRef_FromPyObjectNew(items[i]); } - stack_pointer += -1; + stack_pointer += -1 + oparg; assert(WITHIN_STACK_BOUNDS()); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(seq); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += oparg; - assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } @@ -11934,6 +11917,7 @@ _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(seq); stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer[-1] = val0; DISPATCH(); } diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index e9859459bb16b5..c49f97b4c490d0 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -851,6 +851,7 @@ for (int _i = oparg; --_i >= 0;) { values[_i] = sym_new_not_null(ctx); } + stack_pointer[-1] = values; stack_pointer += -1 + oparg; assert(WITHIN_STACK_BOUNDS()); break; @@ -915,6 +916,7 @@ JitOptSymbol **res; res = &stack_pointer[0]; res[0] = sym_new_not_null(ctx); + stack_pointer[0] = res; stack_pointer += 1; assert(WITHIN_STACK_BOUNDS()); break; @@ -1173,6 +1175,7 @@ stack_pointer[-1] = attr; uint64_t watched_mutations = get_mutations(dict); if (watched_mutations < _Py_MAX_ALLOWED_GLOBALS_MODIFICATIONS) { + stack_pointer[-1] = attr; PyDict_Watch(GLOBALS_WATCHER_ID, dict); _Py_BloomFilter_Add(dependencies, dict); PyObject *res = convert_global_to_const(this_instr, dict, true); diff --git a/Tools/cases_generator/generators_common.py b/Tools/cases_generator/generators_common.py index b93dc232fb59f8..ea99971c0c098c 100644 --- a/Tools/cases_generator/generators_common.py +++ b/Tools/cases_generator/generators_common.py @@ -248,6 +248,7 @@ def decref_inputs( except Exception as ex: ex.args = (ex.args[0] + str(tkn),) raise + self._print_storage(storage) return True def kill_inputs( @@ -402,6 +403,7 @@ def goto_label(self, goto: Token, label: Token, storage: Storage) -> None: self.out.emit(")") def emit_save(self, storage: Storage) -> None: + storage.flush(self.out) storage.save(self.out) self._print_storage(storage) @@ -498,6 +500,9 @@ def _emit_if( else: if PRINT_STACKS: self.emit("/* Merge */\n") + self.out.emit(if_storage.as_comment()) + self.out.emit("\n") + self.out.emit(else_storage.as_comment()) else_storage.merge(if_storage, self.out) storage = else_storage self._print_storage(storage) @@ -577,16 +582,16 @@ def _emit_block( if tkn in local_stores: for var in storage.inputs: if var.name == tkn.text: - if var.in_local or var.in_memory: + 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.in_memory = False + var.memory_offset = None break for var in storage.outputs: if var.name == tkn.text: var.in_local = True - var.in_memory = False + var.memory_offset = None break if tkn.text.startswith("DISPATCH"): self._print_storage(storage) @@ -619,7 +624,7 @@ def emit_tokens( storage.push_outputs() self._print_storage(storage) except StackError as ex: - raise analysis_error(ex.args[0], rbrace) from None + raise analysis_error(ex.args[0], rbrace) # from None return storage def emit(self, txt: str | Token) -> None: diff --git a/Tools/cases_generator/stack.py b/Tools/cases_generator/stack.py index 25c3207588840d..5cfa41885b5146 100644 --- a/Tools/cases_generator/stack.py +++ b/Tools/cases_generator/stack.py @@ -29,71 +29,6 @@ def var_size(var: StackItem) -> str: return "1" -@dataclass -class Local: - item: StackItem - in_memory: bool - in_local: bool - - def __repr__(self) -> str: - return f"Local('{self.item.name}', mem={self.in_memory}, local={self.in_local}, array={self.is_array()})" - - def compact_str(self) -> str: - mtag = "M" if self.in_memory else "" - dtag = "D" if self.in_local else "" - atag = "A" if self.is_array() else "" - return f"'{self.item.name}'{mtag}{dtag}{atag}" - - @staticmethod - def unused(defn: StackItem) -> "Local": - return Local(defn, defn.is_array(), False) - - @staticmethod - def undefined(defn: StackItem) -> "Local": - array = defn.is_array() - return Local(defn, array, False) - - @staticmethod - def redefinition(var: StackItem, prev: "Local") -> "Local": - assert var.is_array() == prev.is_array() - return Local(var, prev.in_memory, True) - - @staticmethod - def from_memory(defn: StackItem) -> "Local": - return Local(defn, True, True) - - def kill(self) -> None: - self.in_local = False - self.in_memory = False - - def copy(self) -> "Local": - return Local( - self.item, - self.in_memory, - self.in_local - ) - - @property - def size(self) -> str: - return self.item.size - - @property - def name(self) -> str: - return self.item.name - - def is_array(self) -> bool: - return self.item.is_array() - - def __eq__(self, other: object) -> bool: - if not isinstance(other, Local): - return NotImplemented - return ( - self.item is other.item - and self.in_memory is other.in_memory - and self.in_local is other.in_local - ) - - @dataclass class StackOffset: "The stack offset of the virtual base of the stack from the physical stack pointer" @@ -203,6 +138,73 @@ def __eq__(self, other: object) -> bool: return self.to_c() == other.to_c() +@dataclass +class Local: + item: StackItem + memory_offset: StackOffset | None + in_local: bool + + def __repr__(self) -> str: + return f"Local('{self.item.name}', mem={self.memory_offset}, local={self.in_local}, array={self.is_array()})" + + #def compact_str(self) -> str: + #mtag = "M" if self.memory_offset else "" + #dtag = "D" if self.in_local else "" + #atag = "A" if self.is_array() else "" + #return f"'{self.item.name}'{mtag}{dtag}{atag}" + + compact_str = __repr__ + + @staticmethod + def unused(defn: StackItem, offset: StackOffset) -> "Local": + return Local(defn, offset, False) + + @staticmethod + def undefined(defn: StackItem) -> "Local": + return Local(defn, None, False) + + @staticmethod + def from_memory(defn: StackItem, offset: StackOffset) -> "Local": + return Local(defn, offset, True) + + def kill(self) -> None: + self.in_local = False + self.memory_offset = None + + def in_memory(self): + return self.memory_offset is not None or self.is_array() + + def is_dead(self) -> bool: + return not self.in_local and self.memory_offset is None + + def copy(self) -> "Local": + return Local( + self.item, + self.memory_offset, + self.in_local + ) + + @property + def size(self) -> str: + return self.item.size + + @property + def name(self) -> str: + return self.item.name + + def is_array(self) -> bool: + return self.item.is_array() + + def __eq__(self, other: object) -> bool: + if not isinstance(other, Local): + return NotImplemented + return ( + self.item is other.item + and self.memory_offset == other.memory_offset + and self.in_local == other.in_local + ) + + class StackError(Exception): pass @@ -218,6 +220,14 @@ def __init__(self, extract_bits: bool=True, cast_type: str = "uintptr_t") -> Non self.extract_bits = extract_bits self.cast_type = cast_type + def drop(self, var: StackItem): + self.top_offset.pop(var) + if self.variables: + popped = self.variables.pop() + if popped.is_dead() or not var.used: + return + raise StackError(f"Dropping live value '{var.name}'") + def pop(self, var: StackItem) -> tuple[str, Local]: self.top_offset.pop(var) indirect = "&" if var.is_array() else "" @@ -242,28 +252,30 @@ def pop(self, var: StackItem) -> tuple[str, Local]: if not var.used: return "", popped self.defined.add(var.name) - if popped.in_local: - if popped.name == var.name: - return "", popped - else: - defn = f"{var.name} = {popped.name};\n" + if popped.name != var.name: + rename = f"{var.name} = {popped.name};\n" + popped.item = var else: + rename = "" + if not popped.in_local: + assert popped.memory_offset is not None if var.is_array(): defn = f"{var.name} = &stack_pointer[{self.top_offset.to_c()}];\n" else: defn = f"{var.name} = stack_pointer[{self.top_offset.to_c()}];\n" - popped.in_memory = True - return defn, Local.redefinition(var, popped) + else: + defn = rename + return defn, popped self.base_offset.pop(var) if var.name in UNUSED or not var.used: - return "", Local.unused(var) + return "", Local.unused(var, self.base_offset) self.defined.add(var.name) cast = f"({var.type})" if (not indirect and var.type) else "" bits = ".bits" if cast and self.extract_bits else "" assign = f"{var.name} = {cast}{indirect}stack_pointer[{self.base_offset.to_c()}]{bits};" assign = f"{assign}\n" - return assign, Local.from_memory(var) + return assign, Local.from_memory(var, self.base_offset.copy()) def push(self, var: Local) -> None: assert(var not in self.variables) @@ -296,10 +308,11 @@ def flush(self, out: CWriter) -> None: for var in self.variables: if ( var.in_local and - not var.in_memory + not var.memory_offset and + not var.is_array() ): Stack._do_emit(out, var.item, var_offset, self.cast_type, self.extract_bits) - var.in_memory = True + var.memory_offset = var_offset.copy() var_offset.push(var.item) number = self.top_offset.to_c() self._adjust_stack_pointer(out, number) @@ -356,8 +369,13 @@ def merge(self, other: "Stack", out: CWriter) -> None: if self_var.name != other_var.name: raise StackError(f"Mismatched variables on stack: {self_var.name} and {other_var.name}") self_var.in_local = self_var.in_local and other_var.in_local - self_var.in_memory = self_var.in_memory and other_var.in_memory + if other_var.memory_offset is None: + self_var.memory_offset = None self.align(other, out) + for self_var, other_var in zip(self.variables, other.variables): + if self_var.memory_offset is not None: + if self_var.memory_offset != other_var.memory_offset: + raise StackError(f"Mismatched stack depths for {self_var.name}: {self_var.memory_offset.to_c()} and {other_var.memory_offset.to_c()}") def stacks(inst: Instruction | PseudoInstruction) -> Iterator[StackEffect]: @@ -380,7 +398,7 @@ def apply_stack_effect(stack: Stack, effect: StackEffect) -> None: if var.name in locals: local = locals[var.name] else: - local = Local.unused(var) + local = Local.unused(var, stack.base_offset) stack.push(local) @@ -410,8 +428,11 @@ def needs_defining(var: Local) -> bool: @staticmethod def is_live(var: Local) -> bool: return ( - var.in_local and - var.name != "unused" + var.name != "unused" and + ( + var.in_local or + var.memory_offset is not None + ) ) def clear_inputs(self, reason:str) -> None: @@ -421,7 +442,7 @@ def clear_inputs(self, reason:str) -> None: raise StackError( f"Input '{tos.name}' is still live {reason}" ) - self.stack.pop(tos.item) + self.stack.drop(tos.item) def clear_dead_inputs(self) -> None: live = "" @@ -431,9 +452,9 @@ def clear_dead_inputs(self) -> None: live = tos.name break self.inputs.pop() - self.stack.pop(tos.item) + self.stack.drop(tos.item) for var in self.inputs: - if not var.in_local and not var.is_array() and var.name != "unused": + if not self.is_live(var): raise StackError( f"Input '{var.name}' is not live, but '{live}' is" ) @@ -441,7 +462,7 @@ def clear_dead_inputs(self) -> None: def _push_defined_outputs(self) -> None: defined_output = "" for output in self.outputs: - if output.in_local and not output.in_memory: + if output.in_local and not output.memory_offset: defined_output = output.name if not defined_output: return @@ -472,7 +493,6 @@ def flush(self, out: CWriter) -> None: def save(self, out: CWriter) -> None: assert self.spilled >= 0 if self.spilled == 0: - self.flush(out) out.start_line() out.emit_spill() self.spilled += 1 @@ -557,7 +577,7 @@ def sanity_check(self) -> None: def is_flushed(self) -> bool: for var in self.outputs: - if var.in_local and not var.in_memory: + if var.in_local and not var.memory_offset: return False return self.stack.is_flushed() @@ -603,6 +623,7 @@ def as_comment(self) -> str: return f"{stack_comment[:-2]}{next_line}inputs: {inputs}{next_line}outputs: {outputs}*/" def close_inputs(self, out: CWriter) -> None: + tmp_defined = False def close_named(close: str, name: str, overwrite: str) -> None: nonlocal tmp_defined @@ -612,9 +633,7 @@ def close_named(close: str, name: str, overwrite: str) -> None: tmp_defined = True out.emit(f"tmp = {name};\n") out.emit(f"{name} = {overwrite};\n") - if not var.is_array(): - var.in_memory = False - self.flush(out) + self.stack.flush(out) out.emit(f"{close}(tmp);\n") else: out.emit(f"{close}({name});\n") @@ -624,6 +643,9 @@ def close_variable(var: Local, overwrite: str) -> None: close = "PyStackRef_CLOSE" if "null" in var.name: close = "PyStackRef_XCLOSE" + var.memory_offset = None + self.save(out) + out.start_line() if var.size: if var.size == "1": close_named(close, f"{var.name}[0]", overwrite) @@ -636,54 +658,56 @@ def close_variable(var: Local, overwrite: str) -> None: out.emit("}\n") else: close_named(close, var.name, overwrite) + self.reload(out) self.clear_dead_inputs() if not self.inputs: return + lowest = self.inputs[0] output: Local | None = None for var in self.outputs: if var.is_array(): if len(self.inputs) > 1: - raise StackError("Cannot call DECREF_INPUTS with multiple live input(s) and array output") + raise StackError("Cannot call DECREF_INPUTS with array output and more than one input") + output = var elif var.in_local: if output is not None: raise StackError("Cannot call DECREF_INPUTS with more than one live output") output = var - self.save_inputs(out) + self.stack.flush(out) if output is not None: - lowest = self.inputs[0] - if lowest.is_array(): - try: - size = int(lowest.size) - except: - size = -1 - if size <= 0: - raise StackError("Cannot call DECREF_INPUTS with non fixed size array as lowest input on stack") - if size > 1: - raise StackError("Cannot call DECREF_INPUTS with array size > 1 as lowest input on stack") - output.in_local = False - close_variable(lowest, output.name) - else: - lowest.in_memory = False - output.in_local = False - close_variable(lowest, output.name) - to_close = self.inputs[: 0 if output is not None else None: -1] - if len(to_close) == 1 and not to_close[0].is_array(): - self.reload(out) - to_close[0].in_local = False - self.flush(out) - self.save_inputs(out) - close_variable(to_close[0], "") - self.reload(out) + if output.is_array(): + assert len(self.inputs) == 1 + self.stack.pop(self.inputs[0].item) + self.stack.push(output) + self.stack.flush(out) + close_variable(self.inputs[0], "") + self.stack.drop(output.item) + self.inputs = [] + return + if var_size(lowest) != var_size(output): + raise StackError("Cannot call DECREF_INPUTS with live output not matching first input size") + lowest.in_local = True + close_variable(lowest, output.name) + assert lowest.memory_offset is not None + for input in reversed(self.inputs[1:]): + close_variable(input, "PyStackRef_NULL") + if output is None: + close_variable(self.inputs[0], "PyStackRef_NULL") + for input in reversed(self.inputs[1:]): + input.kill() + self.stack.drop(input.item) + self.stack.pop(self.inputs[0].item) + output_in_place = self.outputs and output is self.outputs[0] and lowest.memory_offset is not None + if output_in_place: + output.memory_offset = lowest.memory_offset.copy() else: - for var in to_close: - assert var.in_local or var.is_array() - close_variable(var, "PyStackRef_NULL") - self.reload(out) - for var in self.inputs: - var.in_local = False + self.stack.flush(out) + if output is not None: + self.stack.push(output) + self.inputs = [] + if output_in_place: + self.stack.flush(out) if output is not None: - output.in_local = True - # MyPy false positive - lowest.in_local = False # type: ignore[possibly-undefined] - self.flush(out) + code, output = self.stack.pop(output.item) + out.emit(code) diff --git a/Tools/cases_generator/tier2_generator.py b/Tools/cases_generator/tier2_generator.py index 572c636e84c0ca..8a684c8328ef7b 100644 --- a/Tools/cases_generator/tier2_generator.py +++ b/Tools/cases_generator/tier2_generator.py @@ -157,6 +157,7 @@ def write_uop(uop: Uop, emitter: Emitter, stack: Stack) -> Stack: emitter.emit(f"{type}{cache.name} = ({cast})CURRENT_OPERAND{idx}();\n") idx += 1 storage = emitter.emit_tokens(uop, storage, None) + storage.flush(emitter.out) except StackError as ex: raise analysis_error(ex.args[0], uop.body[0]) from None return storage.stack @@ -196,7 +197,6 @@ def generate_tier2( stack = write_uop(uop, emitter, stack) out.start_line() if not uop.properties.always_exits: - stack.flush(out) out.emit("break;\n") out.start_line() out.emit("}") From 417c96d3c408cfb23fed7aa3d0268e9983f46b66 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Tue, 25 Mar 2025 17:05:28 +0000 Subject: [PATCH 07/12] Don't apply liveness analyis to optimizer generated code --- Python/optimizer_cases.c.h | 84 +++++--------------- Tools/cases_generator/optimizer_generator.py | 2 +- Tools/cases_generator/stack.py | 28 ++++--- Tools/cases_generator/tier1_generator.py | 2 +- 4 files changed, 37 insertions(+), 79 deletions(-) diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index c49f97b4c490d0..78094595bf03fc 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -276,16 +276,14 @@ { assert(PyLong_CheckExact(sym_get_const(ctx, left))); assert(PyLong_CheckExact(sym_get_const(ctx, right))); - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); PyObject *temp = _PyLong_Multiply((PyLongObject *)sym_get_const(ctx, left), (PyLongObject *)sym_get_const(ctx, right)); if (temp == NULL) { goto error; } res = sym_new_const(ctx, temp); - stack_pointer[0] = res; - stack_pointer += 1; + stack_pointer[-2] = res; + stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); Py_DECREF(temp); // TODO gh-115506: @@ -311,16 +309,14 @@ { assert(PyLong_CheckExact(sym_get_const(ctx, left))); assert(PyLong_CheckExact(sym_get_const(ctx, right))); - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); PyObject *temp = _PyLong_Add((PyLongObject *)sym_get_const(ctx, left), (PyLongObject *)sym_get_const(ctx, right)); if (temp == NULL) { goto error; } res = sym_new_const(ctx, temp); - stack_pointer[0] = res; - stack_pointer += 1; + stack_pointer[-2] = res; + stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); Py_DECREF(temp); // TODO gh-115506: @@ -346,16 +342,14 @@ { assert(PyLong_CheckExact(sym_get_const(ctx, left))); assert(PyLong_CheckExact(sym_get_const(ctx, right))); - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); PyObject *temp = _PyLong_Subtract((PyLongObject *)sym_get_const(ctx, left), (PyLongObject *)sym_get_const(ctx, right)); if (temp == NULL) { goto error; } res = sym_new_const(ctx, temp); - stack_pointer[0] = res; - stack_pointer += 1; + stack_pointer[-2] = res; + stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); Py_DECREF(temp); // TODO gh-115506: @@ -557,17 +551,15 @@ goto error; } res = sym_new_const(ctx, temp); - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); Py_DECREF(temp); } else { res = sym_new_type(ctx, &PyUnicode_Type); - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); } // _STORE_FAST: GETLOCAL(this_instr->operand0) = res; + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); break; } @@ -710,8 +702,6 @@ JitOptSymbol *retval; JitOptSymbol *res; retval = stack_pointer[-1]; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); ctx->frame->stack_pointer = stack_pointer; frame_pop(ctx); stack_pointer = ctx->frame->stack_pointer; @@ -728,9 +718,7 @@ ctx->done = true; } res = retval; - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-1] = res; break; } @@ -851,7 +839,6 @@ for (int _i = oparg; --_i >= 0;) { values[_i] = sym_new_not_null(ctx); } - stack_pointer[-1] = values; stack_pointer += -1 + oparg; assert(WITHIN_STACK_BOUNDS()); break; @@ -916,7 +903,6 @@ JitOptSymbol **res; res = &stack_pointer[0]; res[0] = sym_new_not_null(ctx); - stack_pointer[0] = res; stack_pointer += 1; assert(WITHIN_STACK_BOUNDS()); break; @@ -1175,7 +1161,6 @@ stack_pointer[-1] = attr; uint64_t watched_mutations = get_mutations(dict); if (watched_mutations < _Py_MAX_ALLOWED_GLOBALS_MODIFICATIONS) { - stack_pointer[-1] = attr; PyDict_Watch(GLOBALS_WATCHER_ID, dict); _Py_BloomFilter_Add(dependencies, dict); PyObject *res = convert_global_to_const(this_instr, dict, true); @@ -1262,11 +1247,7 @@ res = sym_new_type(ctx, &PyBool_Type); } else { - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); res = _Py_uop_sym_new_not_null(ctx); - stack_pointer += 2; - assert(WITHIN_STACK_BOUNDS()); } stack_pointer[-2] = res; stack_pointer += -1; @@ -1293,8 +1274,6 @@ { assert(PyLong_CheckExact(sym_get_const(ctx, left))); assert(PyLong_CheckExact(sym_get_const(ctx, right))); - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); PyObject *tmp = PyObject_RichCompare(sym_get_const(ctx, left), sym_get_const(ctx, right), oparg >> 5); @@ -1305,8 +1284,8 @@ assert(_Py_IsImmortal(tmp)); REPLACE_OP(this_instr, _POP_TWO_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)tmp); res = sym_new_const(ctx, tmp); - stack_pointer[0] = res; - stack_pointer += 1; + stack_pointer[-2] = res; + stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); Py_DECREF(tmp); } @@ -1676,16 +1655,14 @@ _Py_UOpsAbstractFrame *new_frame; PyCodeObject *co = NULL; assert((this_instr + 2)->opcode == _PUSH_FRAME); - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); co = get_code_with_logging((this_instr + 2)); if (co == NULL) { ctx->done = true; break; } new_frame = frame_new(ctx, co, 0, NULL, 0); - stack_pointer[0] = (JitOptSymbol *)new_frame; - stack_pointer += 1; + stack_pointer[-2 - oparg] = (JitOptSymbol *)new_frame; + stack_pointer += -1 - oparg; assert(WITHIN_STACK_BOUNDS()); break; } @@ -1790,8 +1767,6 @@ int argcount = oparg; PyCodeObject *co = NULL; assert((this_instr + 2)->opcode == _PUSH_FRAME); - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); co = get_code_with_logging((this_instr + 2)); if (co == NULL) { ctx->done = true; @@ -1809,8 +1784,8 @@ } else { new_frame = frame_new(ctx, co, 0, NULL, 0); } - stack_pointer[0] = (JitOptSymbol *)new_frame; - stack_pointer += 1; + stack_pointer[-2 - oparg] = (JitOptSymbol *)new_frame; + stack_pointer += -1 - oparg; assert(WITHIN_STACK_BOUNDS()); break; } @@ -2103,6 +2078,7 @@ // might be impossible, but bailing is still safe ctx->done = true; } + stack_pointer[-1] = res; break; } @@ -2261,11 +2237,7 @@ if (sym_is_const(ctx, flag)) { PyObject *value = sym_get_const(ctx, flag); assert(value != NULL); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); eliminate_pop_guard(this_instr, value != Py_True); - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); } sym_set_const(flag, Py_True); stack_pointer += -1; @@ -2279,11 +2251,7 @@ if (sym_is_const(ctx, flag)) { PyObject *value = sym_get_const(ctx, flag); assert(value != NULL); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); eliminate_pop_guard(this_instr, value != Py_False); - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); } sym_set_const(flag, Py_False); stack_pointer += -1; @@ -2297,23 +2265,17 @@ if (sym_is_const(ctx, flag)) { PyObject *value = sym_get_const(ctx, flag); assert(value != NULL); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); eliminate_pop_guard(this_instr, !Py_IsNone(value)); } else { if (sym_has_type(flag)) { assert(!sym_matches_type(flag, &_PyNone_Type)); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); eliminate_pop_guard(this_instr, true); - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); } - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); } sym_set_const(flag, Py_None); + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); break; } @@ -2323,22 +2285,16 @@ if (sym_is_const(ctx, flag)) { PyObject *value = sym_get_const(ctx, flag); assert(value != NULL); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); eliminate_pop_guard(this_instr, Py_IsNone(value)); } else { if (sym_has_type(flag)) { assert(!sym_matches_type(flag, &_PyNone_Type)); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); eliminate_pop_guard(this_instr, false); - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); } - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); } + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); break; } diff --git a/Tools/cases_generator/optimizer_generator.py b/Tools/cases_generator/optimizer_generator.py index b93012fe1336b0..3d89448f1fd0f8 100644 --- a/Tools/cases_generator/optimizer_generator.py +++ b/Tools/cases_generator/optimizer_generator.py @@ -123,7 +123,7 @@ def write_uop( try: out.start_line() if override: - code_list, storage = Storage.for_uop(stack, prototype) + code_list, storage = Storage.for_uop(stack, prototype, check_liveness=False) for code in code_list: out.emit(code) if debug: diff --git a/Tools/cases_generator/stack.py b/Tools/cases_generator/stack.py index 5cfa41885b5146..6f157622f195d7 100644 --- a/Tools/cases_generator/stack.py +++ b/Tools/cases_generator/stack.py @@ -220,13 +220,14 @@ def __init__(self, extract_bits: bool=True, cast_type: str = "uintptr_t") -> Non self.extract_bits = extract_bits self.cast_type = cast_type - def drop(self, var: StackItem): + def drop(self, var: StackItem, check_liveness: bool): self.top_offset.pop(var) if self.variables: popped = self.variables.pop() if popped.is_dead() or not var.used: return - raise StackError(f"Dropping live value '{var.name}'") + if check_liveness: + raise StackError(f"Dropping live value '{var.name}'") def pop(self, var: StackItem) -> tuple[str, Local]: self.top_offset.pop(var) @@ -415,6 +416,7 @@ class Storage: stack: Stack inputs: list[Local] outputs: list[Local] + check_liveness: bool spilled: int = 0 @staticmethod @@ -438,11 +440,11 @@ def is_live(var: Local) -> bool: def clear_inputs(self, reason:str) -> None: while self.inputs: tos = self.inputs.pop() - if self.is_live(tos): + if self.is_live(tos) and self.check_liveness: raise StackError( f"Input '{tos.name}' is still live {reason}" ) - self.stack.drop(tos.item) + self.stack.drop(tos.item, self.check_liveness) def clear_dead_inputs(self) -> None: live = "" @@ -452,7 +454,7 @@ def clear_dead_inputs(self) -> None: live = tos.name break self.inputs.pop() - self.stack.drop(tos.item) + self.stack.drop(tos.item, self.check_liveness) for var in self.inputs: if not self.is_live(var): raise StackError( @@ -516,7 +518,7 @@ def reload(self, out: CWriter) -> None: out.emit_reload() @staticmethod - def for_uop(stack: Stack, uop: Uop) -> tuple[list[str], "Storage"]: + def for_uop(stack: Stack, uop: Uop, check_liveness=True) -> tuple[list[str], "Storage"]: code_list: list[str] = [] inputs: list[Local] = [] peeks: list[Local] = [] @@ -542,7 +544,7 @@ def for_uop(stack: Stack, uop: Uop) -> tuple[list[str], "Storage"]: for var in inputs: stack.push(var) outputs = [ Local.undefined(var) for var in uop.stack.outputs if not var.peek ] - return code_list, Storage(stack, inputs, outputs) + return code_list, Storage(stack, inputs, outputs, check_liveness) @staticmethod def copy_list(arg: list[Local]) -> list[Local]: @@ -554,8 +556,8 @@ 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), self.spilled + new_stack, inputs, self.copy_list(self.outputs), + self.check_liveness, self.spilled ) def sanity_check(self) -> None: @@ -586,7 +588,7 @@ def merge(self, other: "Storage", out: CWriter) -> None: if len(self.inputs) != len(other.inputs): self.clear_dead_inputs() other.clear_dead_inputs() - if len(self.inputs) != len(other.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] raise StackError(f"Unmergeable inputs. Differing state of '{diff.name}'") for var, other_var in zip(self.inputs, other.inputs): @@ -605,7 +607,7 @@ 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: + if self.inputs and self.check_liveness: raise StackError(f"Input variable '{self.inputs[-1].name}' is still live") self._push_defined_outputs() if self.outputs: @@ -682,7 +684,7 @@ def close_variable(var: Local, overwrite: str) -> None: self.stack.push(output) self.stack.flush(out) close_variable(self.inputs[0], "") - self.stack.drop(output.item) + self.stack.drop(output.item, self.check_liveness) self.inputs = [] return if var_size(lowest) != var_size(output): @@ -696,7 +698,7 @@ def close_variable(var: Local, overwrite: str) -> None: close_variable(self.inputs[0], "PyStackRef_NULL") for input in reversed(self.inputs[1:]): input.kill() - self.stack.drop(input.item) + self.stack.drop(input.item, self.check_liveness) self.stack.pop(self.inputs[0].item) output_in_place = self.outputs and output is self.outputs[0] and lowest.memory_offset is not None if output_in_place: diff --git a/Tools/cases_generator/tier1_generator.py b/Tools/cases_generator/tier1_generator.py index 6aeabe42c09f32..20cad326a8a2c0 100644 --- a/Tools/cases_generator/tier1_generator.py +++ b/Tools/cases_generator/tier1_generator.py @@ -205,7 +205,7 @@ def generate_tier1_labels( for name, label in analysis.labels.items(): emitter.emit(f"LABEL({name})\n") emitter.emit("{\n") - storage = Storage(Stack(), [], []) + storage = Storage(Stack(), [], [], False) if label.spilled: storage.spilled = 1 emitter.emit("/* STACK SPILLED */\n") From 05a4daaa43426af01a2da33cba80814d6351acb4 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Tue, 25 Mar 2025 18:07:14 +0000 Subject: [PATCH 08/12] Fix RETURN_VALUE in optimizer --- Python/optimizer_bytecodes.c | 4 +++- Python/optimizer_cases.c.h | 9 +++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index cba878748c222e..02463fdb9c3660 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -717,6 +717,8 @@ dummy_func(void) { } op(_RETURN_VALUE, (retval -- res)) { + JitOptSymbol *temp = retval; + DEAD(retval); SAVE_STACK(); ctx->frame->stack_pointer = stack_pointer; frame_pop(ctx); @@ -736,7 +738,7 @@ dummy_func(void) { ctx->done = true; } RELOAD_STACK(); - res = retval; + res = temp; } op(_RETURN_GENERATOR, ( -- res)) { diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 78094595bf03fc..106e7e38b99719 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -702,6 +702,9 @@ JitOptSymbol *retval; JitOptSymbol *res; retval = stack_pointer[-1]; + JitOptSymbol *temp = retval; + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); ctx->frame->stack_pointer = stack_pointer; frame_pop(ctx); stack_pointer = ctx->frame->stack_pointer; @@ -717,8 +720,10 @@ // might be impossible, but bailing is still safe ctx->done = true; } - res = retval; - stack_pointer[-1] = res; + res = temp; + stack_pointer[0] = res; + stack_pointer += 1; + assert(WITHIN_STACK_BOUNDS()); break; } From b8c3a9ebe9ad89c5d2ccc98ea58ae7ddb2262f8e Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Tue, 25 Mar 2025 18:31:54 +0000 Subject: [PATCH 09/12] Fix up type annotations --- Tools/cases_generator/stack.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Tools/cases_generator/stack.py b/Tools/cases_generator/stack.py index 6f157622f195d7..386b5a9577e806 100644 --- a/Tools/cases_generator/stack.py +++ b/Tools/cases_generator/stack.py @@ -171,7 +171,7 @@ def kill(self) -> None: self.in_local = False self.memory_offset = None - def in_memory(self): + def in_memory(self) -> bool: return self.memory_offset is not None or self.is_array() def is_dead(self) -> bool: @@ -220,7 +220,7 @@ def __init__(self, extract_bits: bool=True, cast_type: str = "uintptr_t") -> Non self.extract_bits = extract_bits self.cast_type = cast_type - def drop(self, var: StackItem, check_liveness: bool): + def drop(self, var: StackItem, check_liveness: bool) -> None: self.top_offset.pop(var) if self.variables: popped = self.variables.pop() @@ -376,7 +376,7 @@ def merge(self, other: "Stack", out: CWriter) -> None: for self_var, other_var in zip(self.variables, other.variables): if self_var.memory_offset is not None: if self_var.memory_offset != other_var.memory_offset: - raise StackError(f"Mismatched stack depths for {self_var.name}: {self_var.memory_offset.to_c()} and {other_var.memory_offset.to_c()}") + raise StackError(f"Mismatched stack depths for {self_var.name}: {self_var.memory_offset} and {other_var.memory_offset}") def stacks(inst: Instruction | PseudoInstruction) -> Iterator[StackEffect]: @@ -518,7 +518,7 @@ def reload(self, out: CWriter) -> None: out.emit_reload() @staticmethod - def for_uop(stack: Stack, uop: Uop, check_liveness=True) -> tuple[list[str], "Storage"]: + def for_uop(stack: Stack, uop: Uop, check_liveness: bool = True) -> tuple[list[str], "Storage"]: code_list: list[str] = [] inputs: list[Local] = [] peeks: list[Local] = [] @@ -687,7 +687,7 @@ def close_variable(var: Local, overwrite: str) -> None: self.stack.drop(output.item, self.check_liveness) self.inputs = [] return - if var_size(lowest) != var_size(output): + if var_size(lowest.item) != var_size(output.item): raise StackError("Cannot call DECREF_INPUTS with live output not matching first input size") lowest.in_local = True close_variable(lowest, output.name) @@ -702,7 +702,7 @@ def close_variable(var: Local, overwrite: str) -> None: self.stack.pop(self.inputs[0].item) output_in_place = self.outputs and output is self.outputs[0] and lowest.memory_offset is not None if output_in_place: - output.memory_offset = lowest.memory_offset.copy() + output.memory_offset = lowest.memory_offset.copy() # type: ignore[union-attr] else: self.stack.flush(out) if output is not None: From d81c7de752528ef0641bead68879fe9dc1f5b988 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 26 Mar 2025 10:15:42 +0000 Subject: [PATCH 10/12] Fixup NULLing out array items during DECREF_INPUTS --- Python/generated_cases.c.h | 6 ++++++ Tools/cases_generator/stack.py | 7 +++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 6c923551d9bbed..493d99db2d3677 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2966,6 +2966,7 @@ _PyFrame_SetStackPointer(frame, stack_pointer); _PyStackRef tmp = kwnames; kwnames = PyStackRef_NULL; + stack_pointer[-1] = kwnames; PyStackRef_CLOSE(tmp); for (int _i = oparg; --_i >= 0;) { tmp = args[_i]; @@ -4714,6 +4715,7 @@ PyStackRef_CLOSE(tmp); tmp = left; left = PyStackRef_NULL; + stack_pointer[-2] = left; PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -2; @@ -9402,6 +9404,7 @@ _PyFrame_SetStackPointer(frame, stack_pointer); _PyStackRef tmp = self_st; self_st = PyStackRef_NULL; + stack_pointer[-1] = self_st; PyStackRef_CLOSE(tmp); tmp = class_st; class_st = PyStackRef_NULL; @@ -9448,6 +9451,7 @@ _PyFrame_SetStackPointer(frame, stack_pointer); _PyStackRef tmp = self_st; self_st = PyStackRef_NULL; + stack_pointer[-1] = self_st; PyStackRef_CLOSE(tmp); tmp = class_st; class_st = PyStackRef_NULL; @@ -11172,9 +11176,11 @@ _PyFrame_SetStackPointer(frame, stack_pointer); _PyStackRef tmp = container; container = PyStackRef_NULL; + stack_pointer[-1] = container; PyStackRef_CLOSE(tmp); tmp = v; v = PyStackRef_NULL; + stack_pointer[-2] = v; PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -2; diff --git a/Tools/cases_generator/stack.py b/Tools/cases_generator/stack.py index 386b5a9577e806..62253ccb5e2c0d 100644 --- a/Tools/cases_generator/stack.py +++ b/Tools/cases_generator/stack.py @@ -264,6 +264,7 @@ def pop(self, var: StackItem) -> tuple[str, Local]: defn = f"{var.name} = &stack_pointer[{self.top_offset.to_c()}];\n" else: defn = f"{var.name} = stack_pointer[{self.top_offset.to_c()}];\n" + popped.in_local = True else: defn = rename return defn, popped @@ -680,7 +681,7 @@ def close_variable(var: Local, overwrite: str) -> None: if output is not None: if output.is_array(): assert len(self.inputs) == 1 - self.stack.pop(self.inputs[0].item) + self.stack.drop(self.inputs[0].item, False) self.stack.push(output) self.stack.flush(out) close_variable(self.inputs[0], "") @@ -699,7 +700,9 @@ def close_variable(var: Local, overwrite: str) -> None: for input in reversed(self.inputs[1:]): input.kill() self.stack.drop(input.item, self.check_liveness) - self.stack.pop(self.inputs[0].item) + if output is None: + self.inputs[0].kill() + self.stack.drop(self.inputs[0].item, False) output_in_place = self.outputs and output is self.outputs[0] and lowest.memory_offset is not None if output_in_place: output.memory_offset = lowest.memory_offset.copy() # type: ignore[union-attr] From af5f616fd1bf98e71748590cf59e0c6d30d1d058 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 26 Mar 2025 10:33:27 +0000 Subject: [PATCH 11/12] Cleanup errors --- Tools/cases_generator/generators_common.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tools/cases_generator/generators_common.py b/Tools/cases_generator/generators_common.py index ea99971c0c098c..da22cb3181470c 100644 --- a/Tools/cases_generator/generators_common.py +++ b/Tools/cases_generator/generators_common.py @@ -518,7 +518,7 @@ def _emit_if( reachable = True except StackError as ex: self._print_storage(if_storage) - raise analysis_error(ex.args[0], rbrace) # from None + raise analysis_error(ex.args[0], rbrace) from None return reachable, rbrace, storage def _emit_block( @@ -624,7 +624,7 @@ def emit_tokens( storage.push_outputs() self._print_storage(storage) except StackError as ex: - raise analysis_error(ex.args[0], rbrace) # from None + raise analysis_error(ex.args[0], rbrace) from None return storage def emit(self, txt: str | Token) -> None: From 9238a67f2b408dbf36e6bda8907a06f37c5b66f0 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 26 Mar 2025 11:10:22 +0000 Subject: [PATCH 12/12] Update test output to reflect fix --- Lib/test/test_generated_cases.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_generated_cases.py b/Lib/test/test_generated_cases.py index 518a02633236ca..856da584ca7e9e 100644 --- a/Lib/test/test_generated_cases.py +++ b/Lib/test/test_generated_cases.py @@ -1250,7 +1250,7 @@ def test_unused_used_used(self): } // THIRD { - y = stack_pointer[-1]; + y = x; USE(y); } DISPATCH();