2022-11-03 01:31:26 -03:00
|
|
|
"""Generate the main interpreter switch."""
|
|
|
|
|
|
|
|
# Write the cases to generated_cases.c.h, which is #included in ceval.c.
|
|
|
|
|
|
|
|
# TODO: Reuse C generation framework from deepfreeze.py?
|
|
|
|
|
|
|
|
import argparse
|
|
|
|
import io
|
2022-11-04 21:40:43 -03:00
|
|
|
import os
|
2022-11-04 19:30:17 -03:00
|
|
|
import re
|
2022-11-03 01:31:26 -03:00
|
|
|
import sys
|
|
|
|
|
|
|
|
import parser
|
2022-11-06 13:40:47 -04:00
|
|
|
from parser import InstDef # TODO: Use parser.InstDef
|
2022-11-03 01:31:26 -03:00
|
|
|
|
|
|
|
arg_parser = argparse.ArgumentParser()
|
|
|
|
arg_parser.add_argument("-i", "--input", type=str, default="Python/bytecodes.c")
|
|
|
|
arg_parser.add_argument("-o", "--output", type=str, default="Python/generated_cases.c.h")
|
|
|
|
arg_parser.add_argument("-c", "--compare", action="store_true")
|
|
|
|
arg_parser.add_argument("-q", "--quiet", action="store_true")
|
|
|
|
|
|
|
|
|
|
|
|
def eopen(filename: str, mode: str = "r"):
|
|
|
|
if filename == "-":
|
|
|
|
if "r" in mode:
|
|
|
|
return sys.stdin
|
|
|
|
else:
|
|
|
|
return sys.stdout
|
|
|
|
return open(filename, mode)
|
|
|
|
|
|
|
|
|
2022-11-06 13:40:47 -04:00
|
|
|
def parse_cases(
|
|
|
|
src: str, filename: str|None = None
|
|
|
|
) -> tuple[list[InstDef], list[parser.Super], list[parser.Family]]:
|
2022-11-03 01:31:26 -03:00
|
|
|
psr = parser.Parser(src, filename=filename)
|
|
|
|
instrs: list[InstDef] = []
|
2022-11-06 13:40:47 -04:00
|
|
|
supers: list[parser.Super] = []
|
2022-11-03 01:31:26 -03:00
|
|
|
families: list[parser.Family] = []
|
|
|
|
while not psr.eof():
|
|
|
|
if inst := psr.inst_def():
|
|
|
|
assert inst.block
|
2022-11-06 13:40:47 -04:00
|
|
|
instrs.append(inst)
|
|
|
|
elif sup := psr.super_def():
|
|
|
|
supers.append(sup)
|
2022-11-03 01:31:26 -03:00
|
|
|
elif fam := psr.family_def():
|
|
|
|
families.append(fam)
|
|
|
|
else:
|
|
|
|
raise psr.make_syntax_error(f"Unexpected token")
|
2022-11-06 13:40:47 -04:00
|
|
|
return instrs, supers, families
|
2022-11-03 01:31:26 -03:00
|
|
|
|
|
|
|
|
|
|
|
def always_exits(block: parser.Block) -> bool:
|
|
|
|
text = block.text
|
|
|
|
lines = text.splitlines()
|
|
|
|
while lines and not lines[-1].strip():
|
|
|
|
lines.pop()
|
|
|
|
if not lines or lines[-1].strip() != "}":
|
|
|
|
return False
|
|
|
|
lines.pop()
|
|
|
|
if not lines:
|
|
|
|
return False
|
|
|
|
line = lines.pop().rstrip()
|
|
|
|
# Indent must match exactly (TODO: Do something better)
|
|
|
|
if line[:12] != " "*12:
|
|
|
|
return False
|
|
|
|
line = line[12:]
|
|
|
|
return line.startswith(("goto ", "return ", "DISPATCH", "GO_TO_", "Py_UNREACHABLE()"))
|
|
|
|
|
|
|
|
|
2022-11-06 13:40:47 -04:00
|
|
|
def write_cases(f: io.TextIOBase, instrs: list[InstDef], supers: list[parser.Super]):
|
2022-11-04 19:30:17 -03:00
|
|
|
predictions = set()
|
|
|
|
for inst in instrs:
|
|
|
|
for target in re.findall(r"(?:PREDICT|GO_TO_INSTRUCTION)\((\w+)\)", inst.block.text):
|
|
|
|
predictions.add(target)
|
2022-11-03 01:31:26 -03:00
|
|
|
indent = " "
|
2022-11-04 21:40:43 -03:00
|
|
|
f.write(f"// This file is generated by {os.path.relpath(__file__)}\n")
|
2022-11-03 01:31:26 -03:00
|
|
|
f.write("// Do not edit!\n")
|
2022-11-06 13:40:47 -04:00
|
|
|
instr_index: dict[str, InstDef] = {}
|
2022-11-03 01:31:26 -03:00
|
|
|
for instr in instrs:
|
|
|
|
assert isinstance(instr, InstDef)
|
2022-11-06 13:40:47 -04:00
|
|
|
instr_index[instr.name] = instr
|
2022-11-03 01:31:26 -03:00
|
|
|
f.write(f"\n{indent}TARGET({instr.name}) {{\n")
|
2022-11-04 19:30:17 -03:00
|
|
|
if instr.name in predictions:
|
|
|
|
f.write(f"{indent} PREDICTED({instr.name});\n")
|
2022-11-03 01:31:26 -03:00
|
|
|
# input = ", ".join(instr.inputs)
|
|
|
|
# output = ", ".join(instr.outputs)
|
|
|
|
# f.write(f"{indent} // {input} -- {output}\n")
|
|
|
|
assert instr.block
|
|
|
|
blocklines = instr.block.text.splitlines(True)
|
|
|
|
# Remove blank lines from ends
|
|
|
|
while blocklines and not blocklines[0].strip():
|
|
|
|
blocklines.pop(0)
|
|
|
|
while blocklines and not blocklines[-1].strip():
|
|
|
|
blocklines.pop()
|
|
|
|
# Remove leading '{' and trailing '}'
|
|
|
|
assert blocklines and blocklines[0].strip() == "{"
|
|
|
|
assert blocklines and blocklines[-1].strip() == "}"
|
|
|
|
blocklines.pop()
|
|
|
|
blocklines.pop(0)
|
|
|
|
# Remove trailing blank lines
|
|
|
|
while blocklines and not blocklines[-1].strip():
|
|
|
|
blocklines.pop()
|
|
|
|
# Write the body
|
|
|
|
for line in blocklines:
|
|
|
|
f.write(line)
|
|
|
|
assert instr.block
|
|
|
|
if not always_exits(instr.block):
|
|
|
|
f.write(f"{indent} DISPATCH();\n")
|
|
|
|
# Write trailing '}'
|
|
|
|
f.write(f"{indent}}}\n")
|
|
|
|
|
2022-11-06 13:40:47 -04:00
|
|
|
for sup in supers:
|
|
|
|
assert isinstance(sup, parser.Super)
|
|
|
|
components = [instr_index[name] for name in sup.ops]
|
|
|
|
f.write(f"\n{indent}TARGET({sup.name}) {{\n")
|
|
|
|
for i, instr in enumerate(components):
|
|
|
|
if i > 0:
|
|
|
|
f.write(f"{indent} NEXTOPARG();\n")
|
|
|
|
f.write(f"{indent} next_instr++;\n")
|
|
|
|
text = instr.block.to_text(-4)
|
|
|
|
textlines = text.splitlines(True)
|
|
|
|
textlines = [line for line in textlines if not line.strip().startswith("PREDICTED(")]
|
|
|
|
text = "".join(textlines)
|
|
|
|
f.write(f"{indent} {text.strip()}\n")
|
|
|
|
f.write(f"{indent} DISPATCH();\n")
|
|
|
|
f.write(f"{indent}}}\n")
|
|
|
|
|
2022-11-03 01:31:26 -03:00
|
|
|
|
|
|
|
def main():
|
|
|
|
args = arg_parser.parse_args()
|
|
|
|
with eopen(args.input) as f:
|
|
|
|
srclines = f.read().splitlines()
|
|
|
|
begin = srclines.index("// BEGIN BYTECODES //")
|
|
|
|
end = srclines.index("// END BYTECODES //")
|
|
|
|
src = "\n".join(srclines[begin+1 : end])
|
2022-11-06 13:40:47 -04:00
|
|
|
instrs, supers, families = parse_cases(src, filename=args.input)
|
|
|
|
ninstrs = nsupers = nfamilies = 0
|
2022-11-03 01:31:26 -03:00
|
|
|
if not args.quiet:
|
|
|
|
ninstrs = len(instrs)
|
2022-11-06 13:40:47 -04:00
|
|
|
nsupers = len(supers)
|
2022-11-03 01:31:26 -03:00
|
|
|
nfamilies = len(families)
|
|
|
|
print(
|
2022-11-06 13:40:47 -04:00
|
|
|
f"Read {ninstrs} instructions, {nsupers} supers, "
|
2022-11-03 01:31:26 -03:00
|
|
|
f"and {nfamilies} families from {args.input}",
|
|
|
|
file=sys.stderr,
|
|
|
|
)
|
|
|
|
with eopen(args.output, "w") as f:
|
2022-11-06 13:40:47 -04:00
|
|
|
write_cases(f, instrs, supers)
|
2022-11-03 01:31:26 -03:00
|
|
|
if not args.quiet:
|
|
|
|
print(
|
2022-11-06 13:40:47 -04:00
|
|
|
f"Wrote {ninstrs + nsupers} instructions to {args.output}",
|
2022-11-03 01:31:26 -03:00
|
|
|
file=sys.stderr,
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|