Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GH-120024: Refactor code generators to uses classes for emitting code. #122730

Merged
merged 1 commit into from
Aug 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
298 changes: 150 additions & 148 deletions Tools/cases_generator/generators_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,169 +57,171 @@ def emit_to(out: CWriter, tkn_iter: Iterator[Token], end: str) -> None:
parens -= 1
out.emit(tkn)

ReplacementFunctionType = Callable[
[Token, Iterator[Token], Uop, Stack, Instruction | None], None
]

def replace_deopt(
out: CWriter,
tkn: Token,
tkn_iter: Iterator[Token],
uop: Uop,
unused: Stack,
inst: Instruction | None,
) -> None:
out.emit_at("DEOPT_IF", tkn)
out.emit(next(tkn_iter))
emit_to(out, tkn_iter, "RPAREN")
next(tkn_iter) # Semi colon
out.emit(", ")
assert inst is not None
assert inst.family is not None
out.emit(inst.family.name)
out.emit(");\n")

class Emitter:

def replace_error(
out: CWriter,
tkn: Token,
tkn_iter: Iterator[Token],
uop: Uop,
stack: Stack,
inst: Instruction | None,
) -> None:
out.emit_at("if ", tkn)
out.emit(next(tkn_iter))
emit_to(out, tkn_iter, "COMMA")
label = next(tkn_iter).text
next(tkn_iter) # RPAREN
next(tkn_iter) # Semi colon
out.emit(") ")
c_offset = stack.peek_offset()
try:
offset = -int(c_offset)
except ValueError:
offset = -1
if offset > 0:
out.emit(f"goto pop_{offset}_")
out.emit(label)
out.emit(";\n")
elif offset == 0:
out.emit("goto ")
out.emit(label)
out.emit(";\n")
else:
out.emit("{\n")
stack.flush_locally(out)
out.emit("goto ")
out.emit(label)
out.emit(";\n")
out.emit("}\n")
out: CWriter
_replacers: dict[str, ReplacementFunctionType]

def __init__(self, out: CWriter):
self._replacers = {
"EXIT_IF": self.exit_if,
"DEOPT_IF": self.deopt_if,
"ERROR_IF": self.error_if,
"ERROR_NO_POP": self.error_no_pop,
"DECREF_INPUTS": self.decref_inputs,
"CHECK_EVAL_BREAKER": self.check_eval_breaker,
"SYNC_SP": self.sync_sp,
}
self.out = out

def replace_error_no_pop(
out: CWriter,
tkn: Token,
tkn_iter: Iterator[Token],
uop: Uop,
stack: Stack,
inst: Instruction | None,
) -> None:
next(tkn_iter) # LPAREN
next(tkn_iter) # RPAREN
next(tkn_iter) # Semi colon
out.emit_at("goto error;", tkn)
def deopt_if(
self,
tkn: Token,
tkn_iter: Iterator[Token],
uop: Uop,
unused: Stack,
inst: Instruction | None,
) -> None:
self.out.emit_at("DEOPT_IF", tkn)
self.out.emit(next(tkn_iter))
emit_to(self.out, tkn_iter, "RPAREN")
next(tkn_iter) # Semi colon
self.out.emit(", ")
assert inst is not None
assert inst.family is not None
self.out.emit(inst.family.name)
self.out.emit(");\n")

exit_if = deopt_if

def replace_decrefs(
out: CWriter,
tkn: Token,
tkn_iter: Iterator[Token],
uop: Uop,
stack: Stack,
inst: Instruction | None,
) -> None:
next(tkn_iter)
next(tkn_iter)
next(tkn_iter)
out.emit_at("", tkn)
for var in uop.stack.inputs:
if var.name == "unused" or var.name == "null" or var.peek:
continue
if var.size:
out.emit(f"for (int _i = {var.size}; --_i >= 0;) {{\n")
out.emit(f"PyStackRef_CLOSE({var.name}[_i]);\n")
out.emit("}\n")
elif var.condition:
if var.condition == "1":
out.emit(f"PyStackRef_CLOSE({var.name});\n")
elif var.condition != "0":
out.emit(f"PyStackRef_XCLOSE({var.name});\n")
def error_if(
self,
tkn: Token,
tkn_iter: Iterator[Token],
uop: Uop,
stack: Stack,
inst: Instruction | None,
) -> None:
self.out.emit_at("if ", tkn)
self.out.emit(next(tkn_iter))
emit_to(self.out, tkn_iter, "COMMA")
label = next(tkn_iter).text
next(tkn_iter) # RPAREN
next(tkn_iter) # Semi colon
self.out.emit(") ")
c_offset = stack.peek_offset()
try:
offset = -int(c_offset)
except ValueError:
offset = -1
if offset > 0:
self.out.emit(f"goto pop_{offset}_")
self.out.emit(label)
self.out.emit(";\n")
elif offset == 0:
self.out.emit("goto ")
self.out.emit(label)
self.out.emit(";\n")
else:
out.emit(f"PyStackRef_CLOSE({var.name});\n")
self.out.emit("{\n")
stack.flush_locally(self.out)
self.out.emit("goto ")
self.out.emit(label)
self.out.emit(";\n")
self.out.emit("}\n")

