mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			132 lines
		
	
	
	
		
			4.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			132 lines
		
	
	
	
		
			4.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| """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
 | |
| import re
 | |
| import sys
 | |
| 
 | |
| import parser
 | |
| from parser import InstDef
 | |
| 
 | |
| 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)
 | |
| 
 | |
| 
 | |
| def parse_cases(src: str, filename: str|None = None) -> tuple[list[InstDef], list[parser.Family]]:
 | |
|     psr = parser.Parser(src, filename=filename)
 | |
|     instrs: list[InstDef] = []
 | |
|     families: list[parser.Family] = []
 | |
|     while not psr.eof():
 | |
|         if inst := psr.inst_def():
 | |
|             assert inst.block
 | |
|             instrs.append(InstDef(inst.name, inst.inputs, inst.outputs, inst.block))
 | |
|         elif fam := psr.family_def():
 | |
|             families.append(fam)
 | |
|         else:
 | |
|             raise psr.make_syntax_error(f"Unexpected token")
 | |
|     return instrs, families
 | |
| 
 | |
| 
 | |
| 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()"))
 | |
| 
 | |
| 
 | |
| def write_cases(f: io.TextIOBase, instrs: list[InstDef]):
 | |
|     predictions = set()
 | |
|     for inst in instrs:
 | |
|         for target in re.findall(r"(?:PREDICT|GO_TO_INSTRUCTION)\((\w+)\)", inst.block.text):
 | |
|             predictions.add(target)
 | |
|     indent = "        "
 | |
|     f.write("// This file is generated by Tools/scripts/generate_cases.py\n")
 | |
|     f.write("// Do not edit!\n")
 | |
|     for instr in instrs:
 | |
|         assert isinstance(instr, InstDef)
 | |
|         f.write(f"\n{indent}TARGET({instr.name}) {{\n")
 | |
|         if instr.name in predictions:
 | |
|             f.write(f"{indent}    PREDICTED({instr.name});\n")
 | |
|         # 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")
 | |
| 
 | |
| 
 | |
| 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])
 | |
|     instrs, families = parse_cases(src, filename=args.input)
 | |
|     ninstrs = nfamilies = 0
 | |
|     if not args.quiet:
 | |
|         ninstrs = len(instrs)
 | |
|         nfamilies = len(families)
 | |
|         print(
 | |
|             f"Read {ninstrs} instructions "
 | |
|             f"and {nfamilies} families from {args.input}",
 | |
|             file=sys.stderr,
 | |
|         )
 | |
|     with eopen(args.output, "w") as f:
 | |
|         write_cases(f, instrs)
 | |
|     if not args.quiet:
 | |
|         print(
 | |
|             f"Wrote {ninstrs} instructions to {args.output}",
 | |
|             file=sys.stderr,
 | |
|         )
 | |
| 
 | |
| 
 | |
| if __name__ == "__main__":
 | |
|     main()
 | 
