1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2009 Jean Privat <jean@pryen.org>
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
9 # http://www.apache.org/licenses/LICENSE-2.0
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.
17 # Tools to manipulate intermediace nit code representation
21 # A simple visitor to visit icode structures
22 abstract class ICodeVisitor
23 # Called when a iregister is read in a icode
24 fun visit_iregister_read
(ic
: ICode, r
: IRegister) do end
26 # Called when a iregister is wrote in a icode
27 fun visit_iregister_write
(ic
: ICode, r
: IRegister) do end
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
33 # Called when a icode is visited
34 # Automatically visits iregisters and sub-icodes
35 fun visit_icode
(ic
: nullable ICode)
37 if ic
== null then return
39 var old_icode
= _current_icode
40 var cur
= ic
.icodes
.iterator
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
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
61 visit_iregister_read
(ic
, e
)
63 var closdefs
= ic
.closure_defs
64 if ic
isa IClosCall then
65 visit_icode
(ic
.break_seq
)
67 if closdefs
!= null then
68 visit_closure_defs
(closdefs
)
72 if r
!= null then visit_iregister_write
(ic
, r
)
75 # Called when closure definitions are visited
76 # Automatically visits each closure definition
77 fun visit_closure_defs
(closdefs
: Collection[nullable IClosureDef])
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)
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
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
108 # Fill register duplicate association
109 var dico
= d
._registers
110 var res
= routine
.result
112 var res2
= new_register
(res
.stype
)
116 for reg
in routine
.registers
do
117 assert not dico
.has_key
(reg
)
118 dico
[reg
] = new_register
(reg
.stype
)
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]))
127 # Fill escape mark association
128 for m
in routine
.escape_marks
do
129 var m2
= new IEscapeMark
130 iroutine
.escape_marks
.add
(m2
)
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
]
140 for i
in [cdefsa
..cdeclsa
[ do
141 cdico
[closdecls
[i
]] = null
146 routine
.body
.dup_with
(d
)
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
159 if rs
.has_key
(r
) then
166 # Return a correct bunch of registers
167 fun dup_iregs
(regs
: Sequence[IRegister]): Array[IRegister]
169 var a
= new Array[IRegister].with_capacity
(regs
.length
)
176 # The assocation between old_ireg and new_ireg
178 var _registers
: Map[IRegister, IRegister] = new HashMap[IRegister, IRegister]
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
186 if ms
.has_key
(m
) then
193 # The associoation between old_seq and new_seq
195 var _marks
: Map[IEscapeMark, IEscapeMark] = new HashMap[IEscapeMark, IEscapeMark]
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]
200 # The current code builder
201 var _icb
: ICodeBuilder
203 init(icb
: ICodeBuilder)
210 # Duplicate the current icode in the icode builder of the ICodeDupContext
211 private fun dup_with
(d
: ICodeDupContext)
213 var c
= inner_dup_with
(d
)
214 if self isa ICodeN then
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
)
225 if r
!= null then r
= d
.dup_ireg
(r
)
226 var cd2
= new IClosureDef(d
.dup_iregs
(cd
.params
), r
)
228 cd
.body
.dup_seq_to
(d
, cd2
.body
)
231 c
.closure_defs
= cdecl2
235 if r
!= null then c
.result
= d
.dup_ireg
(r
)
236 c
.location
= location
237 d
._icb
.seq
.icodes
.add
(c
)
240 # Simle partial duplication of the current icode
241 private fun inner_dup_with
(d
: ICodeDupContext): ICode is abstract
245 redef fun inner_dup_with
(d
)
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)
256 var old_seq
= d
._icb
.seq
262 assert dest
.iescape_mark
== null
263 var mark
= iescape_mark
265 dest
.iescape_mark
= d
.dup_mark
(mark
)
271 redef fun inner_dup_with
(d
)
280 redef fun inner_dup_with
(d
)
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
)
290 redef fun inner_dup_with
(d
)
292 # Get the associated mark (duplicated of not)
293 var mark
= d
.dup_mark
(iescape_mark
)
295 return new IEscape(mark
)
300 redef fun inner_dup_with
(d
)
302 return new IAbort(texts
, module_location
)
307 redef fun inner_dup_with
(d
)
309 return new ICall(property
, d
.dup_iregs
(exprs
))
314 redef fun inner_dup_with
(d
)
316 return new ISuper(property
, d
.dup_iregs
(exprs
))
321 redef fun inner_dup_with
(d
)
323 return new INew(stype
, property
, d
.dup_iregs
(exprs
))
327 redef class IAllocateInstance
328 redef fun inner_dup_with
(d
)
330 return new IAllocateInstance(stype
)
334 redef class IStaticCall
335 redef fun inner_dup_with
(d
)
337 return new IStaticCall(property
, d
.dup_iregs
(exprs
))
341 redef class ICheckInstance
342 redef fun inner_dup_with
(d
)
344 return new ICheckInstance(stype
, d
.dup_ireg
(expr
))
348 redef class IInitAttributes
349 redef fun inner_dup_with
(d
)
351 return new IInitAttributes(stype
, d
.dup_ireg
(expr
))
355 redef class IClosCall
356 redef fun dup_with
(d
)
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
365 # Break sequence cannot be inlined :/
366 assert break_seq
== null
368 var res
= d
._icb
.inline_routine
(closdef
, d
.dup_iregs
(exprs
), null)
369 if result
!= null then
371 d
._icb
.stmt
(new IMove(d
.dup_ireg
(result
.as(not null)), res
))
374 # Standard icloscall duplication
379 redef fun inner_dup_with
(d
)
381 return new IClosCall(closure_decl
, exprs
)
386 redef fun inner_dup_with
(d
)
388 var c2
= new INative(method
, d
.dup_iregs
(exprs
))
394 redef class IIntValue
395 redef fun inner_dup_with
(d
)
397 return new IIntValue(value
)
401 redef class IBoolValue
402 redef fun inner_dup_with
(d
)
404 return new IBoolValue(value
)
408 redef class IStringValue
409 redef fun inner_dup_with
(d
)
411 return new IStringValue(value
)
415 redef class IFloatValue
416 redef fun inner_dup_with
(d
)
418 return new IFloatValue(value
)
422 redef class ICharValue
423 redef fun inner_dup_with
(d
)
425 return new ICharValue(value
)
430 redef fun inner_dup_with
(d
)
432 return new IMove(d
.dup_ireg
(result
.as(not null)), d
.dup_ireg
(expr
))
436 redef class IAttrRead
437 redef fun inner_dup_with
(d
)
439 return new IAttrRead(property
, d
.dup_ireg
(expr
))
443 redef class IAttrWrite
444 redef fun inner_dup_with
(d
)
446 return new IAttrWrite(property
, d
.dup_ireg
(expr1
), d
.dup_ireg
(expr2
))
450 redef class IAttrIsset
451 redef fun inner_dup_with
(d
)
453 return new IAttrIsset(property
, d
.dup_ireg
(expr
))
457 redef class ITypeCheck
458 redef fun inner_dup_with
(d
)
460 return new ITypeCheck(d
.dup_ireg
(expr1
), d
.dup_ireg
(expr2
), stype
)
465 redef fun inner_dup_with
(d
)
467 return new IIs(d
.dup_ireg
(expr1
), d
.dup_ireg
(expr2
))
472 redef fun inner_dup_with
(d
)
474 return new INot(d
.dup_ireg
(expr
))
479 redef fun inner_dup_with
(d
)
482 body
.dup_seq_to
(d
, c2
.body
)
488 redef fun inner_dup_with
(d
)
490 if d
._closures
.has_key
(closure_decl
) then
491 # closdef will be inlined
492 var closdef
= d
._closures
[closure_decl
]
494 if closdef
!= null then
495 res
= d
._icb
.lit_true_reg
497 res
= d
._icb
.lit_false_reg
499 return new IMove(d
.dup_ireg
(result
.as(not null)), res
)
501 return new IHasClos(closure_decl
)