Merge branch 'tools' into wip
[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 module icode_tools
19 import icode_builder
20
21 # A simple visitor to visit icode structures
22 class ICodeVisitor
23 # Called when a iregister is read in a icode
24 fun visit_iregister_read(ic: ICode, r: IRegister) do end
25
26 # Called when a iregister is wrote in a icode
27 fun visit_iregister_write(ic: ICode, r: IRegister) do end
28
29 # The current icode iterator.
30 # Can be used to insert_before, used to change the item or deleted
31 readable var _current_icode: nullable ListIterator[ICode] = null
32
33 # Called when a icode is visited
34 # Automatically visits iregisters and sub-icodes
35 fun visit_icode(ic: nullable ICode)
36 do
37 if ic == null then return
38 if ic isa ISeq then
39 var old_icode = _current_icode
40 var cur = ic.icodes.iterator
41 while cur.is_ok do
42 _current_icode = cur
43 var ic2 = cur.item
44 visit_icode(ic2)
45 cur.next
46 end
47 _current_icode = old_icode
48 else if ic isa IIf then
49 visit_iregister_read(ic, ic.expr)
50 visit_icode(ic.then_seq)
51 visit_icode(ic.else_seq)
52 else if ic isa IOnce then
53 visit_icode(ic.body)
54 else if ic isa ICode1 then
55 visit_iregister_read(ic, ic.expr)
56 else if ic isa ICode2 then
57 visit_iregister_read(ic, ic.expr1)
58 visit_iregister_read(ic, ic.expr2)
59 else if ic isa ICodeN then
60 for e in ic.exprs do
61 visit_iregister_read(ic, e)
62 end
63 var closdefs = ic.closure_defs
64 if ic isa IClosCall then
65 visit_icode(ic.break_seq)
66 end
67 if closdefs != null then
68 visit_closure_defs(closdefs)
69 end
70 end
71 var r = ic.result
72 if r != null then visit_iregister_write(ic, r)
73 end
74
75 # Called when closure definitions are visited
76 # Automatically visits each closure definition
77 fun visit_closure_defs(closdefs: Collection[nullable IClosureDef])
78 do
79 for e in closdefs do
80 if e != null then
81 visit_iroutine(e)
82 end
83 end
84 end
85
86 # Called when an iroutine is visited
87 # Automatically visits the body
88 # Warning: parameters of result registers are not visited
89 fun visit_iroutine(ir: IRoutine)
90 do
91 visit_icode(ir.body)
92 end
93 end
94
95 redef class ICodeBuilder
96 # Inline an iroutine in the current icode sequence
97 # Require that routine != self.iroutine
98 fun inline_routine(routine: IRoutine, args: Sequence[IRegister], closdefs: nullable Sequence[nullable IClosureDef]): nullable IRegister
99 do
100 assert routine != self.iroutine
101 var d = new ICodeDupContext(self)
102 assert args.length == routine.params.length
103 var closdecls = routine.closure_decls
104 var cdefsa = if closdefs != null then closdefs.length else 0
105 var cdeclsa = if closdecls != null then closdecls.length else 0
106 assert cdefsa <= cdeclsa
107
108 # Fill register duplicate association
109 var dico = d._registers
110 var res = routine.result
111 if res != null then
112 var res2 = new_register(res.stype)
113 dico[res] = res2
114 res = res2
115 end
116 for reg in routine.registers do
117 assert not dico.has_key(reg)
118 dico[reg] = new_register(reg.stype)
119 end
120 for i in [0..args.length[ do
121 # FIXME The following assumes that params are readonly.
122 # The alternative is safe but add one move :/
123 dico[routine.params[i]] = args[i]
124 #seq.icodes.add(new IMove(dico[routine.params[i]]), args[i]))
125 end
126
127 # Fill escape mark association
128 for m in routine.escape_marks do
129 var m2 = new IEscapeMark
130 iroutine.escape_marks.add(m2)
131 d._marks[m] = m2
132 end
133
134 # Fill closure association
135 if closdecls != null then
136 var cdico = d._closures
137 for i in [0..cdefsa[ do
138 cdico[closdecls[i]] = closdefs[i]
139 end
140 for i in [cdefsa..cdeclsa[ do
141 cdico[closdecls[i]] = null
142 end
143 end
144
145 # Process inlining
146 routine.body.dup_with(d)
147 return res
148 end
149 end
150
151 # This class stores reference to allow correct duplication of icodes
152 private class ICodeDupContext
153 # Return the correct register
154 # * a duplicate of the local register 'r' of the inlined iroutine
155 # * 'r' else (it is a register of the caller iroutine)
156 fun dup_ireg(r: IRegister): IRegister
157 do
158 var rs = _registers
159 if rs.has_key(r) then
160 return rs[r]
161 else
162 return r
163 end
164 end
165
166 # Return a correct bunch of registers
167 fun dup_iregs(regs: Sequence[IRegister]): Array[IRegister]
168 do
169 var a = new Array[IRegister].with_capacity(regs.length)
170 for r in regs do
171 a.add(dup_ireg(r))
172 end
173 return a
174 end
175
176 # The assocation between old_ireg and new_ireg
177 # Used by dup_ireg
178 var _registers: Map[IRegister, IRegister] = new HashMap[IRegister, IRegister]
179
180 # Return the correct escape mark
181 # * a duplicate of the local escape mark 'm' of the inlined iroutine
182 # * 'r' else (it is a escape mark of the caller iroutine)
183 fun dup_mark(m: IEscapeMark): IEscapeMark
184 do
185 var ms = _marks
186 if ms.has_key(m) then
187 return ms[m]
188 else
189 return m
190 end
191 end
192
193 # The associoation between old_seq and new_seq
194 # Used by dup_mark
195 var _marks: Map[IEscapeMark, IEscapeMark] = new HashMap[IEscapeMark, IEscapeMark]
196
197 # The association between a closure_decl and its closure_def (if any)
198 var _closures: Map[IClosureDecl, nullable IClosureDef] = new ArrayMap[IClosureDecl, nullable IClosureDef]
199
200 # The current code builder
201 var _icb: ICodeBuilder
202
203 init(icb: ICodeBuilder)
204 do
205 _icb = icb
206 end
207 end
208
209 redef class ICode
210 # Duplicate the current icode in the icode builder of the ICodeDupContext
211 private fun dup_with(d: ICodeDupContext)
212 do
213 var c = inner_dup_with(d)
214 if self isa ICodeN then
215 assert c isa ICodeN
216 var cdecl = closure_defs
217 if cdecl != null then
218 # Duplicate the colsure definitions
219 var cdecl2 = new Array[nullable IClosureDef].with_capacity(cdecl.length)
220 for cd in cdecl do
221 if cd == null then
222 cdecl2.add(null)
223 else
224 var r = cd.result
225 if r != null then r = d.dup_ireg(r)
226 var cd2 = new IClosureDef(d.dup_iregs(cd.params), r)
227 cdecl2.add(cd2)
228 cd.body.dup_seq_to(d, cd2.body)
229 end
230 end
231 c.closure_defs = cdecl2
232 end
233 end
234 var r = result
235 if r != null then c.result = d.dup_ireg(r)
236 c.location = location
237 d._icb.seq.icodes.add(c)
238 end
239
240 # Simle partial duplication of the current icode
241 private fun inner_dup_with(d: ICodeDupContext): ICode is abstract
242 end
243
244 redef class ISeq
245 redef fun inner_dup_with(d)
246 do
247 var c2 = new ISeq
248 dup_seq_to(d, c2)
249 return c2
250 end
251
252 # Duplicate each icode and store them in dest
253 # Note: dest must be empty and not modified afted duplication or IEscapes may be wrongly duplicated
254 private fun dup_seq_to(d: ICodeDupContext, dest: ISeq)
255 do
256 var old_seq = d._icb.seq
257 d._icb.seq = dest
258 for c in icodes do
259 c.dup_with(d)
260 end
261 d._icb.seq = old_seq
262 assert dest.iescape_mark == null
263 var mark = iescape_mark
264 if mark != null then
265 dest.iescape_mark = d.dup_mark(mark)
266 end
267 end
268 end
269
270 redef class ILoop
271 redef fun inner_dup_with(d)
272 do
273 var c2 = new ILoop
274 dup_seq_to(d, c2)
275 return c2
276 end
277 end
278
279 redef class IIf
280 redef fun inner_dup_with(d)
281 do
282 var c2 = new IIf(d.dup_ireg(expr))
283 then_seq.dup_seq_to(d, c2.then_seq)
284 else_seq.dup_seq_to(d, c2.else_seq)
285 return c2
286 end
287 end
288
289 redef class IEscape
290 redef fun inner_dup_with(d)
291 do
292 # Get the associated mark (duplicated of not)
293 var mark = d.dup_mark(iescape_mark)
294 # Jump to the mark
295 return new IEscape(mark)
296 end
297 end
298
299 redef class IAbort
300 redef fun inner_dup_with(d)
301 do
302 return new IAbort(texts, module_location)
303 end
304 end
305
306 redef class ICall
307 redef fun inner_dup_with(d)
308 do
309 return new ICall(property, d.dup_iregs(exprs))
310 end
311 end
312
313 redef class ISuper
314 redef fun inner_dup_with(d)
315 do
316 return new ISuper(property, d.dup_iregs(exprs))
317 end
318 end
319
320 redef class INew
321 redef fun inner_dup_with(d)
322 do
323 return new INew(stype, property, d.dup_iregs(exprs))
324 end
325 end
326
327 redef class IAllocateInstance
328 redef fun inner_dup_with(d)
329 do
330 return new IAllocateInstance(stype)
331 end
332 end
333
334 redef class IStaticCall
335 redef fun inner_dup_with(d)
336 do
337 return new IStaticCall(property, d.dup_iregs(exprs))
338 end
339 end
340
341 redef class ICheckInstance
342 redef fun inner_dup_with(d)
343 do
344 return new ICheckInstance(stype, d.dup_ireg(expr))
345 end
346 end
347
348 redef class IInitAttributes
349 redef fun inner_dup_with(d)
350 do
351 return new IInitAttributes(stype, d.dup_ireg(expr))
352 end
353 end
354
355 redef class IClosCall
356 redef fun dup_with(d)
357 do
358 if d._closures.has_key(closure_decl) then
359 # The icloscall need to be replaced with an inlined closdef
360 var closdef = d._closures[closure_decl]
361 if closdef == null then
362 # Default is already guarded and inlined
363 return
364 end
365 # Break sequence cannot be inlined :/
366 assert break_seq == null
367 # Inline the closdef
368 var res = d._icb.inline_routine(closdef, d.dup_iregs(exprs), null)
369 if result != null then
370 assert res != null
371 d._icb.stmt(new IMove(d.dup_ireg(result.as(not null)), res))
372 end
373 else
374 # Standard icloscall duplication
375 super
376 end
377 end
378
379 redef fun inner_dup_with(d)
380 do
381 return new IClosCall(closure_decl, exprs)
382 end
383 end
384
385 redef class INative
386 redef fun inner_dup_with(d)
387 do
388 var c2 = new INative(method, d.dup_iregs(exprs))
389 c2.is_pure = is_pure
390 return c2
391 end
392 end
393
394 redef class IIntValue
395 redef fun inner_dup_with(d)
396 do
397 return new IIntValue(value)
398 end
399 end
400
401 redef class IBoolValue
402 redef fun inner_dup_with(d)
403 do
404 return new IBoolValue(value)
405 end
406 end
407
408 redef class IStringValue
409 redef fun inner_dup_with(d)
410 do
411 return new IStringValue(value)
412 end
413 end
414
415 redef class IFloatValue
416 redef fun inner_dup_with(d)
417 do
418 return new IFloatValue(value)
419 end
420 end
421
422 redef class ICharValue
423 redef fun inner_dup_with(d)
424 do
425 return new ICharValue(value)
426 end
427 end
428
429 redef class IMove
430 redef fun inner_dup_with(d)
431 do
432 return new IMove(d.dup_ireg(result.as(not null)), d.dup_ireg(expr))
433 end
434 end
435
436 redef class IAttrRead
437 redef fun inner_dup_with(d)
438 do
439 return new IAttrRead(property, d.dup_ireg(expr))
440 end
441 end
442
443 redef class IAttrWrite
444 redef fun inner_dup_with(d)
445 do
446 return new IAttrWrite(property, d.dup_ireg(expr1), d.dup_ireg(expr2))
447 end
448 end
449
450 redef class IAttrIsset
451 redef fun inner_dup_with(d)
452 do
453 return new IAttrIsset(property, d.dup_ireg(expr))
454 end
455 end
456
457 redef class ITypeCheck
458 redef fun inner_dup_with(d)
459 do
460 return new ITypeCheck(d.dup_ireg(expr1), d.dup_ireg(expr2), stype)
461 end
462 end
463
464 redef class IIs
465 redef fun inner_dup_with(d)
466 do
467 return new IIs(d.dup_ireg(expr1), d.dup_ireg(expr2))
468 end
469 end
470
471 redef class INot
472 redef fun inner_dup_with(d)
473 do
474 return new INot(d.dup_ireg(expr))
475 end
476 end
477
478 redef class IOnce
479 redef fun inner_dup_with(d)
480 do
481 var c2 = new IOnce
482 body.dup_seq_to(d, c2.body)
483 return c2
484 end
485 end
486
487 redef class IHasClos
488 redef fun inner_dup_with(d)
489 do
490 if d._closures.has_key(closure_decl) then
491 # closdef will be inlined
492 var closdef = d._closures[closure_decl]
493 var res: IRegister
494 if closdef != null then
495 res = d._icb.lit_true_reg
496 else
497 res = d._icb.lit_false_reg
498 end
499 return new IMove(d.dup_ireg(result.as(not null)), res)
500 else
501 return new IHasClos(closure_decl)
502 end
503 end
504 end