518623340aa411711e6b756cb708d4593b83a155
[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_base
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 IRoutine
95 # Inline an iroutine in an icode sequence
96 fun inline_in_seq(seq: ISeq, args: IndexedCollection[IRegister]): nullable IRegister
97 do
98 var d = new ICodeDupContext
99 if args.length != params.length then print "{args.length} != {params.length}"
100 assert args.length == params.length
101 for i in [0..args.length[ do
102 # FIXME The following assumes that params are readonly.
103 # The alternative is safe but add one move :/
104 d._registers[params[i]] = args[i]
105 #seq.icodes.add(new IMove(d.dup_ireg(params[i]), args[i]))
106 end
107 seq.icodes.add(body.dup_with(d))
108 var r = result
109 if r != null then r = d.dup_ireg(r)
110 return r
111 end
112 end
113
114 # This class stores reference to allow correct duplication of icodes
115 private class ICodeDupContext
116 # Duplicate one register
117 # Subsequent invocation will return the same register
118 fun dup_ireg(r: IRegister): IRegister
119 do
120 var rs = _registers
121 if rs.has_key(r) then
122 return rs[r]
123 else
124 var r2 = new IRegister(r.stype)
125 rs[r] = r2
126 return r2
127 end
128 end
129
130 # Duplicate a bunch of registers
131 # Subsequent invocation will return the same registers
132 fun dup_iregs(regs: IndexedCollection[IRegister]): IndexedCollection[IRegister]
133 do
134 var a = new Array[IRegister].with_capacity(regs.length)
135 for r in regs do
136 a.add(dup_ireg(r))
137 end
138 return a
139 end
140
141 # The associoation between old_seq and new_seq
142 # Directly used by the IEscape
143 var _seqs: Map[ISeq, ISeq] = new HashMap[ISeq, ISeq]
144
145 # The assocation between old_ireg and new_ireg
146 # Used by dup_ireg
147 var _registers: Map[IRegister, IRegister] = new HashMap[IRegister, IRegister]
148 end
149
150 redef class ICode
151 # Duplicate the current icode (generic part)
152 private fun dup_with(d: ICodeDupContext): ICode
153 do
154 var c = inner_dup_with(d)
155 var r = result
156 if r != null then c.result = d.dup_ireg(r)
157 c.location = location
158 return c
159 end
160
161 # Duplicate the current icode (specific part)
162 private fun inner_dup_with(d: ICodeDupContext): ICode is abstract
163 end
164
165 redef class ISeq
166 redef fun inner_dup_with(d)
167 do
168 var c2 = new ISeq
169 dup_seq_to(d, c2)
170 return c2
171 end
172
173 # Duplicate each icode and store them in dest
174 # Note: dest must be empty and not modified afted duplication or IEscapes may be wrongly duplicated
175 private fun dup_seq_to(d: ICodeDupContext, dest: ISeq)
176 do
177 d._seqs[self] = dest
178 for c in icodes do
179 dest.icodes.add(c.dup_with(d))
180 end
181 end
182 end
183
184 redef class ILoop
185 redef fun inner_dup_with(d)
186 do
187 var c2 = new ILoop
188 dup_seq_to(d, c2)
189 return c2
190 end
191 end
192
193 redef class IIf
194 redef fun inner_dup_with(d)
195 do
196 var c2 = new IIf(d.dup_ireg(expr))
197 then_seq.dup_seq_to(d, c2.then_seq)
198 else_seq.dup_seq_to(d, c2.else_seq)
199 return c2
200 end
201 end
202
203 redef class IEscape
204 redef fun inner_dup_with(d)
205 do
206 if d._seqs.has_key(seq) then
207 # Jump to a duplicated sequence
208 return new IEscape(d._seqs[seq])
209 else
210 # Jump to an englobing unduplicated sequence
211 return new IEscape(seq)
212 end
213 end
214 end
215
216 redef class IAbort
217 redef fun inner_dup_with(d)
218 do
219 return new IAbort(texts, property_location, module_location)
220 end
221 end
222
223 redef class ICall
224 redef fun inner_dup_with(d)
225 do
226 return new ICall(property, d.dup_iregs(exprs))
227 end
228 end
229
230 redef class ISuper
231 redef fun inner_dup_with(d)
232 do
233 return new ISuper(property, d.dup_iregs(exprs))
234 end
235 end
236
237 redef class INew
238 redef fun inner_dup_with(d)
239 do
240 return new INew(stype, property, d.dup_iregs(exprs))
241 end
242 end
243
244 redef class IClosCall
245 redef fun inner_dup_with(d)
246 do
247 var c2 = new IClosCall(closure_decl, d.dup_iregs(exprs))
248 var bs = break_seq
249 if bs != null then
250 var bs2 = new ISeq
251 c2.break_seq = bs2
252 bs.dup_seq_to(d, bs2)
253 end
254 return c2
255 end
256 end
257
258 redef class INative
259 redef fun inner_dup_with(d)
260 do
261 var c2 = new INative(code, d.dup_iregs(exprs))
262 c2.is_pure = is_pure
263 return c2
264 end
265 end
266
267 redef class IMove
268 redef fun inner_dup_with(d)
269 do
270 return new IMove(d.dup_ireg(result.as(not null)), d.dup_ireg(expr))
271 end
272 end
273
274 redef class IAttrRead
275 redef fun inner_dup_with(d)
276 do
277 return new IAttrRead(property, d.dup_ireg(expr))
278 end
279 end
280
281 redef class IAttrWrite
282 redef fun inner_dup_with(d)
283 do
284 return new IAttrWrite(property, d.dup_ireg(expr1), d.dup_ireg(expr2))
285 end
286 end
287
288 redef class IAttrIsset
289 redef fun inner_dup_with(d)
290 do
291 return new IAttrIsset(property, d.dup_ireg(expr))
292 end
293 end
294
295 redef class ITypeCheck
296 redef fun inner_dup_with(d)
297 do
298 return new ITypeCheck(d.dup_ireg(expr), stype)
299 end
300 end
301
302 redef class IIs
303 redef fun inner_dup_with(d)
304 do
305 return new IIs(d.dup_ireg(expr1), d.dup_ireg(expr2))
306 end
307 end
308
309 redef class INot
310 redef fun inner_dup_with(d)
311 do
312 return new INot(d.dup_ireg(expr))
313 end
314 end
315
316 redef class IOnce
317 redef fun inner_dup_with(d)
318 do
319 var c2 = new IOnce
320 body.dup_seq_to(d, c2.body)
321 return c2
322 end
323 end
324
325 redef class IHasClos
326 redef fun inner_dup_with(d)
327 do
328 return new IHasClos(closure_decl)
329 end
330 end