def error_no_pop(
self,
tkn: Token,
tkn_iter: Iterator[Token],
uop: Uop,
stack: Stack,
inst: Instruction | None,
) -> None:
next(tkn_iter) # LPAREN
next(tkn_iter) # RPAREN
next(tkn_iter) # Semi colon
self.out.emit_at("goto error;", tkn)

def replace_sync_sp(
out: CWriter,
tkn: Token,
tkn_iter: Iterator[Token],
uop: Uop,
stack: Stack,
inst: Instruction | None,
) -> None:
next(tkn_iter)
next(tkn_iter)
next(tkn_iter)
stack.flush(out)

def decref_inputs(
self,
tkn: Token,
tkn_iter: Iterator[Token],
uop: Uop,
stack: Stack,
inst: Instruction | None,
) -> None:
next(tkn_iter)
next(tkn_iter)
next(tkn_iter)
self.out.emit_at("", tkn)
for var in uop.stack.inputs:
if var.name == "unused" or var.name == "null" or var.peek:
continue
if var.size:
self.out.emit(f"for (int _i = {var.size}; --_i >= 0;) {{\n")
self.out.emit(f"PyStackRef_CLOSE({var.name}[_i]);\n")
self.out.emit("}\n")
elif var.condition:
if var.condition == "1":
self.out.emit(f"PyStackRef_CLOSE({var.name});\n")
elif var.condition != "0":
self.out.emit(f"PyStackRef_XCLOSE({var.name});\n")
else:
self.out.emit(f"PyStackRef_CLOSE({var.name});\n")

def replace_check_eval_breaker(
out: CWriter,
tkn: Token,
tkn_iter: Iterator[Token],
uop: Uop,
stack: Stack,
inst: Instruction | None,
) -> None:
next(tkn_iter)
next(tkn_iter)
next(tkn_iter)
if not uop.properties.ends_with_eval_breaker:
out.emit_at("CHECK_EVAL_BREAKER();", tkn)

def sync_sp(
self,
tkn: Token,
tkn_iter: Iterator[Token],
uop: Uop,
stack: Stack,
inst: Instruction | None,
) -> None:
next(tkn_iter)
next(tkn_iter)
next(tkn_iter)
stack.flush(self.out)

REPLACEMENT_FUNCTIONS = {
"EXIT_IF": replace_deopt,
"DEOPT_IF": replace_deopt,
"ERROR_IF": replace_error,
"ERROR_NO_POP": replace_error_no_pop,
"DECREF_INPUTS": replace_decrefs,
"CHECK_EVAL_BREAKER": replace_check_eval_breaker,
"SYNC_SP": replace_sync_sp,
}

ReplacementFunctionType = Callable[
[CWriter, Token, Iterator[Token], Uop, Stack, Instruction | None], None
]

def check_eval_breaker(
self,
tkn: Token,
tkn_iter: Iterator[Token],
uop: Uop,
stack: Stack,
inst: Instruction | None,
) -> None:
next(tkn_iter)
next(tkn_iter)
next(tkn_iter)
if not uop.properties.ends_with_eval_breaker:
self.out.emit_at("CHECK_EVAL_BREAKER();", tkn)

def emit_tokens(
out: CWriter,
uop: Uop,
stack: Stack,
inst: Instruction | None,
replacement_functions: Mapping[
str, ReplacementFunctionType
] = REPLACEMENT_FUNCTIONS,
) -> None:
tkns = uop.body[1:-1]
if not tkns:
return
tkn_iter = iter(tkns)
out.start_line()
for tkn in tkn_iter:
if tkn.kind == "IDENTIFIER" and tkn.text in replacement_functions:
replacement_functions[tkn.text](out, tkn, tkn_iter, uop, stack, inst)
else:
out.emit(tkn)
def emit_tokens(
self,
uop: Uop,
stack: Stack,
inst: Instruction | None,
) -> None:
tkns = uop.body[1:-1]
if not tkns:
return
tkn_iter = iter(tkns)
self.out.start_line()
for tkn in tkn_iter:
if tkn.kind == "IDENTIFIER" and tkn.text in self._replacers:
self._replacers[tkn.text](tkn, tkn_iter, uop, stack, inst)
else:
self.out.emit(tkn)

def emit(self, txt: str | Token) -> None:
self.out.emit(txt)

def cflags(p: Properties) -> str:
flags: list[str] = []
Expand Down
14 changes: 7 additions & 7 deletions Tools/cases_generator/optimizer_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@
DEFAULT_INPUT,
ROOT,
write_header,
emit_tokens,
replace_sync_sp,
Emitter,
)
from cwriter import CWriter
from typing import TextIO, Iterator
Expand Down Expand Up @@ -89,6 +88,10 @@ def emit_default(out: CWriter, uop: Uop) -> None:
else:
out.emit(f"{var.name} = sym_new_not_null(ctx);\n")

class OptimizerEmitter(Emitter):

pass


def write_uop(
override: Uop | None,
Expand Down Expand Up @@ -126,11 +129,8 @@ def write_uop(
cast = f"uint{cache.size*16}_t"
out.emit(f"{type}{cache.name} = ({cast})this_instr->operand;\n")
if override:
replacement_funcs = {
"DECREF_INPUTS": decref_inputs,
"SYNC_SP": replace_sync_sp,
}
emit_tokens(out, override, stack, None, replacement_funcs)
emitter = OptimizerEmitter(out)
emitter.emit_tokens(override, stack, None)
else:
emit_default(out, uop)

Expand Down
Loading
Loading