First files for the Nit virtual machine
authorJulien Pagès <julien.projet@gmail.com>
Wed, 11 Jun 2014 21:57:49 +0000 (23:57 +0200)
committerJulien Pagès <julien.projet@gmail.com>
Fri, 13 Jun 2014 13:42:08 +0000 (15:42 +0200)
A virtual table is now constructed for each MClass
The virtual table is constructed according to the specifications.

The perfect hashtable is at negative offsets.
It contains pointer to blocks of (id, methods) which
are at positive offsets of the virtual table.

Signed-off-by: Julien Pagès <julien.projet@gmail.com>

src/nitvm.nit [new file with mode: 0644]
src/vm.nit [new file with mode: 0644]

diff --git a/src/nitvm.nit b/src/nitvm.nit
new file mode 100644 (file)
index 0000000..9a62208
--- /dev/null
@@ -0,0 +1,62 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2012 Jean Privat <jean@pryen.org>
+#
+# 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.
+
+# The Nit virtual machine launcher
+module nitvm
+
+import vm
+
+# Create a tool context to handle options and paths
+var toolcontext = new ToolContext
+toolcontext.tooldescription = "Usage: nitvm [OPTION]... <file.nit>...\nExecutes Nit programs with a virtual machine."
+# Add an option "-o" to enable compatibility with the tests.sh script
+var opt = new OptionString("compatibility (does nothing)", "-o")
+toolcontext.option_context.add_option(opt)
+var opt_mixins = new OptionArray("Additional modules to min-in", "-m")
+toolcontext.option_context.add_option(opt_mixins)
+# We do not add other options, so process them now!
+toolcontext.process_options(args)
+
+# We need a model to collect stufs
+var model = new Model
+
+# Add a model builder to parse files
+var modelbuilder = new ModelBuilder(model, toolcontext.as(not null))
+
+var arguments = toolcontext.option_context.rest
+var progname = arguments.first
+
+# Here we load and process all modules passed on the command line
+var mmodules = modelbuilder.parse([progname])
+mmodules.add_all modelbuilder.parse(opt_mixins.value)
+modelbuilder.run_phases
+
+if toolcontext.opt_only_metamodel.value then exit(0)
+
+var mainmodule: nullable MModule
+
+# Here we launch the interpreter on the main module
+if mmodules.length == 1 then
+       mainmodule = mmodules.first
+else
+       mainmodule = new MModule(model, null, mmodules.first.name, mmodules.first.location)
+       mainmodule.set_imported_mmodules(mmodules)
+end
+
+var self_mm = mainmodule.as(not null)
+var self_args = arguments.as(not null)
+
+modelbuilder.run_naive_interpreter(self_mm, self_args)
diff --git a/src/vm.nit b/src/vm.nit
new file mode 100644 (file)
index 0000000..3c8f048
--- /dev/null
@@ -0,0 +1,314 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2014 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.
+
+# Implementation of the Nit virtual machine
+module vm
+
+intrude import naive_interpreter
+import model_utils
+import perfect_hashing
+
+redef class ModelBuilder
+       redef fun run_naive_interpreter(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)
+
+               var time1 = get_time
+               self.toolcontext.info("*** NITVM STOPPING : {time1-time0} ***", 2)
+       end
+end
+
+# A virtual machine based on the naive_interpreter
+class VirtualMachine super NaiveInterpreter
+
+       # Perfect hashing and perfect numbering
+       var ph: Perfecthashing = new Perfecthashing
+
+       # Handles memory and garbage collection
+       var memory_manager: MemoryManager = new MemoryManager
+
+       # Subtyping test for the virtual machine
+       redef fun is_subtype(sub, sup: MType): Bool
+       do
+               var anchor = self.frame.arguments.first.mtype.as(MClassType)
+               var sup_accept_null = false
+               if sup isa MNullableType then
+                       sup_accept_null = true
+                       sup = sup.mtype
+               else if sup isa MNullType then
+                       sup_accept_null = true
+               end
+
+               # Can `sub` provides null or not?
+               # Thus we can match with `sup_accept_null`
+               # Also discard the nullable marker if it exists
+               if sub isa MNullableType then
+                       if not sup_accept_null then return false
+                       sub = sub.mtype
+               else if sub isa MNullType then
+                       return sup_accept_null
+               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
+                       if sub isa MNullableType then
+                               if not sup_accept_null then return false
+                               sub = sub.mtype
+                       else if sub isa MNullType then
+                               return sup_accept_null
+                       end
+               end
+
+               assert sub isa MClassType
+
+               # `sup` accepts only null
+               if sup isa MNullType then return false
+
+               assert sup isa MClassType
+
+               # Create the sup vtable if not create
+               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
+
+               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)
+       end
+
+       # Subtyping test with perfect hashing
+       private fun inter_is_subtype(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;
+       `}
+       
+       # Redef init_instance to simulate the loading of a class
+       redef fun init_instance(recv: Instance)
+       do
+               if not recv.mtype.as(MClassType).mclass.loaded then create_class(recv.mtype.as(MClassType).mclass)
+               super
+
+               recv.vtable = recv.mtype.as(MClassType).mclass.vtable
+       end
+       
+       # Creates the runtime structures for this class
+       fun create_class(mclass: MClass) do     mclass.make_vt(self)
+end
+
+redef class MClass
+       # A reference to the virtual table of this class
+       var vtable: nullable VTable
+
+       # True when the class is effectively loaded by the vm, false otherwise
+       var loaded: Bool = false
+
+       # Allocates a VTable for this class and gives it an id
+       private fun make_vt(v: VirtualMachine)
+       do
+               if loaded then return
+
+               # Superclasses contains all superclasses except self
+               var superclasses = new Array[MClass]
+               superclasses.add_all(ancestors)
+               superclasses.remove(self)
+               v.mainmodule.linearize_mclasses(superclasses)
+
+               # Make_vt for super-classes
+               var ids = new Array[Int]
+               var nb_methods = new Array[Int]
+
+               for parent in superclasses do
+                       if parent.vtable == null then parent.make_vt(v)
+                       
+                       # Get the number of introduced methods for this class
+                       var count = 0
+                       var min_visibility = public_visibility
+                       for p in parent.intro_mproperties(min_visibility) do
+                               if p isa MMethod then
+                                       count += 1
+                               end
+                       end
+                       
+                       ids.push(parent.vtable.id)
+                       nb_methods.push(count)
+               end
+               
+               # When all super-classes have their identifiers and vtables, allocate current one
+               allocate_vtable(v, ids, nb_methods)
+               loaded = true
+               # The virtual table now needs to be filled with pointer to methods
+       end
+
+       # Allocate a single vtable
+       #       ids : Array of superclasses identifiers
+       #       nb_methods : Array which contain the number of methods for each class in ids
+       private fun allocate_vtable(v: VirtualMachine, ids: Array[Int], nb_methods: Array[Int])
+       do
+               vtable = new VTable
+               var idc = new Array[Int]
+
+               vtable.mask = v.ph.pnand(ids, 1, idc) - 1
+               vtable.id = idc[0]
+               vtable.classname = name
+
+               # Add current id to Array of super-ids
+               var ids_total = new Array[Int]
+               ids_total.add_all(ids)
+               ids_total.push(vtable.id)
+
+               var nb_methods_total = new Array[Int]
+               var count = 0
+               var min_visibility = public_visibility
+               for p in intro_mproperties(min_visibility) do
+                       if p isa MMethod then
+                               count += 1
+                       end
+               end
+               nb_methods_total.add_all(nb_methods)
+               nb_methods_total.push(count)
+               
+               vtable.internal_vtable = v.memory_manager.init_vtable(ids_total, nb_methods_total, vtable.mask)
+       end
+end
+
+# A VTable contains the virtual method table for the dispatch
+# and informations to perform subtyping tests
+class VTable
+       # The mask to perform perfect hashing
+       var mask: Int
+
+       # Unique identifier given by perfect hashing
+       var id: Int
+
+       # Pointer to the c-allocated area, represents the virtual table
+       var internal_vtable: Pointer
+
+       # The short classname of this class
+       var classname: String
+
+       init do end
+end
+
+redef class Instance
+       
+       var vtable: nullable VTable
+
+       init(mt: MType)
+       do
+               mtype = mt
+
+               # An instance is associated to its class virtual table
+               if mt isa MClassType then
+                       vtable = mt.mclass.vtable
+               end
+       end
+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], mask: Int): Pointer 
+       do
+               # Allocate in C current virtual table
+               var res = intern_init_vtable(ids, nb_methods, mask)
+
+               return res
+       end
+
+       # Construct virtual tables with a bi-dimensional layout
+       private fun intern_init_vtable(ids: Array[Int], nb_methods: Array[Int], mask: Int): Pointer 
+               import Array[Int].length, Array[Int].[] `{
+
+               // Allocate and fill current virtual table
+               int i;
+               int total_size = 0; // total size of this virtual table
+               int nb_classes = Array_of_Int_length(nb_methods);
+               for(i = 0; i<nb_classes; i++) {
+                       /* - One for each method of this class
+                       *  - One for the delta (pointer to attributes)
+                       *  - One for the id 
+                       */
+                       total_size += Array_of_Int__index(nb_methods, i);
+                       total_size += 2;
+               }
+
+               // And the size of the perfect hashtable
+               total_size += mask+1;
+               long unsigned int* vtable = malloc(sizeof(long unsigned int)*total_size);
+               
+               // Initialisation to the first position of the virtual table (ie : Object)
+               long unsigned int *init = vtable + mask + 2;
+               for(i=0; i<total_size; i++)
+                       vtable[i] = (long unsigned int)init;
+
+               // Set the virtual table to its position 0 
+               // ie: after the hashtable
+               vtable = vtable + mask + 1;
+               
+               int current_size = 1;
+               for(i = 0; i < nb_classes; i++) {
+                       /*
+                               vtable[hv] contains a pointer to the group of introducted methods
+                               For each superclasse we have in virtual table :
+                                       (id | delta (attributes) | introduced methods)
+                       */
+                       int hv = mask & Array_of_Int__index(ids, i);
+
+                       vtable[current_size] = Array_of_Int__index(ids, i);
+                       vtable[-hv] = (long unsigned int)&(vtable[current_size]);
+                       
+                       current_size += 2;
+                       current_size += Array_of_Int__index(nb_methods, i);
+               }
+
+               return vtable;
+       `}
+end