55# TODO: Reuse C generation framework from deepfreeze.py?
66
77import argparse
8- import io
98import os
109import re
1110import sys
11+ from typing import TextIO , cast
1212
1313import parser
1414from parser import InstDef # TODO: Use parser.InstDef
2020arg_parser .add_argument ("-q" , "--quiet" , action = "store_true" )
2121
2222
23- def eopen (filename : str , mode : str = "r" ):
23+ def eopen (filename : str , mode : str = "r" ) -> TextIO :
2424 if filename == "-" :
2525 if "r" in mode :
2626 return sys .stdin
2727 else :
2828 return sys .stdout
29- return open (filename , mode )
29+ return cast ( TextIO , open (filename , mode ) )
3030
3131
3232def parse_cases (
@@ -67,66 +67,73 @@ def always_exits(block: parser.Block) -> bool:
6767 return line .startswith (("goto " , "return " , "DISPATCH" , "GO_TO_" , "Py_UNREACHABLE()" ))
6868
6969
70- def write_cases (f : io .TextIOBase , instrs : list [InstDef ], supers : list [parser .Super ]):
70+ def write_instr (instr : InstDef , predictions : set [str ], indent : str , f : TextIO , dedent : int = 0 ):
71+ assert instr .block
72+ if dedent < 0 :
73+ indent += " " * - dedent
74+ # TODO: Is it better to count forward or backward?
75+ for i , input in enumerate (reversed (instr .inputs or ()), 1 ):
76+ f .write (f"{ indent } PyObject *{ input } = PEEK({ i } );\n " )
77+ for output in instr .outputs or ():
78+ f .write (f"{ indent } PyObject *{ output } ;\n " )
79+ # input = ", ".join(instr.inputs)
80+ # output = ", ".join(instr.outputs)
81+ # f.write(f"{indent} // {input} -- {output}\n")
82+ assert instr .block is not None
83+ blocklines = instr .block .to_text (dedent = dedent ).splitlines (True )
84+ # Remove blank lines from ends
85+ while blocklines and not blocklines [0 ].strip ():
86+ blocklines .pop (0 )
87+ while blocklines and not blocklines [- 1 ].strip ():
88+ blocklines .pop ()
89+ # Remove leading '{' and trailing '}'
90+ assert blocklines and blocklines [0 ].strip () == "{"
91+ assert blocklines and blocklines [- 1 ].strip () == "}"
92+ blocklines .pop ()
93+ blocklines .pop (0 )
94+ # Remove trailing blank lines
95+ while blocklines and not blocklines [- 1 ].strip ():
96+ blocklines .pop ()
97+ # Write the body
98+ ninputs = len (instr .inputs or ())
99+ for line in blocklines :
100+ if m := re .match (r"(\s*)ERROR_IF\(([^,]+), (\w+)\);\s*$" , line ):
101+ space , cond , label = m .groups ()
102+ # ERROR_IF() must remove the inputs from the stack.
103+ # The code block is responsible for DECREF()ing them.
104+ if ninputs :
105+ f .write (f"{ space } if ({ cond } ) {{ STACK_SHRINK({ ninputs } ); goto { label } ; }}\n " )
106+ else :
107+ f .write (f"{ space } if ({ cond } ) {{ goto { label } ; }}\n " )
108+ else :
109+ f .write (line )
110+ noutputs = len (instr .outputs or ())
111+ diff = noutputs - ninputs
112+ if diff > 0 :
113+ f .write (f"{ indent } STACK_GROW({ diff } );\n " )
114+ elif diff < 0 :
115+ f .write (f"{ indent } STACK_SHRINK({ - diff } );\n " )
116+ for i , output in enumerate (reversed (instr .outputs or ()), 1 ):
117+ f .write (f"{ indent } POKE({ i } , { output } );\n " )
118+ assert instr .block
119+
120+ def write_cases (f : TextIO , instrs : list [InstDef ], supers : list [parser .Super ]):
71121 predictions : set [str ] = set ()
72- for inst in instrs :
73- assert inst .block is not None
74- for target in re .findall (r"(?:PREDICT|GO_TO_INSTRUCTION)\((\w+)\)" , inst .block .text ):
122+ for instr in instrs :
123+ assert isinstance (instr , InstDef )
124+ assert instr .block is not None
125+ for target in re .findall (r"(?:PREDICT|GO_TO_INSTRUCTION)\((\w+)\)" , instr .block .text ):
75126 predictions .add (target )
76127 indent = " "
77128 f .write (f"// This file is generated by { os .path .relpath (__file__ )} \n " )
78- f .write ("// Do not edit!\n " )
129+ f .write (f "// Do not edit!\n " )
79130 instr_index : dict [str , InstDef ] = {}
80131 for instr in instrs :
81- assert isinstance (instr , InstDef )
82132 instr_index [instr .name ] = instr
83133 f .write (f"\n { indent } TARGET({ instr .name } ) {{\n " )
84134 if instr .name in predictions :
85135 f .write (f"{ indent } PREDICTED({ instr .name } );\n " )
86- # TODO: Is it better to count forward or backward?
87- for i , input in enumerate (reversed (instr .inputs or ()), 1 ):
88- f .write (f"{ indent } PyObject *{ input } = PEEK({ i } );\n " )
89- for output in instr .outputs or ():
90- f .write (f"{ indent } PyObject *{ output } ;\n " )
91- # input = ", ".join(instr.inputs)
92- # output = ", ".join(instr.outputs)
93- # f.write(f"{indent} // {input} -- {output}\n")
94- assert instr .block is not None
95- blocklines = instr .block .text .splitlines (True )
96- # Remove blank lines from ends
97- while blocklines and not blocklines [0 ].strip ():
98- blocklines .pop (0 )
99- while blocklines and not blocklines [- 1 ].strip ():
100- blocklines .pop ()
101- # Remove leading '{' and trailing '}'
102- assert blocklines and blocklines [0 ].strip () == "{"
103- assert blocklines and blocklines [- 1 ].strip () == "}"
104- blocklines .pop ()
105- blocklines .pop (0 )
106- # Remove trailing blank lines
107- while blocklines and not blocklines [- 1 ].strip ():
108- blocklines .pop ()
109- # Write the body
110- ninputs = len (instr .inputs or ())
111- for line in blocklines :
112- if m := re .match (r"(\s*)ERROR_IF\(([^,]+), (\w+)\);\s*$" , line ):
113- space , cond , label = m .groups ()
114- # ERROR_IF() must remove the inputs from the stack.
115- # The code block is responsible for DECREF()ing them.
116- if ninputs :
117- f .write (f"{ space } if ({ cond } ) {{ STACK_SHRINK({ ninputs } ); goto { label } ; }}\n " )
118- else :
119- f .write (f"{ space } if ({ cond } ) {{ goto { label } ; }}\n " )
120- else :
121- f .write (line )
122- noutputs = len (instr .outputs or ())
123- diff = noutputs - ninputs
124- if diff > 0 :
125- f .write (f"{ indent } STACK_GROW({ diff } );\n " )
126- elif diff < 0 :
127- f .write (f"{ indent } STACK_SHRINK({ - diff } );\n " )
128- for i , output in enumerate (reversed (instr .outputs or ()), 1 ):
129- f .write (f"{ indent } POKE({ i } , { output } );\n " )
136+ write_instr (instr , predictions , indent , f )
130137 assert instr .block
131138 if not always_exits (instr .block ):
132139 f .write (f"{ indent } DISPATCH();\n " )
@@ -138,14 +145,13 @@ def write_cases(f: io.TextIOBase, instrs: list[InstDef], supers: list[parser.Sup
138145 components = [instr_index [name ] for name in sup .ops ]
139146 f .write (f"\n { indent } TARGET({ sup .name } ) {{\n " )
140147 for i , instr in enumerate (components ):
148+ assert instr .block
141149 if i > 0 :
142150 f .write (f"{ indent } NEXTOPARG();\n " )
143151 f .write (f"{ indent } next_instr++;\n " )
144- text = instr .block .to_text (- 4 )
145- textlines = text .splitlines (True )
146- textlines = [line for line in textlines if not line .strip ().startswith ("PREDICTED(" )]
147- text = "" .join (textlines )
148- f .write (f"{ indent } { text .strip ()} \n " )
152+ f .write (f"{ indent } {{\n " )
153+ write_instr (instr , predictions , indent , f , dedent = - 4 )
154+ f .write (f" { indent } }}\n " )
149155 f .write (f"{ indent } DISPATCH();\n " )
150156 f .write (f"{ indent } }}\n " )
151157
0 commit comments