X-Git-Url: http://nitlanguage.org diff --git a/src/vm.nit b/src/vm.nit index 4dc29e9..1f6d574 100644 --- a/src/vm.nit +++ b/src/vm.nit @@ -17,7 +17,7 @@ # Implementation of the Nit virtual machine module vm -intrude import naive_interpreter +import interpreter::naive_interpreter import model_utils import perfect_hashing @@ -28,7 +28,7 @@ redef class ModelBuilder 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,9 +41,19 @@ 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 + # 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 + # Subtyping test for the virtual machine redef fun is_subtype(sub, sup: MType): Bool do @@ -96,7 +106,6 @@ class VirtualMachine super NaiveInterpreter # 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 @@ -136,49 +145,130 @@ 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.all_mattributes(mainmodule, none_visibility).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) @@ -268,34 +406,46 @@ redef class MClass for p in parent.intro_mproperties(none_visibility) do if p isa MMethod then methods += 1 - if p isa MAttribute then - attributes += 1 - end + if p isa MAttribute then attributes += 1 end 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 + # 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 @@ -317,14 +467,21 @@ redef class MClass var self_methods = 0 var nb_introduced_attributes = 0 - # For self attributes, fixing offsets - var relative_offset = 0 + # Fixing offsets for self attributes and methods + var relative_offset_attr = 0 + var relative_offset_meth = 0 for p in intro_mproperties(none_visibility) do - if p isa MMethod then self_methods += 1 + 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 + end if p isa MAttribute then nb_introduced_attributes += 1 - p.offset = relative_offset - relative_offset += 1 + p.offset = relative_offset_attr + p.absolute_offset = offset_attributes + relative_offset_attr + relative_offset_attr += 1 end end @@ -335,19 +492,35 @@ 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_mproperties(none_visibility) do + if m isa MMethod then + # `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 + 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] @@ -361,19 +534,107 @@ redef class MClass return deltas 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) + # 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] + superclasses.add_all(ancestors) + + 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.all_mproperties(v.mainmodule, none_visibility).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 - 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 @@ -383,6 +644,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 @@ -399,10 +686,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 @@ -465,4 +748,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