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
20 # A simple visitor to visit icode structures
22 # Called when a iregister is read in a icode
23 fun visit_iregister_read
(ic
: ICode, r
: IRegister) do end
25 # Called when a iregister is wrote in a icode
26 fun visit_iregister_write
(ic
: ICode, r
: IRegister) do end
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
32 # Called when a icode is visited
33 # Automatically visits iregisters and sub-icodes
34 fun visit_icode
(ic
: nullable ICode)
36 if ic
== null then return
38 var old_icode
= _current_icode
39 var cur
= ic
.icodes
.iterator
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
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
60 visit_iregister_read
(ic
, e
)
62 var closdefs
= ic
.closure_defs
63 if ic
isa IClosCall then
64 visit_icode
(ic
.break_seq
)
66 if closdefs
!= null then
67 visit_closure_defs
(closdefs
)
71 if r
!= null then visit_iregister_write
(ic
, r
)
74 # Called when closure definitions are visited
75 # Automatically visits each closure definition
76 fun visit_closure_defs
(closdefs
: Collection[nullable IClosureDef])
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)
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
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
107 # Fill register duplicate association
108 var dico
= d
._registers
109 var res
= routine
.result
111 var res2
= new_register
(res
.stype
)
115 for reg
in routine
.registers
do
116 assert not dico
.has_key
(reg
)
117 dico
[reg
] = new_register
(reg
.stype
)
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]))
126 # Fill escape mark association
127 for m
in routine
.escape_marks
do
128 var m2
= new IEscapeMark
129 iroutine
.escape_marks
.add
(m2
)
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
]
139 for i
in [cdefsa
..cdeclsa
[ do
140 cdico
[closdecls
[i
]] = null
145 routine
.body
.dup_with
(d
)
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
158 if rs
.has_key
(r
) then
165 # Return a correct bunch of registers
166 fun dup_iregs
(regs
: Sequence[IRegister]): Array[IRegister]
168 var a
= new Array[IRegister].with_capacity
(regs
.length
)
175 # The assocation between old_ireg and new_ireg
177 var _registers
: Map[IRegister, IRegister] = new HashMap[IRegister, IRegister]
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
185 if ms
.has_key
(m
) then
192 # The associoation between old_seq and new_seq
194 var _marks
: Map[IEscapeMark, IEscapeMark] = new HashMap[IEscapeMark, IEscapeMark]
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]
199 # The current code builder
200 var _icb
: ICodeBuilder
202 init(icb
: ICodeBuilder)
209 # Duplicate the current icode in the icode builder of the ICodeDupContext
210 private fun dup_with
(d
: ICodeDupContext)
212 var c
= inner_dup_with
(d
)
213 if self isa ICodeN then
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
)
224 if r
!= null then r
= d
.dup_ireg
(r
)
225 var cd2
= new IClosureDef(d
.dup_iregs
(cd
.params
), r
)
227 cd
.body
.dup_seq_to
(d
, cd2
.body
)
230 c
.closure_defs
= cdecl2
234 if r
!= null then c
.result
= d
.dup_ireg
(r
)
235 c
.location
= location
236 d
._icb
.seq
.icodes
.add
(c
)
239 # Simle partial duplication of the current icode
240 private fun inner_dup_with
(d
: ICodeDupContext): ICode is abstract
244 redef fun inner_dup_with
(d
)
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)
255 var old_seq
= d
._icb
.seq
261 assert dest
.iescape_mark
== null
262 var mark
= iescape_mark
264 dest
.iescape_mark
= d
.dup_mark
(mark
)
270 redef fun inner_dup_with
(d
)
279 redef fun inner_dup_with
(d
)
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
)
289 redef fun inner_dup_with
(d
)
291 # Get the associated mark (duplicated of not)
292 var mark
= d
.dup_mark
(iescape_mark
)
294 return new IEscape(mark
)
299 redef fun inner_dup_with
(d
)
301 return new IAbort(texts
, module_location
)
306 redef fun inner_dup_with
(d
)
308 return new ICall(property
, d
.dup_iregs
(exprs
))
313 redef fun inner_dup_with
(d
)
315 return new ISuper(property
, d
.dup_iregs
(exprs
))
320 redef fun inner_dup_with
(d
)
322 return new INew(stype
, property
, d
.dup_iregs
(exprs
))
326 redef class IAllocateInstance
327 redef fun inner_dup_with
(d
)
329 return new IAllocateInstance(stype
)
333 redef class IStaticCall
334 redef fun inner_dup_with
(d
)
336 return new IStaticCall(property
, d
.dup_iregs
(exprs
))
340 redef class ICheckInstance
341 redef fun inner_dup_with
(d
)
343 return new ICheckInstance(stype
, d
.dup_ireg
(expr
))
347 redef class IInitAttributes
348 redef fun inner_dup_with
(d
)
350 return new IInitAttributes(stype
, d
.dup_ireg
(expr
))
354 redef class IClosCall
355 redef fun dup_with
(d
)
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
364 # Break sequence cannot be inlined :/
365 assert break_seq
== null
367 var res
= d
._icb
.inline_routine
(closdef
, d
.dup_iregs
(exprs
), null)
368 if result
!= null then
370 d
._icb
.stmt
(new IMove(d
.dup_ireg
(result
.as(not null)), res
))
373 # Standard icloscall duplication
378 redef fun inner_dup_with
(d
)
380 return new IClosCall(closure_decl
, exprs
)
385 redef fun inner_dup_with
(d
)
387 var c2
= new INative(method
, d
.dup_iregs
(exprs
))
393 redef class IIntValue
394 redef fun inner_dup_with
(d
)
396 return new IIntValue(value
)
400 redef class IBoolValue
401 redef fun inner_dup_with
(d
)
403 return new IBoolValue(value
)
407 redef class IStringValue
408 redef fun inner_dup_with
(d
)
410 return new IStringValue(value
)
414 redef class IFloatValue
415 redef fun inner_dup_with
(d
)
417 return new IFloatValue(value
)
421 redef class ICharValue
422 redef fun inner_dup_with
(d
)
424 return new ICharValue(value
)
429 redef fun inner_dup_with
(d
)
431 return new IMove(d
.dup_ireg
(result
.as(not null)), d
.dup_ireg
(expr
))
435 redef class IAttrRead
436 redef fun inner_dup_with
(d
)
438 return new IAttrRead(property
, d
.dup_ireg
(expr
))
442 redef class IAttrWrite
443 redef fun inner_dup_with
(d
)
445 return new IAttrWrite(property
, d
.dup_ireg
(expr1
), d
.dup_ireg
(expr2
))
449 redef class IAttrIsset
450 redef fun inner_dup_with
(d
)
452 return new IAttrIsset(property
, d
.dup_ireg
(expr
))
456 redef class ITypeCheck
457 redef fun inner_dup_with
(d
)
459 return new ITypeCheck(d
.dup_ireg
(expr1
), d
.dup_ireg
(expr2
), stype
)
464 redef fun inner_dup_with
(d
)
466 return new IIs(d
.dup_ireg
(expr1
), d
.dup_ireg
(expr2
))
471 redef fun inner_dup_with
(d
)
473 return new INot(d
.dup_ireg
(expr
))
478 redef fun inner_dup_with
(d
)
481 body
.dup_seq_to
(d
, c2
.body
)
487 redef fun inner_dup_with
(d
)
489 if d
._closures
.has_key
(closure_decl
) then
490 # closdef will be inlined
491 var closdef
= d
._closures
[closure_decl
]
493 if closdef
!= null then
494 res
= d
._icb
.lit_true_reg
496 res
= d
._icb
.lit_false_reg
498 return new IMove(d
.dup_ireg
(result
.as(not null)), res
)
500 return new IHasClos(closure_decl
)