1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2015 Julien Pagès <julien.pages@lirmm.fr>
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 # Optimization of the nitvm
18 module vm_optimizations
20 import virtual_machine
22 redef class VirtualMachine
24 # Add optimization of the method dispatch
25 redef fun callsite
(callsite
: nullable CallSite, arguments
: Array[Instance]): nullable Instance
27 var initializers
= callsite
.mpropdef
.initializers
28 if initializers
.is_empty
then return send_optimize
(callsite
.as(not null), arguments
)
30 var recv
= arguments
.first
32 for p
in initializers
do
35 for x
in p
.intro
.msignature
.mparameters
do
40 else if p
isa MAttribute then
41 assert recv
isa MutableInstance
42 write_attribute
(p
, recv
, arguments
[i
])
46 assert i
== arguments
.length
48 return send_optimize
(callsite
.as(not null), [recv
])
51 # Try to have the most efficient implementation of the method dispatch
52 fun send_optimize
(callsite
: CallSite, args
: Array[Instance]): nullable Instance
55 var mtype
= recv
.mtype
56 var ret
= send_commons
(callsite
.mproperty
, args
, mtype
)
57 if ret
!= null then return ret
59 if callsite
.status
== 0 then callsite
.optimize
(recv
)
62 if callsite
.status
== 1 then
63 propdef
= method_dispatch_sst
(recv
.vtable
.internal_vtable
, callsite
.offset
)
65 propdef
= method_dispatch_ph
(recv
.vtable
.internal_vtable
, recv
.vtable
.mask
,
66 callsite
.id
, callsite
.offset
)
69 #TODO : we need recompilations here
71 return self.call
(propdef
, args
)
75 redef class AAttrFormExpr
76 # Position of the attribute in attribute table
78 # The relative position of this attribute if perfect hashing is used,
79 # The absolute position of this attribute if SST is used
82 # Indicate the status of the optimization for this node
85 # 1: SST (direct access) can be used
86 # 2: PH (multiple inheritance implementation) must be used
89 # Identifier of the class which introduced the attribute
92 # Optimize this attribute access
93 # * `mproperty` The attribute which is accessed
94 # * `recv` The receiver (The object) of the access
95 protected fun optimize
(mproperty
: MAttribute, recv
: MutableInstance)
97 var position
= recv
.mtype
.as(MClassType).mclass
.get_position_attributes
(mproperty
.intro_mclassdef
.mclass
)
99 # if this attribute class has an unique position for this receiver, then use direct access
100 offset
= position
+ mproperty
.offset
103 # Otherwise, perfect hashing must be used
104 id
= mproperty
.intro_mclassdef
.mclass
.vtable
.id
105 offset
= mproperty
.offset
111 redef class AAttrExpr
114 # TODO : a workaround for now
115 if not v
isa VirtualMachine then return super
117 var recv
= v
.expr
(self.n_expr
)
118 if recv
== null then return null
119 if recv
.mtype
isa MNullType then fatal
(v
, "Receiver is null")
120 var mproperty
= self.mproperty
.as(not null)
122 assert recv
isa MutableInstance
123 if status
== 0 then optimize
(mproperty
, recv
)
128 i
= v
.read_attribute_sst
(recv
.internal_attributes
, offset
)
131 i
= v
.read_attribute_ph
(recv
.internal_attributes
, recv
.vtable
.internal_vtable
, recv
.vtable
.mask
, id
, offset
)
134 # If we get a `MInit` value, throw an error
135 if i
== v
.initialization_value
then
136 v
.fatal
("Uninitialized attribute {mproperty.name}")
140 #TODO : we need recompilations here
147 redef class AAttrAssignExpr
150 # TODO : a workaround for now
151 if not v
isa VirtualMachine then
156 var recv
= v
.expr
(self.n_expr
)
157 if recv
== null then return
158 if recv
.mtype
isa MNullType then fatal
(v
, "Receiver is null")
159 var i
= v
.expr
(self.n_value
)
160 if i
== null then return
161 var mproperty
= self.mproperty
.as(not null)
163 assert recv
isa MutableInstance
164 if status
== 0 then optimize
(mproperty
, recv
)
167 v
.write_attribute_sst
(recv
.internal_attributes
, offset
, i
)
169 v
.write_attribute_ph
(recv
.internal_attributes
, recv
.vtable
.internal_vtable
,
170 recv
.vtable
.mask
, id
, offset
, i
)
173 #TODO : we need recompilations here
178 # Add informations to optimize some method calls
180 # Position of the method in virtual table
182 # The relative position of this MMethod if perfect hashing is used,
183 # The absolute position of this MMethod if SST is used
186 # Indicate the status of the optimization for this node
189 # 1: SST (direct access) can be used
190 # 2: PH (multiple inheritance implementation) must be used
193 # Identifier of the class which introduced the MMethod
196 # Optimize a method dispatch,
197 # If this method is always at the same position in virtual table, we can use direct access,
198 # Otherwise we must use perfect hashing
199 fun optimize
(recv
: Instance)
201 var position
= recv
.mtype
.as(MClassType).mclass
.get_position_methods
(mproperty
.intro_mclassdef
.mclass
)
203 offset
= position
+ mproperty
.offset
206 offset
= mproperty
.offset
209 id
= mproperty
.intro_mclassdef
.mclass
.vtable
.id
214 # Identifier of the target class type
217 # If the Cohen test is used, the position of the target id in vtable
220 # Indicate the status of the optimization for this node
222 # 0 : the default value
223 # 1 : this test can be implemented with direct access
224 # 2 : this test must be implemented with perfect hashing
229 # TODO : a workaround for now
230 if not v
isa VirtualMachine then return super
232 var recv
= v
.expr
(self.n_expr
)
233 if recv
== null then return null
235 optimize
(v
, recv
.mtype
, self.cast_type
.as(not null))
236 var mtype
= v
.unanchor_type
(self.cast_type
.as(not null))
238 # If this test can be optimized, directly call appropriate subtyping methods
239 if status
== 1 and recv
.mtype
isa MClassType then
241 return v
.bool_instance
(v
.inter_is_subtype_sst
(id
, position
, recv
.mtype
.as(MClassType).mclass
.vtable
.internal_vtable
))
242 else if status
== 2 and recv
.mtype
isa MClassType then
244 return v
.bool_instance
(v
.inter_is_subtype_ph
(id
, recv
.vtable
.mask
, recv
.mtype
.as(MClassType).mclass
.vtable
.internal_vtable
))
246 # Use the slow path (default)
247 return v
.bool_instance
(v
.is_subtype
(recv
.mtype
, mtype
))
251 # Optimize a `AIsaExpr`
252 # `source` the source type of the expression
253 # `target` the target type of the subtyping test
254 private fun optimize
(v
: VirtualMachine, source
: MType, target
: MType)
256 # If the source class and target class are not classic classes (non-generics) then return
257 if not source
isa MClassType or not target
isa MClassType or target
isa MGenericType then
261 if not target
.mclass
.abstract_loaded
then return
263 # If the value is positive, the target class has an invariant position in source's structures
264 var value
= source
.mclass
.get_position_methods
(target
.mclass
)
267 # `value - 2` is the position of the target identifier in source vtable
271 # We use perfect hashing
274 id
= target
.mclass
.vtable
.id
278 redef class AAsCastExpr
279 # Identifier of the target class type
282 # If the Cohen test is used, the position of the target id in vtable
285 # Indicate the status of the optimization for this node
287 # 0 : the default value
288 # 1 : this test can be implemented with direct access
289 # 2 : this test must be implemented with perfect hashing
294 # TODO : a workaround for now
295 if not v
isa VirtualMachine then return super
297 var recv
= v
.expr
(self.n_expr
)
298 if recv
== null then return null
300 optimize
(v
, recv
.mtype
, self.mtype
.as(not null))
302 var mtype
= self.mtype
.as(not null)
303 var amtype
= v
.unanchor_type
(mtype
)
306 if status
== 1 and recv
.mtype
isa MClassType then
308 res
= v
.inter_is_subtype_sst
(id
, position
, recv
.mtype
.as(MClassType).mclass
.vtable
.internal_vtable
)
309 else if status
== 2 and recv
.mtype
isa MClassType then
311 res
= v
.inter_is_subtype_ph
(id
, recv
.vtable
.mask
, recv
.mtype
.as(MClassType).mclass
.vtable
.internal_vtable
)
313 # Use the slow path (default)
314 res
= v
.is_subtype
(recv
.mtype
, amtype
)
318 fatal
(v
, "Cast failed. Expected `{amtype}`, got `{recv.mtype}`")
323 # Optimize a `AAsCastExpr`
324 # * `source` the source type of the expression
325 # * `target` the target type of the subtyping test
326 private fun optimize
(v
: VirtualMachine, source
: MType, target
: MType)
328 # If the source class and target class are not classic classes (non-generics) then return
329 if not source
isa MClassType or not target
isa MClassType or target
isa MGenericType then
333 if not target
.mclass
.loaded
then return
335 # If the value is positive, the target class has an invariant position in source's structures
336 var value
= source
.mclass
.get_position_methods
(target
.mclass
)
339 # `value - 2` is the position of the target identifier in source vtable
343 # We use perfect hashing
346 id
= target
.mclass
.vtable
.id