icode: duplicate ICodeN::closure_defs in dup_with
[nit.git] / src / icode / icode_tools.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2009 Jean Privat <jean@pryen.org>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # Tools to manipulate intermediace nit code representation
18 import icode_builder
19
20 # A simple visitor to visit icode structures
21 class ICodeVisitor
22 # Called when a iregister is read in a icode
23 fun visit_iregister_read(ic: ICode, r: IRegister) do end
24
25 # Called when a iregister is wrote in a icode
26 fun visit_iregister_write(ic: ICode, r: IRegister) do end
27
28 # The current icode iterator.
29 # Can be used to insert_before, used to change the item or deleted
30 readable var _current_icode: nullable ListIterator[ICode] = null
31
32 # Called when a icode is visited
33 # Automatically visits iregisters and sub-icodes
34 fun visit_icode(ic: nullable ICode)
35 do
36 if ic == null then return
37 if ic isa ISeq then
38 var old_icode = _current_icode
39 var cur = ic.icodes.iterator
40 while cur.is_ok do
41 _current_icode = cur
42 var ic2 = cur.item
43 visit_icode(ic2)
44 cur.next
45 end
46 _current_icode = old_icode
47 else if ic isa IIf then
48 visit_iregister_read(ic, ic.expr)
49 visit_icode(ic.then_seq)
50 visit_icode(ic.else_seq)
51 else if ic isa IOnce then
52 visit_icode(ic.body)
53 else if ic isa ICode1 then
54 visit_iregister_read(ic, ic.expr)
55 else if ic isa ICode2 then
56 visit_iregister_read(ic, ic.expr1)
57 visit_iregister_read(ic, ic.expr2)
58 else if ic isa ICodeN then
59 for e in ic.exprs do
60 visit_iregister_read(ic, e)
61 end
62 var closdefs = ic.closure_defs
63 if ic isa IClosCall then
64 visit_icode(ic.break_seq)
65 end
66 if closdefs != null then
67 visit_closure_defs(closdefs)
68 end
69 end
70 var r = ic.result
71 if r != null then visit_iregister_write(ic, r)
72 end
73
74 # Called when closure definitions are visited
75 # Automatically visits each closure definition
76 fun visit_closure_defs(closdefs: Collection[nullable IClosureDef])
77 do
78 for e in closdefs do
79 if e != null then
80 visit_iroutine(e)
81 end
82 end
83 end
84
85 # Called when an iroutine is visited
86 # Automatically visits the body
87 # Warning: parameters of result registers are not visited
88 fun visit_iroutine(ir: IRoutine)
89 do
90 visit_icode(ir.body)
91 end
92 end
93
94 redef class ICodeBuilder
95 # Inline an iroutine in the current icode sequence
96 fun inline_routine(routine: IRoutine, args: Sequence[IRegister]): nullable IRegister
97 do
98 var d = new ICodeDupContext(self)
99 assert args.length == routine.params.length
100
101 # Fill register duplicate association
102 var dico = d._registers
103 var res = routine.result
104 if res != null then
105 var res2 = new_register(res.stype)
106 dico[res] = res2
107 res = res2
108 end
109 for reg in routine.registers do
110 assert not dico.has_key(reg)
111 dico[reg] = new_register(reg.stype)
112 end
113 for i in [0..args.length[ do
114 # FIXME The following assumes that params are readonly.
115 # The alternative is safe but add one move :/
116 dico[routine.params[i]] = args[i]
117 #seq.icodes.add(new IMove(dico[routine.params[i]]), args[i]))
118 end
119
120 # Process inlining
121 routine.body.dup_with(d)
122 return res
123 end
124 end
125
126 # This class stores reference to allow correct duplication of icodes
127 private class ICodeDupContext
128 # Return the correct register
129 # * a duplicate of the local register 'r' of the inlined iroutine
130 # * 'r' else (it is a register of the caller iroutine)
131 fun dup_ireg(r: IRegister): IRegister
132 do
133 var rs = _registers
134 if rs.has_key(r) then
135 return rs[r]
136 else
137 return r
138 end
139 end
140
141 # Return a correct bunch of registers
142 fun dup_iregs(regs: Sequence[IRegister]): Sequence[IRegister]
143 do
144 var a = new Array[IRegister].with_capacity(regs.length)
145 for r in regs do
146 a.add(dup_ireg(r))
147 end
148 return a
149 end
150
151 # The associoation between old_seq and new_seq
152 # Directly used by the IEscape
153 var _seqs: Map[ISeq, ISeq] = new HashMap[ISeq, ISeq]
154
155 # The assocation between old_ireg and new_ireg
156 # Used by dup_ireg
157 var _registers: Map[IRegister, IRegister] = new HashMap[IRegister, IRegister]
158
159 # The current code builder
160 var _icb: ICodeBuilder
161
162 init(icb: ICodeBuilder)
163 do
164 _icb = icb
165 end
166 end
167
168 redef class ICode
169 # Duplicate the current icode in the icode builder of the ICodeDupContext
170 private fun dup_with(d: ICodeDupContext)
171 do
172 var c = inner_dup_with(d)
173 if self isa ICodeN then
174 assert c isa ICodeN
175 c.closure_defs = closure_defs
176 end
177 var r = result
178 if r != null then c.result = d.dup_ireg(r)
179 c.location = location
180 d._icb.seq.icodes.add(c)
181 end
182
183 # Simle partial duplication of the current icode
184 private fun inner_dup_with(d: ICodeDupContext): ICode is abstract
185 end
186
187 redef class ISeq
188 redef fun inner_dup_with(d)
189 do
190 var c2 = new ISeq
191 dup_seq_to(d, c2)
192 return c2
193 end
194
195 # Duplicate each icode and store them in dest
196 # Note: dest must be empty and not modified afted duplication or IEscapes may be wrongly duplicated
197 private fun dup_seq_to(d: ICodeDupContext, dest: ISeq)
198 do
199 var old_seq = d._icb.seq
200 d._icb.seq = dest
201 d._seqs[self] = dest
202 for c in icodes do
203 c.dup_with(d)
204 end
205 d._icb.seq = old_seq
206 end
207 end
208
209 redef class ILoop
210 redef fun inner_dup_with(d)
211 do
212 var c2 = new ILoop
213 dup_seq_to(d, c2)
214 return c2
215 end
216 end
217
218 redef class IIf
219 redef fun inner_dup_with(d)
220 do
221 var c2 = new IIf(d.dup_ireg(expr))
222 then_seq.dup_seq_to(d, c2.then_seq)
223 else_seq.dup_seq_to(d, c2.else_seq)
224 return c2
225 end
226 end
227
228 redef class IEscape
229 redef fun inner_dup_with(d)
230 do
231 if d._seqs.has_key(seq) then
232 # Jump to a duplicated sequence
233 return new IEscape(d._seqs[seq])
234 else
235 # Jump to an englobing unduplicated sequence
236 return new IEscape(seq)
237 end
238 end
239 end
240
241 redef class IAbort
242 redef fun inner_dup_with(d)
243 do
244 return new IAbort(texts, module_location)
245 end
246 end
247
248 redef class ICall
249 redef fun inner_dup_with(d)
250 do
251 return new ICall(property, d.dup_iregs(exprs))
252 end
253 end
254
255 redef class ISuper
256 redef fun inner_dup_with(d)
257 do
258 return new ISuper(property, d.dup_iregs(exprs))
259 end
260 end
261
262 redef class INew
263 redef fun inner_dup_with(d)
264 do
265 return new INew(stype, property, d.dup_iregs(exprs))
266 end
267 end
268
269 redef class IClosCall
270 redef fun inner_dup_with(d)
271 do
272 var c2 = new IClosCall(closure_decl, d.dup_iregs(exprs))
273 var bs = break_seq
274 if bs != null then
275 var bs2 = new ISeq
276 c2.break_seq = bs2
277 bs.dup_seq_to(d, bs2)
278 end
279 return c2
280 end
281 end
282
283 redef class INative
284 redef fun inner_dup_with(d)
285 do
286 var c2 = new INative(code, d.dup_iregs(exprs))
287 c2.is_pure = is_pure
288 return c2
289 end
290 end
291
292 redef class IMove
293 redef fun inner_dup_with(d)
294 do
295 return new IMove(d.dup_ireg(result.as(not null)), d.dup_ireg(expr))
296 end
297 end
298
299 redef class IAttrRead
300 redef fun inner_dup_with(d)
301 do
302 return new IAttrRead(property, d.dup_ireg(expr))
303 end
304 end
305
306 redef class IAttrWrite
307 redef fun inner_dup_with(d)
308 do
309 return new IAttrWrite(property, d.dup_ireg(expr1), d.dup_ireg(expr2))
310 end
311 end
312
313 redef class IAttrIsset
314 redef fun inner_dup_with(d)
315 do
316 return new IAttrIsset(property, d.dup_ireg(expr))
317 end
318 end
319
320 redef class ITypeCheck
321 redef fun inner_dup_with(d)
322 do
323 return new ITypeCheck(d.dup_ireg(expr), stype)
324 end
325 end
326
327 redef class IIs
328 redef fun inner_dup_with(d)
329 do
330 return new IIs(d.dup_ireg(expr1), d.dup_ireg(expr2))
331 end
332 end
333
334 redef class INot
335 redef fun inner_dup_with(d)
336 do
337 return new INot(d.dup_ireg(expr))
338 end
339 end
340
341 redef class IOnce
342 redef fun inner_dup_with(d)
343 do
344 var c2 = new IOnce
345 body.dup_seq_to(d, c2.body)
346 return c2
347 end
348 end
349
350 redef class IHasClos
351 redef fun inner_dup_with(d)
352 do
353 return new IHasClos(closure_decl)
354 end
355 end