2323)
2424BEGIN_MARKER = "// BEGIN BYTECODES //"
2525END_MARKER = "// END BYTECODES //"
26- RE_PREDICTED = r"(?s) (?:PREDICT\(|GO_TO_INSTRUCTION\(|DEOPT_IF\(.*?,\s*)(\w+)\);"
26+ RE_PREDICTED = r"^\s* (?:PREDICT\(|GO_TO_INSTRUCTION\(|DEOPT_IF\(.*?,\s*)(\w+)\);\s*$ "
2727UNUSED = "unused"
2828BITS_PER_CODE_UNIT = 16
2929
@@ -112,6 +112,8 @@ class Instruction:
112112 kind : typing .Literal ["inst" , "op" ]
113113 name : str
114114 block : parser .Block
115+ block_text : list [str ] # Block.text, less curlies, less PREDICT() calls
116+ predictions : list [str ] # Prediction targets (instruction names)
115117
116118 # Computed by constructor
117119 always_exits : bool
@@ -129,7 +131,8 @@ def __init__(self, inst: parser.InstDef):
129131 self .kind = inst .kind
130132 self .name = inst .name
131133 self .block = inst .block
132- self .always_exits = always_exits (self .block )
134+ self .block_text , self .predictions = extract_block_text (self .block )
135+ self .always_exits = always_exits (self .block_text )
133136 self .cache_effects = [
134137 effect for effect in inst .inputs if isinstance (effect , parser .CacheEffect )
135138 ]
@@ -164,15 +167,15 @@ def write(self, out: Formatter) -> None:
164167 self .write_body (out , 0 )
165168
166169 # Skip the rest if the block always exits
167- if always_exits ( self .block ) :
170+ if self .always_exits :
168171 return
169172
170173 # Write net stack growth/shrinkage
171174 diff = len (self .output_effects ) - len (self .input_effects )
172175 out .stack_adjust (diff )
173176
174177 # Write output stack effect assignments
175- unmoved_names = set ()
178+ unmoved_names : set [ str ] = set ()
176179 for ieffect , oeffect in zip (self .input_effects , self .output_effects ):
177180 if ieffect .name == oeffect .name :
178181 unmoved_names .add (ieffect .name )
@@ -206,27 +209,10 @@ def write_body(self, out: Formatter, dedent: int, cache_adjust: int = 0) -> None
206209 cache_offset += ceffect .size
207210 assert cache_offset == self .cache_offset + cache_adjust
208211
209- # Get lines of text with proper dedent
210- blocklines = self .block .to_text (dedent = dedent ).splitlines (True )
211-
212- # Remove blank lines from both ends
213- while blocklines and not blocklines [0 ].strip ():
214- blocklines .pop (0 )
215- while blocklines and not blocklines [- 1 ].strip ():
216- blocklines .pop ()
217-
218- # Remove leading and trailing braces
219- assert blocklines and blocklines [0 ].strip () == "{"
220- assert blocklines and blocklines [- 1 ].strip () == "}"
221- blocklines .pop ()
222- blocklines .pop (0 )
223-
224- # Remove trailing blank lines
225- while blocklines and not blocklines [- 1 ].strip ():
226- blocklines .pop ()
227-
228212 # Write the body, substituting a goto for ERROR_IF()
229- for line in blocklines :
213+ assert dedent <= 0
214+ extra = " " * - dedent
215+ for line in self .block_text :
230216 if m := re .match (r"(\s*)ERROR_IF\((.+), (\w+)\);\s*$" , line ):
231217 space , cond , label = m .groups ()
232218 # ERROR_IF() must pop the inputs from the stack.
@@ -241,11 +227,13 @@ def write_body(self, out: Formatter, dedent: int, cache_adjust: int = 0) -> None
241227 else :
242228 break
243229 if ninputs :
244- out .write_raw (f"{ space } if ({ cond } ) goto pop_{ ninputs } _{ label } ;\n " )
230+ out .write_raw (
231+ f"{ extra } { space } if ({ cond } ) goto pop_{ ninputs } _{ label } ;\n "
232+ )
245233 else :
246- out .write_raw (f"{ space } if ({ cond } ) goto { label } ;\n " )
234+ out .write_raw (f"{ extra } { space } if ({ cond } ) goto { label } ;\n " )
247235 else :
248- out .write_raw (line )
236+ out .write_raw (extra + line )
249237
250238
251239InstructionOrCacheEffect = Instruction | parser .CacheEffect
@@ -395,7 +383,11 @@ def analyze(self) -> None:
395383 def find_predictions (self ) -> None :
396384 """Find the instructions that need PREDICTED() labels."""
397385 for instr in self .instrs .values ():
398- for target in re .findall (RE_PREDICTED , instr .block .text ):
386+ targets = set (instr .predictions )
387+ for line in instr .block_text :
388+ if m := re .match (RE_PREDICTED , line ):
389+ targets .add (m .group (1 ))
390+ for target in targets :
399391 if target_instr := self .instrs .get (target ):
400392 target_instr .predicted = True
401393 else :
@@ -552,7 +544,9 @@ def stack_analysis(
552544 # and 'lowest' and 'highest' are the extremes.
553545 # Note that 'lowest' may be negative.
554546 # TODO: Reverse the numbering.
555- stack = [StackEffect (f"_tmp_{ i + 1 } " , "" ) for i in reversed (range (highest - lowest ))]
547+ stack = [
548+ StackEffect (f"_tmp_{ i + 1 } " , "" ) for i in reversed (range (highest - lowest ))
549+ ]
556550 return stack , - lowest
557551
558552 def write_instructions (self ) -> None :
@@ -577,7 +571,9 @@ def write_instructions(self) -> None:
577571 if instr .predicted :
578572 self .out .emit (f"PREDICTED({ name } );" )
579573 instr .write (self .out )
580- if not always_exits (instr .block ):
574+ if not instr .always_exits :
575+ for prediction in instr .predictions :
576+ self .out .emit (f"PREDICT({ prediction } );" )
581577 self .out .emit (f"DISPATCH();" )
582578
583579 # Write and count super-instructions
@@ -652,18 +648,40 @@ def wrap_super_or_macro(self, up: SuperOrMacroInstruction):
652648 self .out .emit (f"DISPATCH();" )
653649
654650
655- def always_exits (block : parser .Block ) -> bool :
651+ def extract_block_text (block : parser .Block ) -> tuple [list [str ], list [str ]]:
652+ # Get lines of text with proper dedent
653+ blocklines = block .text .splitlines (True )
654+
655+ # Remove blank lines from both ends
656+ while blocklines and not blocklines [0 ].strip ():
657+ blocklines .pop (0 )
658+ while blocklines and not blocklines [- 1 ].strip ():
659+ blocklines .pop ()
660+
661+ # Remove leading and trailing braces
662+ assert blocklines and blocklines [0 ].strip () == "{"
663+ assert blocklines and blocklines [- 1 ].strip () == "}"
664+ blocklines .pop ()
665+ blocklines .pop (0 )
666+
667+ # Remove trailing blank lines
668+ while blocklines and not blocklines [- 1 ].strip ():
669+ blocklines .pop ()
670+
671+ # Separate PREDICT(...) macros from end
672+ predictions : list [str ] = []
673+ while blocklines and (m := re .match (r"^\s*PREDICT\((\w+)\);\s*$" , blocklines [- 1 ])):
674+ predictions .insert (0 , m .group (1 ))
675+ blocklines .pop ()
676+
677+ return blocklines , predictions
678+
679+
680+ def always_exits (lines : list [str ]) -> bool :
656681 """Determine whether a block always ends in a return/goto/etc."""
657- text = block .text
658- lines = text .splitlines ()
659- while lines and not lines [- 1 ].strip ():
660- lines .pop ()
661- if not lines or lines [- 1 ].strip () != "}" :
662- return False
663- lines .pop ()
664682 if not lines :
665683 return False
666- line = lines . pop () .rstrip ()
684+ line = lines [ - 1 ] .rstrip ()
667685 # Indent must match exactly (TODO: Do something better)
668686 if line [:12 ] != " " * 12 :
669687 return False
0 commit comments