6
6
7
7
import argparse
8
8
import contextlib
9
+ import dataclasses
9
10
import os
10
11
import re
11
12
import sys
@@ -163,6 +164,71 @@ def write_body(self, f: typing.TextIO, ndent: str, dedent: int) -> None:
163
164
f .write (line )
164
165
165
166
167
+ @dataclasses .dataclass
168
+ class SuperComponent :
169
+ instr : Instruction
170
+ input_mapping : typing .Dict [str , parser .StackEffect ]
171
+ output_mapping : typing .Dict [str , parser .StackEffect ]
172
+
173
+
174
+ class SuperInstruction (parser .Super ):
175
+
176
+ stack : list [str ]
177
+ initial_sp : int
178
+ final_sp : int
179
+ parts : list [SuperComponent ]
180
+
181
+ def __init__ (self , sup : parser .Super ):
182
+ super ().__init__ (sup .kind , sup .name , sup .ops )
183
+ self .context = sup .context
184
+
185
+ def analyze (self , a : "Analyzer" ) -> None :
186
+ components = [a .instrs [name ] for name in self .ops ]
187
+ self .stack , self .initial_sp = self .super_macro_analysis (a , components )
188
+ sp = self .initial_sp
189
+ self .parts = []
190
+ for instr in components :
191
+ input_mapping = {}
192
+ for ieffect in reversed (instr .input_effects ):
193
+ sp -= 1
194
+ if ieffect .name != "unused" :
195
+ input_mapping [self .stack [sp ]] = ieffect
196
+ output_mapping = {}
197
+ for oeffect in instr .output_effects :
198
+ if oeffect .name != "unused" :
199
+ output_mapping [self .stack [sp ]] = oeffect
200
+ sp += 1
201
+ self .parts .append (SuperComponent (instr , input_mapping , output_mapping ))
202
+ self .final_sp = sp
203
+
204
+ def super_macro_analysis (
205
+ self , a : "Analyzer" , components : list [Instruction ]
206
+ ) -> tuple [list [str ], int ]:
207
+ """Analyze a super-instruction or macro.
208
+
209
+ Print an error if there's a cache effect (which we don't support yet).
210
+
211
+ Return the list of variable names and the initial stack pointer.
212
+ """
213
+ lowest = current = highest = 0
214
+ for instr in components :
215
+ if instr .cache_effects :
216
+ print (
217
+ f"Super-instruction { self .name !r} has cache effects in { instr .name !r} " ,
218
+ file = sys .stderr ,
219
+ )
220
+ a .errors += 1
221
+ current -= len (instr .input_effects )
222
+ lowest = min (lowest , current )
223
+ current += len (instr .output_effects )
224
+ highest = max (highest , current )
225
+ # At this point, 'current' is the net stack effect,
226
+ # and 'lowest' and 'highest' are the extremes.
227
+ # Note that 'lowest' may be negative.
228
+ stack = [f"_tmp_{ i + 1 } " for i in range (highest - lowest )]
229
+ return stack , - lowest
230
+
231
+
166
232
class Analyzer :
167
233
"""Parse input, analyze it, and write to output."""
168
234
@@ -178,6 +244,7 @@ def __init__(self, filename: str):
178
244
179
245
instrs : dict [str , Instruction ] # Includes ops
180
246
supers : dict [str , parser .Super ] # Includes macros
247
+ super_instrs : dict [str , SuperInstruction ]
181
248
families : dict [str , parser .Family ]
182
249
183
250
def parse (self ) -> None :
@@ -223,6 +290,7 @@ def analyze(self) -> None:
223
290
self .find_predictions ()
224
291
self .map_families ()
225
292
self .check_families ()
293
+ self .analyze_supers ()
226
294
227
295
def find_predictions (self ) -> None :
228
296
"""Find the instructions that need PREDICTED() labels."""
@@ -291,6 +359,14 @@ def check_families(self) -> None:
291
359
)
292
360
self .errors += 1
293
361
362
+ def analyze_supers (self ) -> None :
363
+ """Analyze each super instruction."""
364
+ self .super_instrs = {}
365
+ for name , sup in self .supers .items ():
366
+ dup = SuperInstruction (sup )
367
+ dup .analyze (self )
368
+ self .super_instrs [name ] = dup
369
+
294
370
def write_instructions (self , filename : str ) -> None :
295
371
"""Write instructions to output file."""
296
372
indent = " " * 8
@@ -317,7 +393,7 @@ def write_instructions(self, filename: str) -> None:
317
393
# Write super-instructions and macros
318
394
n_supers = 0
319
395
n_macros = 0
320
- for sup in self .supers .values ():
396
+ for sup in self .super_instrs .values ():
321
397
if sup .kind == "super" :
322
398
n_supers += 1
323
399
elif sup .kind == "macro" :
@@ -331,7 +407,7 @@ def write_instructions(self, filename: str) -> None:
331
407
)
332
408
333
409
def write_super_macro (
334
- self , f : typing .TextIO , sup : parser . Super , indent : str = ""
410
+ self , f : typing .TextIO , sup : SuperInstruction , indent : str = ""
335
411
) -> None :
336
412
337
413
# TODO: Make write() and block() methods of some Formatter class
@@ -355,76 +431,34 @@ def block(head: str):
355
431
356
432
write ("" )
357
433
with block (f"TARGET({ sup .name } )" ):
358
- components = [self .instrs [name ] for name in sup .ops ]
359
- stack , nbelow = self .super_macro_analysis (sup .name , components )
360
- sp = nbelow
361
-
362
- for i , var in enumerate (stack ):
363
- if i < sp :
364
- write (f"PyObject *{ var } = PEEK({ sp - i } );" )
434
+ for i , var in enumerate (sup .stack ):
435
+ if i < sup .initial_sp :
436
+ write (f"PyObject *{ var } = PEEK({ sup .initial_sp - i } );" )
365
437
else :
366
438
write (f"PyObject *{ var } ;" )
367
439
368
- for i , instr in enumerate (components ):
440
+ for i , comp in enumerate (sup . parts ):
369
441
if i > 0 and sup .kind == "super" :
370
442
write (f"NEXTOPARG();" )
371
443
write (f"next_instr++;" )
372
444
373
445
with block ("" ):
374
- instack = stack [sp - len (instr .input_effects ) : sp ]
375
- for var , ineffect in zip (instack , instr .input_effects ):
376
- if ineffect .name != "unused" :
377
- write (f"PyObject *{ ineffect .name } = { var } ;" )
378
- for outeffect in instr .output_effects :
379
- if outeffect .name != "unused" :
380
- write (f"PyObject *{ outeffect .name } ;" )
381
-
382
- instr .write_body (f , indent , dedent = - 4 )
383
-
384
- sp -= len (instack )
385
- nout = len (instr .output_effects )
386
- sp += nout
387
- outstack = stack [sp - nout : sp ]
388
- for var , outeffect in zip (outstack , instr .output_effects ):
389
- if outeffect .name != "unused" :
390
- write (f"{ var } = { outeffect .name } ;" )
391
-
392
- if sp > nbelow :
393
- write (f"STACK_GROW({ sp - nbelow } );" )
394
- elif sp < nbelow :
395
- write (f"STACK_SHRINK({ nbelow - sp } );" )
396
- for i , var in enumerate (reversed (stack [:sp ]), 1 ):
446
+ for var , ieffect in comp .input_mapping .items ():
447
+ write (f"PyObject *{ ieffect .name } = { var } ;" )
448
+ for oeffect in comp .output_mapping .values ():
449
+ write (f"PyObject *{ oeffect .name } ;" )
450
+ comp .instr .write_body (f , indent , dedent = - 4 )
451
+ for var , oeffect in comp .output_mapping .items ():
452
+ write (f"{ var } = { oeffect .name } ;" )
453
+
454
+ if sup .final_sp > sup .initial_sp :
455
+ write (f"STACK_GROW({ sup .final_sp - sup .initial_sp } );" )
456
+ elif sup .final_sp < sup .initial_sp :
457
+ write (f"STACK_SHRINK({ sup .initial_sp - sup .final_sp } );" )
458
+ for i , var in enumerate (reversed (sup .stack [:sup .final_sp ]), 1 ):
397
459
write (f"POKE({ i } , { var } );" )
398
460
write (f"DISPATCH();" )
399
461
400
- # TODO: Move this into analysis phase
401
- def super_macro_analysis (
402
- self , name : str , components : list [Instruction ]
403
- ) -> tuple [list [str ], int ]:
404
- """Analyze a super-instruction or macro.
405
-
406
- Print an error if there's a cache effect (which we don't support yet).
407
-
408
- Return the list of variable names and the initial stack pointer.
409
- """
410
- lowest = current = highest = 0
411
- for instr in components :
412
- if instr .cache_effects :
413
- print (
414
- f"Super-instruction { name !r} has cache effects in { instr .name !r} " ,
415
- file = sys .stderr ,
416
- )
417
- self .errors += 1
418
- current -= len (instr .input_effects )
419
- lowest = min (lowest , current )
420
- current += len (instr .output_effects )
421
- highest = max (highest , current )
422
- # At this point, 'current' is the net stack effect,
423
- # and 'lowest' and 'highest' are the extremes.
424
- # Note that 'lowest' may be negative.
425
- stack = [f"_tmp_{ i + 1 } " for i in range (highest - lowest )]
426
- return stack , - lowest
427
-
428
462
429
463
def always_exits (block : parser .Block ) -> bool :
430
464
"""Determine whether a block always ends in a return/goto/etc."""
0 commit comments