Skip to content

Commit ceb1659

Browse files
committed
Reject uop definitions that declare values as 'unused' that are already cached by prior uops
1 parent 8ad6067 commit ceb1659

File tree

10 files changed

+64
-49
lines changed

10 files changed

+64
-49
lines changed

Lib/test/test_generated_cases.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ def skip_if_different_mount_drives():
3131
with test_tools.imports_under_tool("cases_generator"):
3232
from analyzer import StackItem
3333
import parser
34-
from stack import Stack
34+
from stack import Stack, StackError
3535
import tier1_generator
3636
import optimizer_generator
3737

@@ -827,6 +827,24 @@ def test_deopt_and_exit(self):
827827
with self.assertRaises(Exception):
828828
self.run_cases_test(input, output)
829829

830+
def test_unused_cached_value(self):
831+
832+
input = """
833+
op(FIRST, (arg1 -- out)) {
834+
out = arg1;
835+
}
836+
837+
op(SECOND, (unused -- unused)) {
838+
}
839+
840+
macro(BOTH) = FIRST + SECOND;
841+
"""
842+
output = """
843+
"""
844+
with self.assertRaises(SyntaxError):
845+
self.run_cases_test(input, output)
846+
847+
830848
class TestGeneratedAbstractCases(unittest.TestCase):
831849
def setUp(self) -> None:
832850
super().setUp()

Programs/test_frozenmain.h

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/bytecodes.c

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3448,7 +3448,7 @@ dummy_func(
34483448
}
34493449
}
34503450

3451-
op(_CHECK_FUNCTION_VERSION, (func_version/2, callable, unused, unused[oparg] -- callable, unused, unused[oparg])) {
3451+
op(_CHECK_FUNCTION_VERSION, (func_version/2, callable, self_or_null, unused[oparg] -- callable, self_or_null, unused[oparg])) {
34523452
PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable);
34533453
EXIT_IF(!PyFunction_Check(callable_o));
34543454
PyFunctionObject *func = (PyFunctionObject *)callable_o;
@@ -3479,7 +3479,6 @@ dummy_func(
34793479
assert(PyStackRef_IsNull(null));
34803480
assert(Py_TYPE(callable_o) == &PyMethod_Type);
34813481
self = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_self);
3482-
stack_pointer[-1 - oparg] = self; // Patch stack as it is used by _PY_FRAME_GENERAL
34833482
method = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_func);
34843483
assert(PyFunction_Check(PyStackRef_AsPyObjectBorrow(method)));
34853484
PyStackRef_CLOSE(callable);
@@ -3544,16 +3543,12 @@ dummy_func(
35443543
EXIT_IF(Py_TYPE(PyStackRef_AsPyObjectBorrow(callable)) != &PyMethod_Type);
35453544
}
35463545

3547-
op(_INIT_CALL_BOUND_METHOD_EXACT_ARGS, (callable, unused, unused[oparg] -- func, self, unused[oparg])) {
3546+
op(_INIT_CALL_BOUND_METHOD_EXACT_ARGS, (callable, null, unused[oparg] -- func, self, unused[oparg])) {
35483547
PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable);
35493548
STAT_INC(CALL, hit);
3550-
stack_pointer[-1 - oparg] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_self); // Patch stack as it is used by _INIT_CALL_PY_EXACT_ARGS
3551-
stack_pointer[-2 - oparg] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_func); // This is used by CALL, upon deoptimization
3552-
self = stack_pointer[-1 - oparg];
3553-
func = stack_pointer[-2 - oparg];
3549+
self = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_self);
3550+
func = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_func);
35543551
PyStackRef_CLOSE(callable);
3555-
// self may be unused in tier 1, so silence warnings.
3556-
(void)self;
35573552
}
35583553

