icode: inline foreign icloscall and ihasclos
[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 escape mark association
138 for m in routine.escape_marks do
139 var m2 = new IEscapeMark
140 iroutine.escape_marks.add(m2)
141 d._marks[m] = m2
142 end
143
144 # Fill closure association
145 if closdecls != null then
146 var cdico = d._closures
147 for i in [0..cdefsa[ do
148 cdico[closdecls[i]] = closdefs[i]
149 end
150 for i in [cdefsa..cdeclsa[ do
151 cdico[closdecls[i]] = null
152 end
153 end
154
155 # Process inlining
156 routine.body.dup_with(d)
157 _current_inlining.pop
158 return res
159 end
160 end
161
162 # This class stores reference to allow correct duplication of icodes
163 private class ICodeDupContext
164 # Return the correct register
165 # * a duplicate of the local register 'r' of the inlined iroutine
166 # * 'r' else (it is a register of the caller iroutine)
167 fun dup_ireg(r: IRegister): IRegister
168 do
169 var rs = _registers
170 if rs.has_key(r) then
171 return rs[r]
172 else
173 return r
174 end
175 end
176
177 # Return a correct bunch of registers
178 fun dup_iregs(regs: Sequence[IRegister]): Array[IRegister]
179 do
180 var a = new Array[IRegister].with_capacity(regs.length)
181 for r in regs do
182 a.add(dup_ireg(r))
183 end
184 return a
185 end
186
187 # The assocation between old_ireg and new_ireg
188 # Used by dup_ireg
189 var _registers: Map[IRegister, IRegister] = new HashMap[IRegister, IRegister]
190
191 # Return the correct escape mark
192 # * a duplicate of the local escape mark 'm' of the inlined iroutine
193 # * 'r' else (it is a escape mark of the caller iroutine)
194 fun dup_mark(m: IEscapeMark): IEscapeMark
195 do
196 var ms = _marks
197 if ms.has_key(m) then
198 return ms[m]
199 else
200 return m
201 end
202 end
203
204 # The associoation between old_seq and new_seq
205 # Used by dup_mark
206 var _marks: Map[IEscapeMark, IEscapeMark] = new HashMap[IEscapeMark, IEscapeMark]
207
208 # The association between a closure_decl and its closure_def (if any)
209 var _closures: Map[IClosureDecl, nullable IClosureDef] = new ArrayMap[IClosureDecl, nullable IClosureDef]
210
211 # The current code builder
212 var _icb: ICodeBuilder
213
214 init(icb: ICodeBuilder)
215 do
216 _icb = icb
217 end
218 end
219
220 redef class ICode
221 # Duplicate the current icode in the icode builder of the ICodeDupContext
222 private fun dup_with(d: ICodeDupContext)
223 do
224 var c = inner_dup_with(d)
225 if self isa ICodeN then
226 assert c isa ICodeN
227 var cdecl = closure_defs
228 if cdecl != null then
229 # Duplicate the colsure definitions
230 var cdecl2 = new Array[nullable IClosureDef].with_capacity(cdecl.length)
231 for cd in cdecl do
232 if cd == null then
233 cdecl2.add(null)
234 else
235 var r = cd.result
236 if r != null then r = d.dup_ireg(r)
237 var cd2 = new IClosureDef(d.dup_iregs(cd.params), r)
238 cdecl2.add(cd2)
239 cd.body.dup_seq_to(d, cd2.body)
240 end
241 end
242 c.closure_defs = cdecl2
243 end
244 end
245 var r = result
246 if r != null then c.result = d.dup_ireg(r)
247 c.location = location
248 d._icb.seq.icodes.add(c)
249 end
250
251 # Simle partial duplication of the current icode
252 private fun inner_dup_with(d: ICodeDupContext): ICode is abstract
253 end
254
255 redef class ISeq
256 redef fun inner_dup_with(d)
257 do
258 var c2 = new ISeq
259 dup_seq_to(d, c2)
260 return c2
261 end
262
263 # Duplicate each icode and store them in dest
264 # Note: dest must be empty and not modified afted duplication or IEscapes may be wrongly duplicated
265 private fun dup_seq_to(d: ICodeDupContext, dest: ISeq)
266 do
267 var old_seq = d._icb.seq
268 d._icb.seq = dest
269 for c in icodes do
270 c.dup_with(d)
271 end
272 d._icb.seq = old_seq
273 assert dest.iescape_mark == null
274 var mark = iescape_mark
275 if mark != null then
276 dest.iescape_mark = d.dup_mark(mark)
277 end
278 end
279 end
280
281 redef class ILoop
282 redef fun inner_dup_with(d)
283 do
284 var c2 = new ILoop
285 dup_seq_to(d, c2)
286 return c2
287 end
288 end
289
290 redef class IIf
291 redef fun inner_dup_with(d)
292 do
293 var c2 = new IIf(d.dup_ireg(expr))
294 then_seq.dup_seq_to(d, c2.then_seq)
295 else_seq.dup_seq_to(d, c2.else_seq)
296 return c2
297 end
298 end
299
300 redef class IEscape
301 redef fun inner_dup_with(d)
302 do
303 # Get the associated mark (duplicated of not)
304 var mark = d.dup_mark(iescape_mark)
305 # Jump to the mark
306 return new IEscape(mark)
307 end
308 end
309
310 redef class IAbort
311 redef fun inner_dup_with(d)
312 do
313 return new IAbort(texts, module_location)
314 end
315 end
316
317 redef class ICall
318 redef fun inner_dup_with(d)
319 do
320 return new ICall(property, d.dup_iregs(exprs))
321 end
322 end
323
324 redef class ISuper
325 redef fun inner_dup_with(d)
326 do
327 return new ISuper(property, d.dup_iregs(exprs))
328 end
329 end
330
331 redef class INew
332 redef fun inner_dup_with(d)
333 do
334 return new INew(stype, property, d.dup_iregs(exprs))
335 end
336 end
337
338 redef class IAllocateInstance
339 redef fun inner_dup_with(d)
340 do
341 return new IAllocateInstance(stype)
342 end
343 end
344
345 redef class IStaticCall
346 redef fun inner_dup_with(d)
347 do
348 return new IStaticCall(property, d.dup_iregs(exprs))
349 end
350 end
351
352 redef class ICheckInstance
353 redef fun inner_dup_with(d)
354 do
355 return new ICheckInstance(stype, d.dup_ireg(expr))
356 end
357 end
358
359 redef class IInitAttributes
360 redef fun inner_dup_with(d)
361 do
362 return new IInitAttributes(stype, d.dup_ireg(expr))
363 end
364 end
365
366 redef class IClosCall
367 redef fun dup_with(d)
368 do
369 if d._closures.has_key(closure_decl) then
370 # The icloscall need to be replaced with an inlined closdef
371 var closdef = d._closures[closure_decl]
372 if closdef == null then
373 # Default is already guarded and inlined
374 return
375 end
376 # Break sequence cannot be inlined :/
377 assert break_seq == null
378 # Inline the closdef
379 var res = d._icb.inline_routine(closdef, d.dup_iregs(exprs), null)
380 if result != null then
381 assert res != null
382 d._icb.stmt(new IMove(d.dup_ireg(result.as(not null)), res))
383 end
384 else
385 # Standard icloscall duplication
386 super
387 end
388 end
389
390 redef fun inner_dup_with(d)
391 do
392 return new IClosCall(closure_decl, exprs)
393 end
394 end
395
396 redef class INative
397 redef fun inner_dup_with(d)
398 do
399 var c2 = new INative(code, d.dup_iregs(exprs))
400 c2.is_pure = is_pure
401 return c2
402 end
403 end
404
405 redef class IMove
406 redef fun inner_dup_with(d)
407 do
408 return new IMove(d.dup_ireg(result.as(not null)), d.dup_ireg(expr))
409 end
410 end
411
412 redef class IAttrRead
413 redef fun inner_dup_with(d)
414 do
415 return new IAttrRead(property, d.dup_ireg(expr))
416 end
417 end
418
419 redef class IAttrWrite
420 redef fun inner_dup_with(d)
421 do
422 return new IAttrWrite(property, d.dup_ireg(expr1), d.dup_ireg(expr2))
423 end
424 end
425
426 redef class IAttrIsset
427 redef fun inner_dup_with(d)
428 do
429 return new IAttrIsset(property, d.dup_ireg(expr))
430 end
431 end
432
433 redef class ITypeCheck
434 redef fun inner_dup_with(d)
435 do
436 return new ITypeCheck(d.dup_ireg(expr), stype)
437 end
438 end
439
440 redef class IIs
441 redef fun inner_dup_with(d)
442 do
443 return new IIs(d.dup_ireg(expr1), d.dup_ireg(expr2))
444 end
445 end
446
447 redef class INot
448 redef fun inner_dup_with(d)
449 do
450 return new INot(d.dup_ireg(expr))
451 end
452 end
453
454 redef class IOnce
455 redef fun inner_dup_with(d)
456 do
457 var c2 = new IOnce
458 body.dup_seq_to(d, c2.body)
459 return c2
460 end
461 end
462
463 redef class IHasClos
464 redef fun inner_dup_with(d)
465 do
466 if d._closures.has_key(closure_decl) then
467 # closdef will be inlined
468 var closdef = d._closures[closure_decl]
469 var res: IRegister
470 if closdef != null then
471 res = d._icb.lit_true_reg
472 else
473 res = d._icb.lit_false_reg
474 end
475 return new IMove(d.dup_ireg(result.as(not null)), res)
476 else
477 return new IHasClos(closure_decl)
478 end
479 end
480 end