GH-118926: Better distinguish between pointer and arrays in interpreter generator (GH-121496)

This commit is contained in:
Mark Shannon 2024-07-09 11:33:56 +01:00 committed by GitHub
parent facf9862da
commit bf8686e1ea
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 73 additions and 19 deletions

View File

@ -815,7 +815,6 @@ class TestGeneratedCases(unittest.TestCase):
""" """
self.run_cases_test(input, output) self.run_cases_test(input, output)
def test_deopt_and_exit(self): def test_deopt_and_exit(self):
input = """ input = """
pure op(OP, (arg1 -- out)) { pure op(OP, (arg1 -- out)) {
@ -827,6 +826,49 @@ class TestGeneratedCases(unittest.TestCase):
with self.assertRaises(Exception): with self.assertRaises(Exception):
self.run_cases_test(input, output) self.run_cases_test(input, output)
def test_array_of_one(self):
input = """
inst(OP, (arg[1] -- out[1])) {
out[0] = arg[0];
}
"""
output = """
TARGET(OP) {
frame->instr_ptr = next_instr;
next_instr += 1;
INSTRUCTION_STATS(OP);
_PyStackRef *arg;
_PyStackRef *out;
arg = &stack_pointer[-1];
out = &stack_pointer[-1];
out[0] = arg[0];
DISPATCH();
}
"""
self.run_cases_test(input, output)
def test_pointer_to_stackref(self):
input = """
inst(OP, (arg: _PyStackRef * -- out)) {
out = *arg;
}
"""
output = """
TARGET(OP) {
frame->instr_ptr = next_instr;
next_instr += 1;
INSTRUCTION_STATS(OP);
_PyStackRef *arg;
_PyStackRef out;
arg = (_PyStackRef *)stack_pointer[-1].bits;
out = *arg;
stack_pointer[-1] = out;
DISPATCH();
}
"""
self.run_cases_test(input, output)
class TestGeneratedAbstractCases(unittest.TestCase): class TestGeneratedAbstractCases(unittest.TestCase):
def setUp(self) -> None: def setUp(self) -> None:
super().setUp() super().setUp()

View File

@ -106,13 +106,15 @@ class StackItem:
def __str__(self) -> str: def __str__(self) -> str:
cond = f" if ({self.condition})" if self.condition else "" cond = f" if ({self.condition})" if self.condition else ""
size = f"[{self.size}]" if self.size != "1" else "" size = f"[{self.size}]" if self.size else ""
type = "" if self.type is None else f"{self.type} " type = "" if self.type is None else f"{self.type} "
return f"{type}{self.name}{size}{cond} {self.peek}" return f"{type}{self.name}{size}{cond} {self.peek}"
def is_array(self) -> bool: def is_array(self) -> bool:
return self.type == "_PyStackRef *" return self.size != ""
def get_size(self) -> str:
return self.size if self.size else "1"
@dataclass @dataclass
class StackEffect: class StackEffect:
@ -293,7 +295,7 @@ def convert_stack_item(item: parser.StackEffect, replace_op_arg_1: str | None) -
if replace_op_arg_1 and OPARG_AND_1.match(item.cond): if replace_op_arg_1 and OPARG_AND_1.match(item.cond):
cond = replace_op_arg_1 cond = replace_op_arg_1
return StackItem( return StackItem(
item.name, item.type, cond, (item.size or "1") item.name, item.type, cond, item.size
) )
def analyze_stack(op: parser.InstDef | parser.Pseudo, replace_op_arg_1: str | None = None) -> StackEffect: def analyze_stack(op: parser.InstDef | parser.Pseudo, replace_op_arg_1: str | None = None) -> StackEffect:

View File

@ -5,9 +5,10 @@ from analyzer import (
Instruction, Instruction,
Uop, Uop,
Properties, Properties,
StackItem,
) )
from cwriter import CWriter from cwriter import CWriter
from typing import Callable, Mapping, TextIO, Iterator from typing import Callable, Mapping, TextIO, Iterator, Tuple
from lexer import Token from lexer import Token
from stack import Stack from stack import Stack
@ -24,6 +25,15 @@ def root_relative_path(filename: str) -> str:
return filename return filename
def type_and_null(var: StackItem) -> Tuple[str, str]:
if var.type:
return var.type, "NULL"
elif var.is_array():
return "_PyStackRef *", "NULL"
else:
return "_PyStackRef", "PyStackRef_NULL"
def write_header( def write_header(
generator: str, sources: list[str], outfile: TextIO, comment: str = "//" generator: str, sources: list[str], outfile: TextIO, comment: str = "//"
) -> None: ) -> None:
@ -126,7 +136,7 @@ def replace_decrefs(
for var in uop.stack.inputs: for var in uop.stack.inputs:
if var.name == "unused" or var.name == "null" or var.peek: if var.name == "unused" or var.name == "null" or var.peek:
continue continue
if var.size != "1": if var.size:
out.emit(f"for (int _i = {var.size}; --_i >= 0;) {{\n") out.emit(f"for (int _i = {var.size}; --_i >= 0;) {{\n")
out.emit(f"PyStackRef_CLOSE({var.name}[_i]);\n") out.emit(f"PyStackRef_CLOSE({var.name}[_i]);\n")
out.emit("}\n") out.emit("}\n")

View File

@ -285,7 +285,6 @@ class Parser(PLexer):
if not (size := self.expression()): if not (size := self.expression()):
raise self.make_syntax_error("Expected expression") raise self.make_syntax_error("Expected expression")
self.require(lx.RBRACKET) self.require(lx.RBRACKET)
type_text = "_PyStackRef *"
size_text = size.text.strip() size_text = size.text.strip()
return StackEffect(tkn.text, type_text, cond_text, size_text) return StackEffect(tkn.text, type_text, cond_text, size_text)
return None return None

View File

@ -28,14 +28,15 @@ def var_size(var: StackItem) -> str:
if var.condition == "0": if var.condition == "0":
return "0" return "0"
elif var.condition == "1": elif var.condition == "1":
return var.size return var.get_size()
elif var.condition == "oparg & 1" and var.size == "1": elif var.condition == "oparg & 1" and not var.size:
return f"({var.condition})" return f"({var.condition})"
else: else:
return f"(({var.condition}) ? {var.size} : 0)" return f"(({var.condition}) ? {var.get_size()} : 0)"
else: elif var.size:
return var.size return var.size
else:
return "1"
@dataclass @dataclass
class StackOffset: class StackOffset:

View File

@ -13,12 +13,14 @@ from analyzer import (
analyze_files, analyze_files,
Skip, Skip,
analysis_error, analysis_error,
StackItem,
) )
from generators_common import ( from generators_common import (
DEFAULT_INPUT, DEFAULT_INPUT,
ROOT, ROOT,
write_header, write_header,
emit_tokens, emit_tokens,
type_and_null,
) )
from cwriter import CWriter from cwriter import CWriter
from typing import TextIO from typing import TextIO
@ -38,19 +40,16 @@ def declare_variables(inst: Instruction, out: CWriter) -> None:
for var in reversed(uop.stack.inputs): for var in reversed(uop.stack.inputs):
if var.name not in variables: if var.name not in variables:
variables.add(var.name) variables.add(var.name)
type, null = (var.type, "NULL") if var.type else ("_PyStackRef", "PyStackRef_NULL") type, null = type_and_null(var)
space = " " if type[-1].isalnum() else "" space = " " if type[-1].isalnum() else ""
if var.condition: if var.condition:
out.emit(f"{type}{space}{var.name} = {null};\n") out.emit(f"{type}{space}{var.name} = {null};\n")
else: else:
if var.is_array(): out.emit(f"{type}{space}{var.name};\n")
out.emit(f"{var.type}{space}{var.name};\n")
else:
out.emit(f"{type}{space}{var.name};\n")
for var in uop.stack.outputs: for var in uop.stack.outputs:
if var.name not in variables: if var.name not in variables:
variables.add(var.name) variables.add(var.name)
type, null = (var.type, "NULL") if var.type else ("_PyStackRef", "PyStackRef_NULL") type, null = type_and_null(var)
space = " " if type[-1].isalnum() else "" space = " " if type[-1].isalnum() else ""
if var.condition: if var.condition:
out.emit(f"{type}{space}{var.name} = {null};\n") out.emit(f"{type}{space}{var.name} = {null};\n")

View File

@ -20,6 +20,7 @@ from generators_common import (
emit_tokens, emit_tokens,
emit_to, emit_to,
REPLACEMENT_FUNCTIONS, REPLACEMENT_FUNCTIONS,
type_and_null,
) )
from cwriter import CWriter from cwriter import CWriter
from typing import TextIO, Iterator from typing import TextIO, Iterator
@ -35,7 +36,7 @@ def declare_variable(
if var.name in variables: if var.name in variables:
return return
variables.add(var.name) variables.add(var.name)
type, null = (var.type, "NULL") if var.type else ("_PyStackRef", "PyStackRef_NULL") type, null = type_and_null(var)
space = " " if type[-1].isalnum() else "" space = " " if type[-1].isalnum() else ""
if var.condition: if var.condition:
out.emit(f"{type}{space}{var.name} = {null};\n") out.emit(f"{type}{space}{var.name} = {null};\n")