+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2015 Julien Pagès <julien.pages@lirmm.fr>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Optimization of the nitvm
-module vm_optimizations
-
-import vm
-
-redef class VirtualMachine
-
- # Add optimization of the method dispatch
- redef fun callsite(callsite: nullable CallSite, arguments: Array[Instance]): nullable Instance
- do
- var initializers = callsite.mpropdef.initializers
- if initializers.is_empty then return send_optimize(callsite.as(not null), arguments)
-
- var recv = arguments.first
- var i = 1
- for p in initializers do
- if p isa MMethod then
- var args = [recv]
- for x in p.intro.msignature.mparameters do
- args.add arguments[i]
- i += 1
- end
- self.send(p, args)
- else if p isa MAttribute then
- assert recv isa MutableInstance
- write_attribute(p, recv, arguments[i])
- i += 1
- else abort
- end
- assert i == arguments.length
-
- return send_optimize(callsite.as(not null), [recv])
- end
-
- # Try to have the most efficient implementation of the method dispatch
- fun send_optimize(callsite: CallSite, args: Array[Instance]): nullable Instance
- do
- var recv = args.first
- var mtype = recv.mtype
- var ret = send_commons(callsite.mproperty, args, mtype)
- if ret != null then return ret
-
- if callsite.status == 0 then callsite.optimize(recv)
-
- var propdef
- if callsite.status == 1 then
- propdef = method_dispatch_sst(recv.vtable.internal_vtable, callsite.offset)
- else
- propdef = method_dispatch_ph(recv.vtable.internal_vtable, recv.vtable.mask,
- callsite.id, callsite.offset)
- end
-
- return self.call(propdef, args)
- end
-end
-
-redef class AAttrFormExpr
- # Position of the attribute in attribute table
- #
- # The relative position of this attribute if perfect hashing is used,
- # The absolute position of this attribute if SST is used
- var offset: Int
-
- # Indicate the status of the optimization for this node
- #
- # 0: default value
- # 1: SST (direct access) can be used
- # 2: PH (multiple inheritance implementation) must be used
- var status: Int = 0
-
- # Identifier of the class which introduced the attribute
- var id: Int
-
- # Optimize this attribute access
- # * `mproperty` The attribute which is accessed
- # * `recv` The receiver (The object) of the access
- protected fun optimize(mproperty: MAttribute, recv: MutableInstance)
- do
- if mproperty.intro_mclassdef.mclass.positions_attributes[recv.mtype.as(MClassType).mclass] != -1 then
- # if this attribute class has an unique position for this receiver, then use direct access
- offset = mproperty.absolute_offset
- status = 1
- else
- # Otherwise, perfect hashing must be used
- id = mproperty.intro_mclassdef.mclass.vtable.id
- offset = mproperty.offset
- status = 2
- end
- end
-end
-
-redef class AAttrExpr
- redef fun expr(v)
- do
- # TODO : a workaround for now
- if not v isa VirtualMachine then return super
-
- var recv = v.expr(self.n_expr)
- if recv == null then return null
- if recv.mtype isa MNullType then fatal(v, "Receiver is null")
- var mproperty = self.mproperty.as(not null)
-
- assert recv isa MutableInstance
- if status == 0 then optimize(mproperty, recv)
-
- var i: Instance
- if status == 1 then
- # SST
- i = v.read_attribute_sst(recv.internal_attributes, offset)
- else
- # PH
- i = v.read_attribute_ph(recv.internal_attributes, recv.vtable.internal_vtable, recv.vtable.mask, id, offset)
- end
-
- # If we get a `MInit` value, throw an error
- if i == v.initialization_value then
- v.fatal("Uninitialized attribute {mproperty.name}")
- abort
- end
-
- return i
- end
-end
-
-redef class AAttrAssignExpr
- redef fun stmt(v)
- do
- # TODO : a workaround for now
- if not v isa VirtualMachine then
- super
- return
- end
-
- var recv = v.expr(self.n_expr)
- if recv == null then return
- if recv.mtype isa MNullType then fatal(v, "Receiver is null")
- var i = v.expr(self.n_value)
- if i == null then return
- var mproperty = self.mproperty.as(not null)
-
- assert recv isa MutableInstance
- if status == 0 then optimize(mproperty, recv)
-
- if status == 1 then
- v.write_attribute_sst(recv.internal_attributes, offset, i)
- else
- v.write_attribute_ph(recv.internal_attributes, recv.vtable.internal_vtable,
- recv.vtable.mask, id, offset, i)
- end
- end
-end
-
-# Add informations to optimize some method calls
-redef class CallSite
- # Position of the method in virtual table
- #
- # The relative position of this MMethod if perfect hashing is used,
- # The absolute position of this MMethod if SST is used
- var offset: Int
-
- # Indicate the status of the optimization for this node
- #
- # 0: default value
- # 1: SST (direct access) can be used
- # 2: PH (multiple inheritance implementation) must be used
- var status: Int = 0
-
- # Identifier of the class which introduced the MMethod
- var id: Int
-
- # Optimize a method dispatch,
- # If this method is always at the same position in virtual table, we can use direct access,
- # Otherwise we must use perfect hashing
- fun optimize(recv: Instance)
- do
- if mproperty.intro_mclassdef.mclass.positions_methods[recv.mtype.as(MClassType).mclass] != -1 then
- offset = mproperty.absolute_offset
- status = 1
- else
- offset = mproperty.offset
- status = 2
- end
- id = mproperty.intro_mclassdef.mclass.vtable.id
- end
-end
-
-redef class AIsaExpr
- # Identifier of the target class type
- var id: Int
-
- # If the Cohen test is used, the position of the target id in vtable
- var position: Int
-
- # Indicate the status of the optimization for this node
- #
- # 0 : the default value
- # 1 : this test can be implemented with direct access
- # 2 : this test must be implemented with perfect hashing
- var status: Int = 0
-
- redef fun expr(v)
- do
- # TODO : a workaround for now
- if not v isa VirtualMachine then return super
-
- var recv = v.expr(self.n_expr)
- if recv == null then return null
-
- if status == 0 then optimize(v, recv.mtype, self.cast_type.as(not null))
- var mtype = v.unanchor_type(self.cast_type.as(not null))
-
- # If this test can be optimized, directly call appropriate subtyping methods
- if status == 1 and recv.mtype isa MClassType then
- # Direct access
- return v.bool_instance(v.inter_is_subtype_sst(id, position, recv.mtype.as(MClassType).mclass.vtable.internal_vtable))
- else if status == 2 and recv.mtype isa MClassType then
- # Perfect hashing
- return v.bool_instance(v.inter_is_subtype_ph(id, recv.vtable.mask, recv.mtype.as(MClassType).mclass.vtable.internal_vtable))
- else
- # Use the slow path (default)
- return v.bool_instance(v.is_subtype(recv.mtype, mtype))
- end
- end
-
- # Optimize a `AIsaExpr`
- # `source` the source type of the expression
- # `target` the target type of the subtyping test
- private fun optimize(v: VirtualMachine, source: MType, target: MType)
- do
- # If the source class and target class are not classic classes (non-generics) then return
- if not source isa MClassType or not target isa MClassType or target isa MGenericType then
- return
- end
-
- if not target.mclass.loaded then return
-
- # Try to get the position of the target type in source's structures
- var value = source.mclass.positions_methods.get_or_null(target.mclass)
-
- if value != null then
- if value != -1 then
- # Store informations for Cohen test
- position = target.mclass.color
- status = 1
- else
- # We use perfect hashing
- status = 2
- end
- end
- id = target.mclass.vtable.id
- end
-end
-
-redef class AAsCastExpr
- # Identifier of the target class type
- var id: Int
-
- # If the Cohen test is used, the position of the target id in vtable
- var position: Int
-
- # Indicate the status of the optimization for this node
- #
- # 0 : the default value
- # 1 : this test can be implemented with direct access
- # 2 : this test must be implemented with perfect hashing
- var status: Int = 0
-
- redef fun expr(v)
- do
- # TODO : a workaround for now
- if not v isa VirtualMachine then return super
-
- var recv = v.expr(self.n_expr)
- if recv == null then return null
-
- if status == 0 then optimize(v, recv.mtype, self.mtype.as(not null))
-
- var mtype = self.mtype.as(not null)
- var amtype = v.unanchor_type(mtype)
-
- var res: Bool
- if status == 1 and recv.mtype isa MClassType then
- # Direct access
- res = v.inter_is_subtype_sst(id, position, recv.mtype.as(MClassType).mclass.vtable.internal_vtable)
- else if status == 2 and recv.mtype isa MClassType then
- # Perfect hashing
- res = v.inter_is_subtype_ph(id, recv.vtable.mask, recv.mtype.as(MClassType).mclass.vtable.internal_vtable)
- else
- # Use the slow path (default)
- res = v.is_subtype(recv.mtype, amtype)
- end
-
- if not res then
- fatal(v, "Cast failed. Expected `{amtype}`, got `{recv.mtype}`")
- end
- return recv
- end
-
- # Optimize a `AAsCastExpr`
- # * `source` the source type of the expression
- # * `target` the target type of the subtyping test
- private fun optimize(v: VirtualMachine, source: MType, target: MType)
- do
- # If the source class and target class are not classic classes (non-generics) then return
- if not source isa MClassType or not target isa MClassType or target isa MGenericType then
- return
- end
-
- if not target.mclass.loaded then return
-
- # Try to get the position of the target type in source's structures
- var value = source.mclass.positions_methods.get_or_null(target.mclass)
-
- if value != null then
- if value != -1 then
- # Store informations for Cohen test
- position = target.mclass.color
- status = 1
- else
- # We use perfect hashing
- status = 2
- end
- end
- id = target.mclass.vtable.id
- end
-end