35593554
op(_CHECK_PEP_523, (--)) {
@@ -3568,7 +3563,7 @@ dummy_func(
35683563
EXIT_IF(code->co_argcount != oparg + (!PyStackRef_IsNull(self_or_null)));
35693564
}
35703565

3571-
op(_CHECK_STACK_SPACE, (callable, unused, unused[oparg] -- callable, unused, unused[oparg])) {
3566+
op(_CHECK_STACK_SPACE, (callable, self_or_null, unused[oparg] -- callable, self_or_null, unused[oparg])) {
35723567
PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable);
35733568
PyFunctionObject *func = (PyFunctionObject *)callable_o;
35743569
PyCodeObject *code = (PyCodeObject *)func->func_code;

Python/executor_cases.c.h

Lines changed: 8 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/generated_cases.c.h

Lines changed: 7 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Tools/cases_generator/analyzer.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ class StackItem:
103103
condition: str | None
104104
size: str
105105
peek: bool = False
106+
cached: bool = False
106107

107108
def __str__(self) -> str:
108109
cond = f" if ({self.condition})" if self.condition else ""

Tools/cases_generator/optimizer_generator.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
from cwriter import CWriter
2424
from typing import TextIO, Iterator
2525
from lexer import Token
26-
from stack import Stack, SizeMismatch
26+
from stack import Stack, StackError
2727

2828
DEFAULT_OUTPUT = ROOT / "Python/optimizer_cases.c.h"
2929
DEFAULT_ABSTRACT_INPUT = (ROOT / "Python/optimizer_bytecodes.c").absolute().as_posix()
@@ -141,7 +141,7 @@ def write_uop(
141141
out.emit(stack.push(var))
142142
out.start_line()
143143
stack.flush(out, cast_type="_Py_UopsSymbol *", extract_bits=True)
144-
except SizeMismatch as ex:
144+
except StackError as ex:
145145
raise analysis_error(ex.args[0], uop.body[0])
146146

147147

Tools/cases_generator/stack.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ def clear(self) -> None:
113113
self.pushed = []
114114

115115

116-
class SizeMismatch(Exception):
116+
class StackError(Exception):
117117
pass
118118

119119

@@ -133,21 +133,26 @@ def pop(self, var: StackItem, extract_bits: bool = False) -> str:
133133
if self.variables:
134134
popped = self.variables.pop()
135135
if popped.size != var.size:
136-
raise SizeMismatch(
136+
raise StackError(
137137
f"Size mismatch when popping '{popped.name}' from stack to assign to {var.name}. "
138138
f"Expected {var.size} got {popped.size}"
139139
)
140140
if popped.name == var.name:
141+
var.cached = popped.cached
141142
return ""
142143
elif popped.name in UNUSED:
143144
self.defined.add(var.name)
144145
return (
145146
f"{var.name} = {indirect}stack_pointer[{self.top_offset.to_c()}];\n"
146147
)
147148
elif var.name in UNUSED:
149+
if popped.cached:
150+
raise StackError(f"Value is declared unused, but is already cached by prior operation")
148151
return ""
149152
else:
150153
self.defined.add(var.name)
154+
assert popped.cached
155+
var.cached = True
151156
return f"{var.name} = {popped.name};\n"
152157
self.base_offset.pop(var)
153158
if var.name in UNUSED:
@@ -177,6 +182,8 @@ def push(self, var: StackItem) -> str:
177182
return f"{var.name} = &stack_pointer[{c_offset}];\n"
178183
else:
179184
self.top_offset.push(var)
185+
if var.name not in UNUSED:
186+
var.cached = True
180187
return ""
181188

182189
def flush(self, out: CWriter, cast_type: str = "uintptr_t", extract_bits: bool = False) -> None:

Tools/cases_generator/tier1_generator.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
)
2323
from cwriter import CWriter
2424
from typing import TextIO
25-
from stack import Stack, SizeMismatch
25+
from stack import Stack, StackError
2626

2727

2828
DEFAULT_OUTPUT = ROOT / "Python/generated_cases.c.h"
@@ -100,8 +100,8 @@ def write_uop(
100100
out.emit("}\n")
101101
# out.emit(stack.as_comment() + "\n")
102102
return offset
103-
except SizeMismatch as ex:
104-
raise analysis_error(ex.args[0], uop.body[0])
103+
except StackError as ex:
104+
raise analysis_error(ex.args[0], uop.body[0]) from None
105105

106106

107107
def uses_this(inst: Instruction) -> bool:

Tools/cases_generator/tier2_generator.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
from cwriter import CWriter
2525
from typing import TextIO, Iterator
2626
from lexer import Token
27-
from stack import Stack, SizeMismatch
27+
from stack import Stack, StackError
2828

2929
DEFAULT_OUTPUT = ROOT / "Python/executor_cases.c.h"
3030

@@ -176,8 +176,8 @@ def write_uop(uop: Uop, out: CWriter, stack: Stack) -> None:
176176
if uop.properties.stores_sp:
177177
for i, var in enumerate(uop.stack.outputs):
178178
out.emit(stack.push(var))
179-
except SizeMismatch as ex:
180-
raise analysis_error(ex.args[0], uop.body[0])
179+
except StackError as ex:
180+
raise analysis_error(ex.args[0], uop.body[0]) from None
181181

182182

183183
SKIPS = ("_EXTENDED_ARG",)

0 commit comments

Comments
 (0)