X-Git-Url: http://nitlanguage.org diff --git a/src/vm.nit b/src/vm.nit index b5e5bb8..b253533 100644 --- a/src/vm.nit +++ b/src/vm.nit @@ -17,18 +17,17 @@ # Implementation of the Nit virtual machine module vm -intrude import interpreter::naive_interpreter -import model_utils +import interpreter::naive_interpreter import perfect_hashing redef class ModelBuilder - redef fun run_naive_interpreter(mainmodule: MModule, arguments: Array[String]) + fun run_virtual_machine(mainmodule: MModule, arguments: Array[String]) do var time0 = get_time self.toolcontext.info("*** NITVM STARTING ***", 1) var interpreter = new VirtualMachine(self, mainmodule, arguments) - init_naive_interpreter(interpreter, mainmodule) + interpreter.start(mainmodule) var time1 = get_time self.toolcontext.info("*** NITVM STOPPING : {time1-time0} ***", 2) @@ -41,13 +40,34 @@ class VirtualMachine super NaiveInterpreter # Perfect hashing and perfect numbering var ph: Perfecthashing = new Perfecthashing - # Handles memory and garbage collection + # Handles memory allocated in C var memory_manager: MemoryManager = new MemoryManager - # Subtyping test for the virtual machine + # The unique instance of the `MInit` value + var initialization_value: Instance is noinit + + init + do + var init_type = new MInitType(mainmodule.model) + initialization_value = new MutableInstance(init_type) + super + end + + # Runtime subtyping test redef fun is_subtype(sub, sup: MType): Bool do + if sub == sup then return true + var anchor = self.frame.arguments.first.mtype.as(MClassType) + + # `sub` or `sup` are formal or virtual types, resolve them to concrete types + if sub isa MParameterType or sub isa MVirtualType then + sub = sub.resolve_for(anchor.mclass.mclass_type, anchor, mainmodule, false) + end + if sup isa MParameterType or sup isa MVirtualType then + sup = sup.resolve_for(anchor.mclass.mclass_type, anchor, mainmodule, false) + end + var sup_accept_null = false if sup isa MNullableType then sup_accept_null = true @@ -67,11 +87,6 @@ class VirtualMachine super NaiveInterpreter end # Now the case of direct null and nullable is over - # An unfixed formal type can only accept itself - if sup isa MParameterType or sup isa MVirtualType then - return sub == sup - end - if sub isa MParameterType or sub isa MVirtualType then sub = sub.anchor_to(mainmodule, anchor) # Manage the second layer of null/nullable @@ -90,35 +105,36 @@ class VirtualMachine super NaiveInterpreter assert sup isa MClassType - # Create the sup vtable if not create + # `sub` and `sup` can be discovered inside a Generic type during the subtyping test if not sup.mclass.loaded then create_class(sup.mclass) - - # Sub can be discovered inside a Generic type during the subtyping test if not sub.mclass.loaded then create_class(sub.mclass) - if anchor == null then anchor = sub - if sup isa MGenericType then - var sub2 = sub.supertype_to(mainmodule, anchor, sup.mclass) - assert sub2.mclass == sup.mclass - - for i in [0..sup.mclass.arity[ do - var sub_arg = sub2.arguments[i] - var sup_arg = sup.arguments[i] - var res = is_subtype(sub_arg, sup_arg) - - if res == false then return false - end - return true - end - + # For now, always use perfect hashing for subtyping test var super_id = sup.mclass.vtable.id var mask = sub.mclass.vtable.mask - return inter_is_subtype(super_id, mask, sub.mclass.vtable.internal_vtable) + var res = inter_is_subtype_ph(super_id, mask, sub.mclass.vtable.internal_vtable) + if res == false then return false + # sub and sup can be generic types, each argument of generics has to be tested + + if not sup isa MGenericType then return true + var sub2 = sub.supertype_to(mainmodule, anchor, sup.mclass) + + # Test each argument of a generic by recursive calls + for i in [0..sup.mclass.arity[ do + var sub_arg = sub2.arguments[i] + var sup_arg = sup.arguments[i] + var res2 = is_subtype(sub_arg, sup_arg) + if res2 == false then return false + end + return true end # Subtyping test with perfect hashing - private fun inter_is_subtype(id: Int, mask:Int, vtable: Pointer): Bool `{ + # * `id` is the identifier of the target class + # * `mask` is the perfect hashing mask of the receiver class + # * `vtable` is the pointer to the virtual table of the receiver class + fun inter_is_subtype_ph(id: Int, mask:Int, vtable: Pointer): Bool `{ // hv is the position in hashtable int hv = id & mask; @@ -129,6 +145,17 @@ class VirtualMachine super NaiveInterpreter return *offset == id; `} + # Subtyping test with Cohen test (direct access) + # * `id` is the identifier of the target class + # * `mask` is the absolute position of the target identifier in the virtual table + # * `vtable` is the pointer to the virtual table of the receiver class + fun inter_is_subtype_sst(id: Int, position: Int, vtable: Pointer): Bool `{ + // Direct access to the position given in parameter + int tableid = (long unsigned int)((long int *)vtable)[position]; + + return id == tableid; + `} + # Redef init_instance to simulate the loading of a class redef fun init_instance(recv: Instance) do @@ -136,50 +163,135 @@ class VirtualMachine super NaiveInterpreter recv.vtable = recv.mtype.as(MClassType).mclass.vtable - assert(recv isa MutableInstance) + assert recv isa MutableInstance - recv.internal_attributes = init_internal_attributes(null_instance, recv.mtype.as(MClassType).mclass.all_mattributes(mainmodule, none_visibility).length) + recv.internal_attributes = init_internal_attributes(initialization_value, recv.mtype.as(MClassType).mclass.mattributes.length) super end + # Associate a `PrimitiveInstance` to its `VTable` + redef fun init_instance_primitive(recv: Instance) + do + if not recv.mtype.as(MClassType).mclass.loaded then create_class(recv.mtype.as(MClassType).mclass) + + recv.vtable = recv.mtype.as(MClassType).mclass.vtable + end + + # Create a virtual table for this `MClass` if not already done + redef fun get_primitive_class(name: String): MClass + do + var mclass = super + + if not mclass.loaded then create_class(mclass) + + return mclass + end + # Initialize the internal representation of an object (its attribute values) - private fun init_internal_attributes(null_instance: Instance, size: Int): Pointer + # `init_instance` is the initial value of attributes + private fun init_internal_attributes(init_instance: Instance, size: Int): Pointer import Array[Instance].length, Array[Instance].[] `{ Instance* attributes = malloc(sizeof(Instance) * size); int i; for(i=0; i 0 then previous_parent = superclasses[0] for parent in superclasses do if not parent.loaded then parent.make_vt(v) # Get the number of introduced methods and attributes for this class - var methods = 0 - var attributes = 0 + var methods = parent.intro_mmethods.length + var attributes = parent.intro_mattributes.length - for p in parent.intro_mproperties(none_visibility) do - if p isa MMethod then methods += 1 - if p isa MAttribute then - attributes += 1 - end - end + # Updates `mmethods` and `mattributes` + mmethods.add_all(parent.intro_mmethods) + mattributes.add_all(parent.intro_mattributes) ids.push(parent.vtable.id) nb_methods.push(methods) nb_attributes.push(attributes) - # Update `positions_attributes` and `positions_methods` in `parent` - update_positions(offset_attributes, offset_methods, parent) + # Update `positions_attributes` and `positions_methods` in `parent`. + # If the position is invariant for this parent, store this position + # else store a special value (-1) + var pos_attr = -1 + var pos_meth = -1 + + if previous_parent.as(not null).positions_attributes[parent] == offset_attributes then pos_attr = offset_attributes + if previous_parent.as(not null).positions_methods[parent] == offset_methods then pos_meth = offset_methods + + parent.update_positions(pos_attr, pos_meth, self) offset_attributes += attributes offset_methods += methods + offset_methods += 2 # Because each block starts with an id and the delta end # When all super-classes have their identifiers and vtables, allocate current one allocate_vtable(v, ids, nb_methods, nb_attributes, offset_attributes, offset_methods) loaded = true + + # Set the absolute position of the identifier of this class in the virtual table + color = offset_methods - 2 + # The virtual table now needs to be filled with pointer to methods + superclasses.add(self) + for cl in superclasses do + fill_vtable(v, vtable.as(not null), cl) + end end # Allocate a single vtable - # `ids : Array of superclasses identifiers - # `nb_methods : Array which contain the number of introduced methods for each class in ids - # `nb_attributes : Array which contain the number of introduced attributes for each class in ids - # `offset_attributes : Offset from the beginning of the table of the group of attributes - # `offset_methods : Offset from the beginning of the table of the group of methods + # * `ids : Array of superclasses identifiers + # * `nb_methods : Array which contain the number of introduced methods for each class in ids + # * `nb_attributes : Array which contain the number of introduced attributes for each class in ids + # * `offset_attributes : Offset from the beginning of the table of the group of attributes + # * `offset_methods : Offset from the beginning of the table of the group of methods private fun allocate_vtable(v: VirtualMachine, ids: Array[Int], nb_methods: Array[Int], nb_attributes: Array[Int], offset_attributes: Int, offset_methods: Int) do @@ -316,17 +507,39 @@ redef class MClass var self_methods = 0 var nb_introduced_attributes = 0 - # For self attributes, fixing offsets - var relative_offset = 0 - for p in intro_mproperties(none_visibility) do - if p isa MMethod then self_methods += 1 - if p isa MAttribute then - nb_introduced_attributes += 1 - p.offset = relative_offset - relative_offset += 1 + # Fixing offsets for self attributes and methods + var relative_offset_attr = 0 + var relative_offset_meth = 0 + + # Update `intro_mmethods` and `intro_mattributes` + # For each MClassdef this MClass has + for classdef in mclassdefs do + # For each property this MClassdef introduce + for p in classdef.intro_mproperties do + # Collect properties and fixing offsets + if p isa MMethod then + self_methods += 1 + p.offset = relative_offset_meth + p.absolute_offset = offset_methods + relative_offset_meth + relative_offset_meth += 1 + + intro_mmethods.add(p) + end + if p isa MAttribute then + nb_introduced_attributes += 1 + p.offset = relative_offset_attr + p.absolute_offset = offset_attributes + relative_offset_attr + relative_offset_attr += 1 + + intro_mattributes.add(p) + end end end + # Updates caches with introduced attributes of `self` class + mattributes.add_all(intro_mattributes) + mmethods.add_all(intro_mmethods) + nb_methods_total.add_all(nb_methods) nb_methods_total.push(self_methods) @@ -334,19 +547,33 @@ redef class MClass nb_attributes_total.push(nb_introduced_attributes) # Save the offsets of self class - offset_attributes += nb_introduced_attributes - offset_methods += self_methods update_positions(offset_attributes, offset_methods, self) # Since we have the number of attributes for each class, calculate the delta - var d = calculate_delta(nb_attributes_total) - vtable.internal_vtable = v.memory_manager.init_vtable(ids_total, nb_methods_total, d, vtable.mask) + var deltas = calculate_delta(nb_attributes_total) + vtable.internal_vtable = v.memory_manager.init_vtable(ids_total, nb_methods_total, deltas, vtable.mask) + end + + # Fill the vtable with methods of `self` class + # * `v` : Current instance of the VirtualMachine + # * `table` : the table of self class, will be filled with its methods + private fun fill_vtable(v:VirtualMachine, table: VTable, cl: MClass) + do + var methods = new Array[MMethodDef] + for m in cl.intro_mmethods do + # `propdef` is the most specific implementation for this MMethod + var propdef = m.lookup_first_definition(v.mainmodule, self.intro.bound_mtype) + methods.push(propdef) + end + + # Call a method in C to put propdefs of self methods in the vtables + v.memory_manager.put_methods(vtable.internal_vtable, vtable.mask, cl.vtable.id, methods) end # Computes delta for each class # A delta represents the offset for this group of attributes in the object - # `nb_attributes` : number of attributes for each class (classes are linearized from Object to current) - # return deltas for each class + # *`nb_attributes` : number of attributes for each class (classes are linearized from Object to current) + # * return deltas for each class private fun calculate_delta(nb_attributes: Array[Int]): Array[Int] do var deltas = new Array[Int] @@ -365,7 +592,9 @@ redef class MClass private fun superclasses_ordering(v: VirtualMachine): Array[MClass] do var superclasses = new Array[MClass] - superclasses.add_all(ancestors) + + # Add all superclasses of `self` + superclasses.add_all(self.in_hierarchy(v.mainmodule).greaters) var res = new Array[MClass] if superclasses.length > 1 then @@ -380,8 +609,8 @@ redef class MClass end # A kind of Depth-First-Search for superclasses ordering - # `v` : the current executed instance of VirtualMachine - # `res` : Result Array, ie current superclasses ordering + # *`v` : the current executed instance of VirtualMachine + # * `res` : Result Array, ie current superclasses ordering private fun dfs(v: VirtualMachine, res: Array[MClass]): Array[MClass] do # Add this class at the beginning @@ -397,7 +626,7 @@ redef class MClass for cl in direct_parents do # If we never have visited this class if not res.has(cl) then - var properties_length = cl.all_mproperties(v.mainmodule, none_visibility).length + var properties_length = cl.mmethods.length + cl.mattributes.length if properties_length > max then max = properties_length prefix = cl @@ -436,19 +665,31 @@ redef class MClass return res end - # Update positions of self class in `parent` - # `attributes_offset`: absolute offset of introduced attributes - # `methods_offset`: absolute offset of introduced methods - private fun update_positions(attributes_offsets: Int, methods_offset:Int, parent: MClass) + # Update positions of the class `cl` + # * `attributes_offset`: absolute offset of introduced attributes + # * `methods_offset`: absolute offset of introduced methods + private fun update_positions(attributes_offsets: Int, methods_offset:Int, cl: MClass) do - parent.positions_attributes[self] = attributes_offsets - parent.positions_methods[self] = methods_offset + positions_attributes[cl] = attributes_offsets + positions_methods[cl] = methods_offset end end redef class MAttribute - # Represents the relative offset of this attribute in the runtime instance + # Relative offset of this attribute in the runtime instance + # (beginning of the block of its introducing class) + var offset: Int + + # Absolute offset of this attribute in the runtime instance (beginning of the attribute table) + var absolute_offset: Int +end + +redef class MMethod + # Relative offset of this method in the virtual table (from the beginning of the block) var offset: Int + + # Absolute offset of this method in the virtual table (from the beginning of the vtable) + var absolute_offset: Int end # Redef MutableInstance to improve implementation of attributes in objects @@ -458,6 +699,32 @@ redef class MutableInstance var internal_attributes: Pointer end +# Redef to associate an `Instance` to its `VTable` +redef class Instance + + # Associate a runtime instance to its virtual table which contains methods, types etc. + var vtable: nullable VTable +end + +# Is the type of the initial value inside attributes +class MInitType + super MType + + redef var model: Model + + redef fun to_s do return "InitType" + redef fun as_nullable do return self + redef fun need_anchor do return false + redef fun resolve_for(mtype, anchor, mmodule, cleanup_virtual) do return self + redef fun can_resolve_for(mtype, anchor, mmodule) do return true + + redef fun collect_mclassdefs(mmodule) do return new HashSet[MClassDef] + + redef fun collect_mclasses(mmodule) do return new HashSet[MClass] + + redef fun collect_mtypes(mmodule) do return new HashSet[MClassType] +end + # A VTable contains the virtual method table for the dispatch # and informations to perform subtyping tests class VTable @@ -474,10 +741,6 @@ class VTable var classname: String is noinit end -redef class Instance - var vtable: nullable VTable -end - # Handle memory, used for allocate virtual table and associated structures class MemoryManager @@ -540,4 +803,29 @@ class MemoryManager return vtable; `} + + # Put implementation of methods of a class in `vtable` + # * `vtable` : Pointer to the C-virtual table + # * `mask` : perfect-hashing mask of the class corresponding to the vtable + # * `id` : id of the target class + # * `methods` : array of MMethodDef of the target class + fun put_methods(vtable: Pointer, mask: Int, id: Int, methods: Array[MMethodDef]) + import Array[MMethodDef].length, Array[MMethodDef].[] `{ + + // Get the area to fill with methods by a sequence of perfect hashing + int hv = mask & id; + long unsigned int *pointer = (long unsigned int*)(((long unsigned int *)vtable)[-hv]); + + // pointer+2 is the beginning of the area for methods implementation + int length = Array_of_MMethodDef_length(methods); + long unsigned int *area = (pointer + 2); + int i; + + for(i=0; i