From 9162da254aac8bbd30a3bdf90b0c5f0d3b8f7079 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 26 Jul 2024 10:51:42 +0100 Subject: [PATCH] [3.13] GH-122155: Fix cases generator to correctly compute 'peek' offset for error handling (GH-122158) (GH-122174) --- Lib/test/test_generated_cases.py | 50 ++++++++++++++++++++++ Tools/cases_generator/analyzer.py | 6 ++- Tools/cases_generator/generators_common.py | 2 +- Tools/cases_generator/stack.py | 15 +++++-- 4 files changed, 67 insertions(+), 6 deletions(-) diff --git a/Lib/test/test_generated_cases.py b/Lib/test/test_generated_cases.py index 7b9dd36f854..0f34922727e 100644 --- a/Lib/test/test_generated_cases.py +++ b/Lib/test/test_generated_cases.py @@ -813,6 +813,56 @@ class TestGeneratedCases(unittest.TestCase): with self.assertRaises(Exception): self.run_cases_test(input, output) + def test_pop_on_error_peeks(self): + + input = """ + op(FIRST, (x, y -- a, b)) { + a = x; + b = y; + } + + op(SECOND, (a, b -- a, b)) { + } + + op(THIRD, (j, k --)) { + ERROR_IF(cond, error); + } + + macro(TEST) = FIRST + SECOND + THIRD; + """ + output = """ + TARGET(TEST) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(TEST); + PyObject *y; + PyObject *x; + PyObject *a; + PyObject *b; + PyObject *k; + PyObject *j; + // FIRST + y = stack_pointer[-1]; + x = stack_pointer[-2]; + { + a = x; + b = y; + } + // SECOND + { + } + // THIRD + k = b; + j = a; + { + if (cond) goto pop_2_error; + } + stack_pointer += -2; + DISPATCH(); + } + """ + self.run_cases_test(input, output) + class TestGeneratedAbstractCases(unittest.TestCase): def setUp(self) -> None: super().setUp() diff --git a/Tools/cases_generator/analyzer.py b/Tools/cases_generator/analyzer.py index fdb635486b9..77e5e932feb 100644 --- a/Tools/cases_generator/analyzer.py +++ b/Tools/cases_generator/analyzer.py @@ -300,9 +300,13 @@ def analyze_stack(op: parser.InstDef, replace_op_arg_1: str | None = None) -> St convert_stack_item(i, replace_op_arg_1) for i in op.inputs if isinstance(i, parser.StackEffect) ] outputs: list[StackItem] = [convert_stack_item(i, replace_op_arg_1) for i in op.outputs] + # Mark variables with matching names at the base of the stack as "peek" + modified = False for input, output in zip(inputs, outputs): - if input.name == output.name: + if input.name == output.name and not modified: input.peek = output.peek = True + else: + modified = True return StackEffect(inputs, outputs) diff --git a/Tools/cases_generator/generators_common.py b/Tools/cases_generator/generators_common.py index cc9eb8a0e90..acb5ac3a50a 100644 --- a/Tools/cases_generator/generators_common.py +++ b/Tools/cases_generator/generators_common.py @@ -84,7 +84,7 @@ def replace_error( next(tkn_iter) # RPAREN next(tkn_iter) # Semi colon out.emit(") ") - c_offset = stack.peek_offset.to_c() + c_offset = stack.peek_offset() try: offset = -int(c_offset) close = ";\n" diff --git a/Tools/cases_generator/stack.py b/Tools/cases_generator/stack.py index 5aecac39aef..e0038631c1b 100644 --- a/Tools/cases_generator/stack.py +++ b/Tools/cases_generator/stack.py @@ -47,6 +47,9 @@ class StackOffset: def empty() -> "StackOffset": return StackOffset([], []) + def copy(self) -> "StackOffset": + return StackOffset(self.popped[:], self.pushed[:]) + def pop(self, item: StackItem) -> None: self.popped.append(var_size(item)) @@ -120,14 +123,11 @@ class Stack: def __init__(self) -> None: self.top_offset = StackOffset.empty() self.base_offset = StackOffset.empty() - self.peek_offset = StackOffset.empty() self.variables: list[StackItem] = [] self.defined: set[str] = set() def pop(self, var: StackItem) -> str: self.top_offset.pop(var) - if not var.peek: - self.peek_offset.pop(var) indirect = "&" if var.is_array() else "" if self.variables: popped = self.variables.pop() @@ -201,9 +201,16 @@ class Stack: self.variables = [] self.base_offset.clear() self.top_offset.clear() - self.peek_offset.clear() out.start_line() + def peek_offset(self) -> str: + peek = self.base_offset.copy() + for var in self.variables: + if not var.peek: + break + peek.push(var) + return peek.to_c() + def as_comment(self) -> str: return f"/* Variables: {[v.name for v in self.variables]}. Base offset: {self.base_offset.to_c()}. Top offset: {self.top_offset.to_c()} */"