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