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