13
13
import typing
14
14
15
15
import parser
16
+ from parser import StackEffect
16
17
17
18
DEFAULT_INPUT = os .path .relpath (
18
19
os .path .join (os .path .dirname (__file__ ), "../../Python/bytecodes.c" )
@@ -73,6 +74,34 @@ def block(self, head: str):
73
74
yield
74
75
self .emit ("}" )
75
76
77
+ def stack_adjust (self , diff : int ):
78
+ if diff > 0 :
79
+ self .emit (f"STACK_GROW({ diff } );" )
80
+ elif diff < 0 :
81
+ self .emit (f"STACK_SHRINK({ - diff } );" )
82
+
83
+ def declare (self , dst : StackEffect , src : StackEffect | None ):
84
+ if dst .name == UNUSED :
85
+ return
86
+ type = f"{ dst .type } " if dst .type else "PyObject *"
87
+ init = ""
88
+ if src :
89
+ cast = self .cast (dst , src )
90
+ init = f" = { cast } { src .name } "
91
+ self .emit (f"{ type } { dst .name } { init } ;" )
92
+
93
+ def assign (self , dst : StackEffect , src : StackEffect ):
94
+ if src .name == UNUSED :
95
+ return
96
+ cast = self .cast (dst , src )
97
+ if m := re .match (r"^PEEK\((\d+)\)$" , dst .name ):
98
+ self .emit (f"POKE({ m .group (1 )} , { cast } { src .name } );" )
99
+ else :
100
+ self .emit (f"{ dst .name } = { cast } { src .name } ;" )
101
+
102
+ def cast (self , dst : StackEffect , src : StackEffect ) -> str :
103
+ return f"({ dst .type or 'PyObject *' } )" if src .type != dst .type else ""
104
+
76
105
77
106
@dataclasses .dataclass
78
107
class Instruction :
@@ -88,8 +117,8 @@ class Instruction:
88
117
always_exits : bool
89
118
cache_offset : int
90
119
cache_effects : list [parser .CacheEffect ]
91
- input_effects : list [parser . StackEffect ]
92
- output_effects : list [parser . StackEffect ]
120
+ input_effects : list [StackEffect ]
121
+ output_effects : list [StackEffect ]
93
122
94
123
# Set later
95
124
family : parser .Family | None = None
@@ -106,7 +135,7 @@ def __init__(self, inst: parser.InstDef):
106
135
]
107
136
self .cache_offset = sum (c .size for c in self .cache_effects )
108
137
self .input_effects = [
109
- effect for effect in inst .inputs if isinstance (effect , parser . StackEffect )
138
+ effect for effect in inst .inputs if isinstance (effect , StackEffect )
110
139
]
111
140
self .output_effects = inst .outputs # For consistency/completeness
112
141
@@ -122,16 +151,15 @@ def write(self, out: Formatter) -> None:
122
151
)
123
152
124
153
# Write input stack effect variable declarations and initializations
125
- for i , seffect in enumerate (reversed (self .input_effects ), 1 ):
126
- if seffect . name != UNUSED :
127
- out .emit ( f"PyObject * { seffect . name } = PEEK( { i } );" )
154
+ for i , ieffect in enumerate (reversed (self .input_effects ), 1 ):
155
+ src = StackEffect ( f"PEEK( { i } )" , "" )
156
+ out .declare ( ieffect , src )
128
157
129
158
# Write output stack effect variable declarations
130
- input_names = {seffect .name for seffect in self .input_effects }
131
- input_names .add (UNUSED )
132
- for seffect in self .output_effects :
133
- if seffect .name not in input_names :
134
- out .emit (f"PyObject *{ seffect .name } ;" )
159
+ input_names = {ieffect .name for ieffect in self .input_effects }
160
+ for oeffect in self .output_effects :
161
+ if oeffect .name not in input_names :
162
+ out .declare (oeffect , None )
135
163
136
164
self .write_body (out , 0 )
137
165
@@ -141,19 +169,17 @@ def write(self, out: Formatter) -> None:
141
169
142
170
# Write net stack growth/shrinkage
143
171
diff = len (self .output_effects ) - len (self .input_effects )
144
- if diff > 0 :
145
- out .emit (f"STACK_GROW({ diff } );" )
146
- elif diff < 0 :
147
- out .emit (f"STACK_SHRINK({ - diff } );" )
172
+ out .stack_adjust (diff )
148
173
149
174
# Write output stack effect assignments
150
- unmoved_names = { UNUSED }
175
+ unmoved_names = set ()
151
176
for ieffect , oeffect in zip (self .input_effects , self .output_effects ):
152
177
if ieffect .name == oeffect .name :
153
178
unmoved_names .add (ieffect .name )
154
- for i , seffect in enumerate (reversed (self .output_effects )):
155
- if seffect .name not in unmoved_names :
156
- out .emit (f"POKE({ i + 1 } , { seffect .name } );" )
179
+ for i , oeffect in enumerate (reversed (self .output_effects ), 1 ):
180
+ if oeffect .name not in unmoved_names :
181
+ dst = StackEffect (f"PEEK({ i } )" , "" )
182
+ out .assign (dst , oeffect )
157
183
158
184
# Write cache effect
159
185
if self .cache_offset :
@@ -223,23 +249,26 @@ def write_body(self, out: Formatter, dedent: int, cache_adjust: int = 0) -> None
223
249
224
250
225
251
InstructionOrCacheEffect = Instruction | parser .CacheEffect
252
+ StackEffectMapping = list [tuple [StackEffect , StackEffect ]]
226
253
227
254
228
255
@dataclasses .dataclass
229
256
class Component :
230
257
instr : Instruction
231
- input_mapping : dict [ str , parser . StackEffect ]
232
- output_mapping : dict [ str , parser . StackEffect ]
258
+ input_mapping : StackEffectMapping
259
+ output_mapping : StackEffectMapping
233
260
234
261
def write_body (self , out : Formatter , cache_adjust : int ) -> None :
235
262
with out .block ("" ):
236
- for var , ieffect in self .input_mapping .items ():
237
- out .emit (f"PyObject *{ ieffect .name } = { var } ;" )
238
- for oeffect in self .output_mapping .values ():
239
- out .emit (f"PyObject *{ oeffect .name } ;" )
263
+ for var , ieffect in self .input_mapping :
264
+ out .declare (ieffect , var )
265
+ for _ , oeffect in self .output_mapping :
266
+ out .declare (oeffect , None )
267
+
240
268
self .instr .write_body (out , dedent = - 4 , cache_adjust = cache_adjust )
241
- for var , oeffect in self .output_mapping .items ():
242
- out .emit (f"{ var } = { oeffect .name } ;" )
269
+
270
+ for var , oeffect in self .output_mapping :
271
+ out .assign (var , oeffect )
243
272
244
273
245
274
# TODO: Use a common base class for {Super,Macro}Instruction
@@ -250,7 +279,7 @@ class SuperOrMacroInstruction:
250
279
"""Common fields for super- and macro instructions."""
251
280
252
281
name : str
253
- stack : list [str ]
282
+ stack : list [StackEffect ]
254
283
initial_sp : int
255
284
final_sp : int
256
285
@@ -445,15 +474,13 @@ def analyze_super(self, super: parser.Super) -> SuperInstruction:
445
474
case parser .CacheEffect () as ceffect :
446
475
parts .append (ceffect )
447
476
case Instruction () as instr :
448
- input_mapping = {}
477
+ input_mapping : StackEffectMapping = []
449
478
for ieffect in reversed (instr .input_effects ):
450
479
sp -= 1
451
- if ieffect .name != UNUSED :
452
- input_mapping [stack [sp ]] = ieffect
453
- output_mapping = {}
480
+ input_mapping .append ((stack [sp ], ieffect ))
481
+ output_mapping : StackEffectMapping = []
454
482
for oeffect in instr .output_effects :
455
- if oeffect .name != UNUSED :
456
- output_mapping [stack [sp ]] = oeffect
483
+ output_mapping .append ((stack [sp ], oeffect ))
457
484
sp += 1
458
485
parts .append (Component (instr , input_mapping , output_mapping ))
459
486
case _:
@@ -471,15 +498,13 @@ def analyze_macro(self, macro: parser.Macro) -> MacroInstruction:
471
498
case parser .CacheEffect () as ceffect :
472
499
parts .append (ceffect )
473
500
case Instruction () as instr :
474
- input_mapping = {}
501
+ input_mapping : StackEffectMapping = []
475
502
for ieffect in reversed (instr .input_effects ):
476
503
sp -= 1
477
- if ieffect .name != UNUSED :
478
- input_mapping [stack [sp ]] = ieffect
479
- output_mapping = {}
504
+ input_mapping .append ((stack [sp ], ieffect ))
505
+ output_mapping : StackEffectMapping = []
480
506
for oeffect in instr .output_effects :
481
- if oeffect .name != UNUSED :
482
- output_mapping [stack [sp ]] = oeffect
507
+ output_mapping .append ((stack [sp ], oeffect ))
483
508
sp += 1
484
509
parts .append (Component (instr , input_mapping , output_mapping ))
485
510
case _:
@@ -514,7 +539,7 @@ def check_macro_components(
514
539
515
540
def stack_analysis (
516
541
self , components : typing .Iterable [InstructionOrCacheEffect ]
517
- ) -> tuple [list [str ], int ]:
542
+ ) -> tuple [list [StackEffect ], int ]:
518
543
"""Analyze a super-instruction or macro.
519
544
520
545
Print an error if there's a cache effect (which we don't support yet).
@@ -536,7 +561,8 @@ def stack_analysis(
536
561
# At this point, 'current' is the net stack effect,
537
562
# and 'lowest' and 'highest' are the extremes.
538
563
# Note that 'lowest' may be negative.
539
- stack = [f"_tmp_{ i + 1 } " for i in range (highest - lowest )]
564
+ # TODO: Reverse the numbering.
565
+ stack = [StackEffect (f"_tmp_{ i + 1 } " , "" ) for i in range (highest - lowest )]
540
566
return stack , - lowest
541
567
542
568
def write_instructions (self ) -> None :
@@ -616,19 +642,17 @@ def wrap_super_or_macro(self, up: SuperOrMacroInstruction):
616
642
self .out .emit ("" )
617
643
with self .out .block (f"TARGET({ up .name } )" ):
618
644
for i , var in enumerate (up .stack ):
645
+ src = None
619
646
if i < up .initial_sp :
620
- self .out .emit (f"PyObject *{ var } = PEEK({ up .initial_sp - i } );" )
621
- else :
622
- self .out .emit (f"PyObject *{ var } ;" )
647
+ src = StackEffect (f"PEEK({ up .initial_sp - i } )" , "" )
648
+ self .out .declare (var , src )
623
649
624
650
yield
625
651
626
- if up .final_sp > up .initial_sp :
627
- self .out .emit (f"STACK_GROW({ up .final_sp - up .initial_sp } );" )
628
- elif up .final_sp < up .initial_sp :
629
- self .out .emit (f"STACK_SHRINK({ up .initial_sp - up .final_sp } );" )
652
+ self .out .stack_adjust (up .final_sp - up .initial_sp )
630
653
for i , var in enumerate (reversed (up .stack [: up .final_sp ]), 1 ):
631
- self .out .emit (f"POKE({ i } , { var } );" )
654
+ dst = StackEffect (f"PEEK({ i } )" , "" )
655
+ self .out .assign (dst , var )
632
656
633
657
self .out .emit (f"DISPATCH();" )
634
658
0 commit comments