11from copy import deepcopy
22
33from kirin import types as kirin_types , interp
4+ from kirin .ir import PyAttr
45from kirin .dialects import py , scf , ilist
56
67from bloqade import qubit , annotate
910from .lattice import (
1011 AnyRecord ,
1112 NotRecord ,
12- RecordIdx ,
1313 RecordTuple ,
1414 InvalidRecord ,
15+ ConstantCarrier ,
1516 ImmutableRecords ,
1617)
1718from .analysis import RecordFrame , RecordAnalysis
@@ -57,10 +58,7 @@ def measure_qubit_list(
5758 if not isinstance (num_qubits , kirin_types .Literal ):
5859 return (AnyRecord (),)
5960
60- record_idxs = []
61- for _ in range (num_qubits .data ):
62- record_idx = frame .global_record_state .increment_record_idx ()
63- record_idxs .append (record_idx )
61+ record_idxs = frame .global_record_state .add_record_idxs (num_qubits .data )
6462
6563 return (RecordTuple (members = tuple (record_idxs )),)
6664
@@ -70,9 +68,22 @@ class PyIndexing(interp.MethodTable):
7068 @interp .impl (py .GetItem )
7169 def getitem (self , interp : RecordAnalysis , frame : RecordFrame , stmt : py .GetItem ):
7270
73- idx_or_slice = interp .get_const_value ((int , slice ), stmt .index )
74- if idx_or_slice is None :
75- return (InvalidRecord (),)
71+ # maybe_const will work fine outside of any loops because
72+ # constprop will put the expected data into a hint.
73+
74+ # if maybeconst fails, we fall back to getting the value from the frame
75+ # (note that even outside loops, the constant impl will happily
76+ # capture integer/slice constants so if THAT fails, then something
77+ # has truly gone wrong).
78+ possible_idx_or_slice = interp .maybe_const (stmt .index , (int , slice ))
79+ if possible_idx_or_slice is not None :
80+ idx_or_slice = possible_idx_or_slice
81+ else :
82+ idx_or_slice = frame .get (stmt .index )
83+ if not isinstance (idx_or_slice , ConstantCarrier ):
84+ return (InvalidRecord (),)
85+ else :
86+ idx_or_slice = idx_or_slice .value
7687
7788 obj = frame .get (stmt .obj )
7889 if isinstance (obj , RecordTuple ):
@@ -106,36 +117,68 @@ class PyAlias(interp.MethodTable):
106117 @interp .impl (py .Alias )
107118 def alias (
108119 self ,
109- interp : RecordAnalysis ,
120+ interp_ : RecordAnalysis ,
110121 frame : RecordFrame ,
111122 stmt : py .Alias ,
112123 ):
113- value = frame .get (stmt .value )
114- if isinstance (value , RecordIdx ):
115- frame .global_record_state .drop_record_idx (value )
116- elif isinstance (value , RecordTuple ):
117- for member in value .members :
118- frame .global_record_state .drop_record_idx (member )
124+ input = frame .get (stmt .value ) # expect this to be a RecordTuple
119125
120- return (value ,)
126+ # two variables share the same references in the global state
127+ return (input ,)
121128
122129
123130@scf .dialect .register (key = "record" )
124- class LoopHandling (scf . absint . Methods ):
131+ class LoopHandling (interp . MethodTable ):
125132 @interp .impl (scf .stmts .For )
126133 def for_loop (
127134 self , interp_ : RecordAnalysis , frame : RecordFrame , stmt : scf .stmts .For
128135 ):
129136
130- # this will contain the in-loop measure variable declared outside the loop
131137 loop_vars = frame .get_values (stmt .initializers )
132- # NotRecord in the beginning just lets the sink have some value
133- loop_vars = interp_ .run_ssacfg_region (frame , stmt .body , loop_vars )
134138
135- # need to update the information in the frame
136- if isinstance (loop_vars , interp .ReturnValue ):
137- return loop_vars
138- elif loop_vars is None :
139- loop_vars = ()
139+ for _ in range (2 ):
140+ loop_vars = interp_ .frame_call_region (
141+ frame , stmt , stmt .body , InvalidRecord (), * loop_vars
142+ )
143+
144+ if loop_vars is None :
145+ loop_vars = ()
146+
147+ elif isinstance (loop_vars , interp .ReturnValue ):
148+ return loop_vars
140149
141150 return loop_vars
151+
152+ @interp .impl (scf .stmts .Yield )
153+ def for_yield (
154+ self , interp_ : RecordAnalysis , frame : RecordFrame , stmt : scf .stmts .Yield
155+ ):
156+ return interp .YieldValue (frame .get_values (stmt .values ))
157+
158+
159+ # Only carry about carrying integers for now because
160+ # the current issue is that
161+ @py .dialect .register (key = "record" )
162+ class ConstantForwarding (interp .MethodTable ):
163+ @interp .impl (py .Constant )
164+ def constant (
165+ self ,
166+ interp_ : RecordAnalysis ,
167+ frame : RecordFrame ,
168+ stmt : py .Constant ,
169+ ):
170+ # can't use interp_.maybe_const/expect_const because it assumes the data is already
171+ # there to begin with...
172+ if not isinstance (stmt .value , PyAttr ):
173+ return (InvalidRecord (),)
174+
175+ expected_int_or_slice = stmt .value .data
176+
177+ if not isinstance (expected_int_or_slice , (int , slice )):
178+ return (InvalidRecord (),)
179+
180+ return (ConstantCarrier (value = expected_int_or_slice ),)
181+
182+
183+ # outside_frame -> create new frame with context manager COPIED from outside frame
184+ # the frame and the stack are separate
0 commit comments