# This file is part of NIT ( http://www.nitlanguage.org ). # # Copyright 2015 Julien Pagès # # 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 virtual_machine redef class VirtualMachine # Add optimization of the method dispatch redef fun callsite(callsite: nullable CallSite, arguments: Array[Instance]): nullable Instance do return send_optimize(callsite.as(not null), arguments) 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 #TODO : we need recompilations here callsite.status = 0 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 var position = recv.mtype.as(MClassType).mclass.get_position_attributes(mproperty.intro_mclassdef.mclass) if position > 0 then # if this attribute class has an unique position for this receiver, then use direct access offset = position + mproperty.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 #TODO : we need recompilations here status = 0 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 #TODO : we need recompilations here status = 0 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 var position = recv.mtype.as(MClassType).mclass.get_position_methods(mproperty.intro_mclassdef.mclass) if position > 0 then offset = position + mproperty.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 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.abstract_loaded then return # If the value is positive, the target class has an invariant position in source's structures var value = source.mclass.get_position_methods(target.mclass) if value > 0 then # `value - 2` is the position of the target identifier in source vtable position = value - 2 status = 1 else # We use perfect hashing status = 2 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 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 # If the value is positive, the target class has an invariant position in source's structures var value = source.mclass.get_position_methods(target.mclass) if value > 0 then # `value - 2` is the position of the target identifier in source vtable position = value - 2 status = 1 else # We use perfect hashing status = 2 end id = target.mclass.vtable.id end end