Skip to content

Commit 6d18932

Browse files
committed
Do the super/macro analysis at analysis time
1 parent e416d19 commit 6d18932

File tree

1 file changed

+95
-61
lines changed

1 file changed

+95
-61
lines changed

Tools/cases_generator/generate_cases.py

Lines changed: 95 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import argparse
88
import contextlib
9+
import dataclasses
910
import os
1011
import re
1112
import sys
@@ -163,6 +164,71 @@ def write_body(self, f: typing.TextIO, ndent: str, dedent: int) -> None:
163164
f.write(line)
164165

165166

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+
166232
class Analyzer:
167233
"""Parse input, analyze it, and write to output."""
168234

@@ -178,6 +244,7 @@ def __init__(self, filename: str):
178244

179245
instrs: dict[str, Instruction] # Includes ops
180246
supers: dict[str, parser.Super] # Includes macros
247+
super_instrs: dict[str, SuperInstruction]
181248
families: dict[str, parser.Family]
182249

183250
def parse(self) -> None:
@@ -223,6 +290,7 @@ def analyze(self) -> None:
223290
self.find_predictions()
224291
self.map_families()
225292
self.check_families()
293+
self.analyze_supers()
226294

227295
def find_predictions(self) -> None:
228296
"""Find the instructions that need PREDICTED() labels."""
@@ -291,6 +359,14 @@ def check_families(self) -> None:
291359
)
292360
self.errors += 1
293361

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+
294370
def write_instructions(self, filename: str) -> None:
295371
"""Write instructions to output file."""
296372
indent = " " * 8
@@ -317,7 +393,7 @@ def write_instructions(self, filename: str) -> None:
317393
# Write super-instructions and macros
318394
n_supers = 0
319395
n_macros = 0
320-
for sup in self.supers.values():
396+
for sup in self.super_instrs.values():
321397
if sup.kind == "super":
322398
n_supers += 1
323399
elif sup.kind == "macro":
@@ -331,7 +407,7 @@ def write_instructions(self, filename: str) -> None:
331407
)
332408

333409
def write_super_macro(
334-
self, f: typing.TextIO, sup: parser.Super, indent: str = ""
410+
self, f: typing.TextIO, sup: SuperInstruction, indent: str = ""
335411
) -> None:
336412

337413
# TODO: Make write() and block() methods of some Formatter class
@@ -355,76 +431,34 @@ def block(head: str):
355431

356432
write("")
357433
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});")
365437
else:
366438
write(f"PyObject *{var};")
367439

368-
for i, instr in enumerate(components):
440+
for i, comp in enumerate(sup.parts):
369441
if i > 0 and sup.kind == "super":
370442
write(f"NEXTOPARG();")
371443
write(f"next_instr++;")
372444

373445
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):
397459
write(f"POKE({i}, {var});")
398460
write(f"DISPATCH();")
399461

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-
428462

429463
def always_exits(block: parser.Block) -> bool:
430464
"""Determine whether a block always ends in a return/goto/etc."""

0 commit comments

Comments
 (0)