X-Git-Url: http://nitlanguage.org diff --git a/src/vm.nit b/src/vm.nit index 754b8ec..b253533 100644 --- a/src/vm.nit +++ b/src/vm.nit @@ -17,18 +17,17 @@ # Implementation of the Nit virtual machine module vm -intrude import 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,45 +105,57 @@ 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; // Follow the pointer to somewhere in the vtable long unsigned int *offset = (long unsigned int*)(((long int *)vtable)[-hv]); - + // If the pointed value is corresponding to the identifier, the test is true, otherwise false 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,83 +163,185 @@ class VirtualMachine super NaiveInterpreter recv.vtable = recv.mtype.as(MClassType).mclass.vtable - assert(recv isa MutableInstance) - - recv.internal_attributes = init_internal_attributes(null_instance, recv.mtype.as(MClassType).mclass.cached_attributes.length) + assert recv isa MutableInstance + 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 parent.vtable == null then parent.make_vt(v) - + 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 + + # Updates `mmethods` and `mattributes` + mmethods.add_all(parent.intro_mmethods) + mattributes.add_all(parent.intro_mattributes) - 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 - ids.push(parent.vtable.id) nb_methods.push(methods) nb_attributes.push(attributes) + + # 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) + 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 introducted methods for each class in ids - # nb_attributes : Array which contain the number of introducted attributes for each class in ids - private fun allocate_vtable(v: VirtualMachine, ids: Array[Int], nb_methods: Array[Int], nb_attributes: Array[Int]) + # * `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 vtable = new VTable var idc = new Array[Int] @@ -298,35 +505,75 @@ redef class MClass var nb_attributes_total = new Array[Int] var self_methods = 0 - var self_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 - self_attributes += 1 - p.offset = relative_offset - relative_offset += 1 - cached_attributes.push(p) + var nb_introduced_attributes = 0 + + # 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) nb_attributes_total.add_all(nb_attributes) - nb_attributes_total.push(self_attributes) + nb_attributes_total.push(nb_introduced_attributes) - # 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) + # Save the offsets of self class + update_positions(offset_attributes, offset_methods, self) + + # Since we have the number of attributes for each class, calculate the delta + 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] @@ -339,20 +586,145 @@ redef class MClass return deltas end + + # Order superclasses of self + # Return the order of superclasses in runtime structures of this class + private fun superclasses_ordering(v: VirtualMachine): Array[MClass] + do + var superclasses = new Array[MClass] + + # Add all superclasses of `self` + superclasses.add_all(self.in_hierarchy(v.mainmodule).greaters) + + var res = new Array[MClass] + if superclasses.length > 1 then + # Starting at self + var ordering = self.dfs(v, res) + + return ordering + else + # There is no super-class, self is Object + return superclasses + end + end + + # A kind of Depth-First-Search for 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 + res.insert(self, 0) + + var direct_parents = self.in_hierarchy(v.mainmodule).direct_greaters.to_a + + if direct_parents.length > 1 then + # Prefix represents the class which has the most properties + # we try to choose it in first to reduce the number of potential recompilations + var prefix = null + var max = -1 + for cl in direct_parents do + # If we never have visited this class + if not res.has(cl) then + var properties_length = cl.mmethods.length + cl.mattributes.length + if properties_length > max then + max = properties_length + prefix = cl + end + end + end + + if prefix != null then + # Add the prefix class ordering at the beginning of our sequence + var prefix_res = new Array[MClass] + prefix_res = prefix.dfs(v, prefix_res) + + # Then we recurse on other classes + for cl in direct_parents do + if cl != prefix then + res = new Array[MClass] + res = cl.dfs(v, res) + + for cl_res in res do + if not prefix_res.has(cl_res) then prefix_res.push(cl_res) + end + end + end + res = prefix_res + end + + res.push(self) + else + if direct_parents.length > 0 then + res = direct_parents.first.dfs(v, res) + end + end + + if not res.has(self) then res.push(self) + + return res + end + + # 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 + 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 redef class MutableInstance - + # C-array to store pointers to attributes of this Object 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 @@ -369,15 +741,11 @@ 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 # Allocate and fill a virtual table - fun init_vtable(ids: Array[Int], nb_methods: Array[Int], nb_attributes: Array[Int], mask: Int): Pointer + fun init_vtable(ids: Array[Int], nb_methods: Array[Int], nb_attributes: Array[Int], mask: Int): Pointer do # Allocate in C current virtual table var res = intern_init_vtable(ids, nb_methods, nb_attributes, mask) @@ -386,7 +754,7 @@ class MemoryManager end # Construct virtual tables with a bi-dimensional layout - private fun intern_init_vtable(ids: Array[Int], nb_methods: Array[Int], deltas: Array[Int], mask: Int): Pointer + private fun intern_init_vtable(ids: Array[Int], nb_methods: Array[Int], deltas: Array[Int], mask: Int): Pointer import Array[Int].length, Array[Int].[] `{ // Allocate and fill current virtual table @@ -396,7 +764,7 @@ class MemoryManager for(i = 0; i