icode: do no do recursive inline
[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 # IRoutine currently inlining
96 # Used to avoid recursive inlining
97 var _current_inlining: Array[IRoutine] = new Array[IRoutine]
98
99 # Return false if routine can be saflely inlined
100 fun is_currently_inlining_routine(routine: IRoutine): Bool
101 do
102 return routine == iroutine or _current_inlining.has(routine)
103 end
104
105 # Inline an iroutine in the current icode sequence
106 # Require not is_currently_inlining
107 fun inline_routine(routine: IRoutine, args: Sequence[IRegister], closdefs: nullable Sequence[nullable IClosureDef]): nullable IRegister
108 do
109 assert not is_currently_inlining_routine(routine)
110 _current_inlining.add(routine)
111 var d = new ICodeDupContext(self)
112 assert args.length == routine.params.length
113 var closdecls = routine.closure_decls
114 var cdefsa = if closdefs != null then closdefs.length else 0
115 var cdeclsa = if closdecls != null then closdecls.length else 0
116 assert cdefsa <= cdeclsa
117
118 # Fill register duplicate association
119 var dico = d._registers
120 var res = routine.result
121 if res != null then
122 var res2 = new_register(res.stype)
123 dico[res] = res2
124 res = res2
125 end
126 for reg in routine.registers do
127 assert not dico.has_key(reg)
128 dico[reg] = new_register(reg.stype)
129 end
130 for i in [0..args.length[ do
131 # FIXME The following assumes that params are readonly.
132 # The alternative is safe but add one move :/
133 dico[routine.params[i]] = args[i]
134 #seq.icodes.add(new IMove(dico[routine.params[i]]), args[i]))
135 end
136
137 # Fill closure association
138 if closdecls != null then
139 var cdico = d._closures
140 for i in [0..cdefsa[ do
141 cdico[closdecls[i]] = closdefs[i]
142 end
143 for i in [cdefsa..cdeclsa[ do
144 cdico[closdecls[i]] = null
145 end
146 end
147
148 # Process inlining
149 routine.body.dup_with(d)
150 _current_inlining.pop
151 return res
152 end
153 end
154
155 # This class stores reference to allow correct duplication of icodes
156 private class ICodeDupContext
157 # Return the correct register
158 # * a duplicate of the local register 'r' of the inlined iroutine
159 # * 'r' else (it is a register of the caller iroutine)
160 fun dup_ireg(r: IRegister): IRegister
161 do
162 var rs = _registers
163 if rs.has_key(r) then
164 return rs[r]
165 else
166 return r
167 end
168 end
169
170 # Return a correct bunch of registers
171 fun dup_iregs(regs: Sequence[IRegister]): Sequence[IRegister]
172 do
173 var a = new Array[IRegister].with_capacity(regs.length)
174 for r in regs do
175 a.add(dup_ireg(r))
176 end
177 return a
178 end
179
180 # The associoation between old_seq and new_seq
181 # Directly used by the IEscape
182 var _seqs: Map[ISeq, ISeq] = new HashMap[ISeq, ISeq]
183
184 # The assocation between old_ireg and new_ireg
185 # Used by dup_ireg
186 var _registers: Map[IRegister, IRegister] = new HashMap[IRegister, IRegister]
187
188 # The association between a closure_decl and its closure_def (if any)
189 var _closures: Map[IClosureDecl, nullable IClosureDef] = new ArrayMap[IClosureDecl, nullable IClosureDef]
190
191 # The current code builder
192 var _icb: ICodeBuilder
193
194 init(icb: ICodeBuilder)
195 do
196 _icb = icb
197 end
198 end
199
200 redef class ICode
201 # Duplicate the current icode in the icode builder of the ICodeDupContext
202 private fun dup_with(d: ICodeDupContext)
203 do
204 var c = inner_dup_with(d)
205 if self isa ICodeN then
206 assert c isa ICodeN
207 c.closure_defs = closure_defs
208 end
209 var r = result
210 if r != null then c.result = d.dup_ireg(r)
211 c.location = location
212 d._icb.seq.icodes.add(c)
213 end
214
215 # Simle partial duplication of the current icode
216 private fun inner_dup_with(d: ICodeDupContext): ICode is abstract
217 end
218
219 redef class ISeq
220 redef fun inner_dup_with(d)
221 do
222 var c2 = new ISeq
223 dup_seq_to(d, c2)
224 return c2
225 end
226
227 # Duplicate each icode and store them in dest
228 # Note: dest must be empty and not modified afted duplication or IEscapes may be wrongly duplicated
229 private fun dup_seq_to(d: ICodeDupContext, dest: ISeq)
230 do
231 var old_seq = d._icb.seq
232 d._icb.seq = dest
233 d._seqs[self] = dest
234 for c in icodes do
235 c.dup_with(d)
236 end
237 d._icb.seq = old_seq
238 end
239 end
240
241 redef class ILoop
242 redef fun inner_dup_with(d)
243 do
244 var c2 = new ILoop
245 dup_seq_to(d, c2)
246 return c2
247 end
248 end
249
250 redef class IIf
251 redef fun inner_dup_with(d)
252 do
253 var c2 = new IIf(d.dup_ireg(expr))
254 then_seq.dup_seq_to(d, c2.then_seq)
255 else_seq.dup_seq_to(d, c2.else_seq)
256 return c2
257 end
258 end
259
260 redef class IEscape
261 redef fun inner_dup_with(d)
262 do
263 if d._seqs.has_key(seq) then
264 # Jump to a duplicated sequence
265 return new IEscape(d._seqs[seq])
266 else
267 # Jump to an englobing unduplicated sequence
268 return new IEscape(seq)
269 end
270 end
271 end
272
273 redef class IAbort
274 redef fun inner_dup_with(d)
275 do
276 return new IAbort(texts, module_location)
277 end
278 end
279
280 redef class ICall
281 redef fun inner_dup_with(d)
282 do
283 return new ICall(property, d.dup_iregs(exprs))
284 end
285 end
286
287 redef class ISuper
288 redef fun inner_dup_with(d)
289 do
290 return new ISuper(property, d.dup_iregs(exprs))
291 end
292 end
293
294 redef class INew
295 redef fun inner_dup_with(d)
296 do
297 return new INew(stype, property, d.dup_iregs(exprs))
298 end
299 end
300
301 redef class IClosCall
302 redef fun dup_with(d)
303 do
304 var closdef = d._closures[closure_decl]
305 if closdef == null then
306 # Default is already guarded and inlined
307 return
308 end
309 # Break sequence cannot be inlined :/
310 assert break_seq == null
311 var res = d._icb.inline_routine(closdef, d.dup_iregs(exprs), null)
312 if result != null then
313 assert res != null
314 d._icb.stmt(new IMove(d.dup_ireg(result.as(not null)), res))
315 end
316 end
317 end
318
319 redef class INative
320 redef fun inner_dup_with(d)
321 do
322 var c2 = new INative(code, d.dup_iregs(exprs))
323 c2.is_pure = is_pure
324 return c2
325 end
326 end
327
328 redef class IMove
329 redef fun inner_dup_with(d)
330 do
331 return new IMove(d.dup_ireg(result.as(not null)), d.dup_ireg(expr))
332 end
333 end
334
335 redef class IAttrRead
336 redef fun inner_dup_with(d)
337 do
338 return new IAttrRead(property, d.dup_ireg(expr))
339 end
340 end
341
342 redef class IAttrWrite
343 redef fun inner_dup_with(d)
344 do
345 return new IAttrWrite(property, d.dup_ireg(expr1), d.dup_ireg(expr2))
346 end
347 end
348
349 redef class IAttrIsset
350 redef fun inner_dup_with(d)
351 do
352 return new IAttrIsset(property, d.dup_ireg(expr))
353 end
354 end
355
356 redef class ITypeCheck
357 redef fun inner_dup_with(d)
358 do
359 return new ITypeCheck(d.dup_ireg(expr), stype)
360 end
361 end
362
363 redef class IIs
364 redef fun inner_dup_with(d)
365 do
366 return new IIs(d.dup_ireg(expr1), d.dup_ireg(expr2))
367 end
368 end
369
370 redef class INot
371 redef fun inner_dup_with(d)
372 do
373 return new INot(d.dup_ireg(expr))
374 end
375 end
376
377 redef class IOnce
378 redef fun inner_dup_with(d)
379 do
380 var c2 = new IOnce
381 body.dup_seq_to(d, c2.body)
382 return c2
383 end
384 end
385
386 redef class IHasClos
387 redef fun inner_dup_with(d)
388 do
389 var closdef = d._closures[closure_decl]
390 var res: IRegister
391 if closdef != null then
392 res = d._icb.lit_true_reg
393 else
394 res = d._icb.lit_false_reg
395 end
396 return new IMove(d.dup_ireg(result.as(not null)), res)
397 end
398 end