23
23
)
24
24
BEGIN_MARKER = "// BEGIN BYTECODES //"
25
25
END_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*$ "
27
27
UNUSED = "unused"
28
28
BITS_PER_CODE_UNIT = 16
29
29
@@ -112,6 +112,8 @@ class Instruction:
112
112
kind : typing .Literal ["inst" , "op" ]
113
113
name : str
114
114
block : parser .Block
115
+ block_text : list [str ] # Block.text, less curlies, less PREDICT() calls
116
+ predictions : list [str ] # Prediction targets (instruction names)
115
117
116
118
# Computed by constructor
117
119
always_exits : bool
@@ -129,7 +131,8 @@ def __init__(self, inst: parser.InstDef):
129
131
self .kind = inst .kind
130
132
self .name = inst .name
131
133
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 )
133
136
self .cache_effects = [
134
137
effect for effect in inst .inputs if isinstance (effect , parser .CacheEffect )
135
138
]
@@ -164,15 +167,15 @@ def write(self, out: Formatter) -> None:
164
167
self .write_body (out , 0 )
165
168
166
169
# Skip the rest if the block always exits
167
- if always_exits ( self .block ) :
170
+ if self .always_exits :
168
171
return
169
172
170
173
# Write net stack growth/shrinkage
171
174
diff = len (self .output_effects ) - len (self .input_effects )
172
175
out .stack_adjust (diff )
173
176
174
177
# Write output stack effect assignments
175
- unmoved_names = set ()
178
+ unmoved_names : set [ str ] = set ()
176
179
for ieffect , oeffect in zip (self .input_effects , self .output_effects ):
177
180
if ieffect .name == oeffect .name :
178
181
unmoved_names .add (ieffect .name )
@@ -206,27 +209,10 @@ def write_body(self, out: Formatter, dedent: int, cache_adjust: int = 0) -> None
206
209
cache_offset += ceffect .size
207
210
assert cache_offset == self .cache_offset + cache_adjust
208
211
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
-
228
212
# 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 :
230
216
if m := re .match (r"(\s*)ERROR_IF\((.+), (\w+)\);\s*$" , line ):
231
217
space , cond , label = m .groups ()
232
218
# 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
241
227
else :
242
228
break
243
229
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
+ )
245
233
else :
246
- out .write_raw (f"{ space } if ({ cond } ) goto { label } ;\n " )
234
+ out .write_raw (f"{ extra } { space } if ({ cond } ) goto { label } ;\n " )
247
235
else :
248
- out .write_raw (line )
236
+ out .write_raw (extra + line )
249
237
250
238
251
239
InstructionOrCacheEffect = Instruction | parser .CacheEffect
@@ -395,7 +383,11 @@ def analyze(self) -> None:
395
383
def find_predictions (self ) -> None :
396
384
"""Find the instructions that need PREDICTED() labels."""
397
385
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 :
399
391
if target_instr := self .instrs .get (target ):
400
392
target_instr .predicted = True
401
393
else :
@@ -552,7 +544,9 @@ def stack_analysis(
552
544
# and 'lowest' and 'highest' are the extremes.
553
545
# Note that 'lowest' may be negative.
554
546
# 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
+ ]
556
550
return stack , - lowest
557
551
558
552
def write_instructions (self ) -> None :
@@ -577,7 +571,9 @@ def write_instructions(self) -> None:
577
571
if instr .predicted :
578
572
self .out .emit (f"PREDICTED({ name } );" )
579
573
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 } );" )
581
577
self .out .emit (f"DISPATCH();" )
582
578
583
579
# Write and count super-instructions
@@ -652,18 +648,40 @@ def wrap_super_or_macro(self, up: SuperOrMacroInstruction):
652
648
self .out .emit (f"DISPATCH();" )
653
649
654
650
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 :
656
681
"""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 ()
664
682
if not lines :
665
683
return False
666
- line = lines . pop () .rstrip ()
684
+ line = lines [ - 1 ] .rstrip ()
667
685
# Indent must match exactly (TODO: Do something better)
668
686
if line [:12 ] != " " * 12 :
669
687
return False
0 commit comments