Преглед на файлове

Cleanup and add missing file

Yentl Van Tendeloo преди 7 години
родител
ревизия
e510659417
променени са 4 файла, в които са добавени 195 реда и са изтрити 11 реда
  1. 191 0
      kernel/modelverse_kernel/jit.py
  2. 1 4
      kernel/modelverse_kernel/main.py
  3. 2 6
      kernel/modelverse_kernel/request_handler.py
  4. 1 1
      wrappers/modelverse_SCCD.py

+ 191 - 0
kernel/modelverse_kernel/jit.py

@@ -0,0 +1,191 @@
+import math
+import keyword
+from collections import defaultdict
+import modelverse_kernel.primitives as primitive_functions
+
+class ModelverseJit(object):
+    """A high-level interface to the modelverse JIT compiler."""
+    def __init__(self):
+        self.todo_entry_points = set()
+        self.jitted_parameters = {}
+        self.jit_globals = {
+            'PrimitiveFinished' : primitive_functions.PrimitiveFinished,
+        }
+        # jitted_entry_points maps body ids to values in jit_globals.
+        self.jitted_entry_points = {}
+        # global_functions maps global value names to body ids.
+        self.global_functions = {}
+        # global_functions_inv maps body ids to global value names.
+        self.global_functions_inv = {}
+        # jitted_function_aliases maps body ids to known aliases.
+        self.jitted_function_aliases = defaultdict(set)
+        self.jit_count = 0
+        self.compilation_dependencies = {}
+
+    def mark_entry_point(self, body_id):
+        """Marks the node with the given identifier as a function entry point."""
+        if body_id not in self.jitted_entry_points:
+            self.todo_entry_points.add(body_id)
+
+    def is_jittable_entry_point(self, body_id):
+        """Tells if the node with the given identifier is a function entry point that
+           has not been marked as non-jittable. This only returns `True` if the JIT
+           is enabled and the function entry point has been marked jittable, or if
+           the function has already been compiled."""
+        return ((body_id in self.todo_entry_points) or (body_id in self.jitted_entry_points))
+
+    def generate_name(self, infix, suggested_name=None):
+        """Generates a new name or picks the suggested name if it is still
+           available."""
+        if suggested_name is not None \
+            and suggested_name not in self.jit_globals \
+            and not keyword.iskeyword(suggested_name):
+            self.jit_count += 1
+            return suggested_name
+        else:
+            function_name = 'jit_%s%d' % (infix, self.jit_count)
+            self.jit_count += 1
+            return function_name
+
+    def generate_function_name(self, body_id, suggested_name=None):
+        """Generates a new function name or picks the suggested name if it is still
+           available."""
+        if suggested_name is None:
+            suggested_name = self.get_global_name(body_id)
+
+        return self.generate_name('func', suggested_name)
+
+    def register_global(self, body_id, global_name):
+        """Associates the given body id with the given global name."""
+        self.global_functions[global_name] = body_id
+        self.global_functions_inv[body_id] = global_name
+
+    def get_global_name(self, body_id):
+        """Gets the name of the global function with the given body id.
+           Returns None if no known global exists with the given id."""
+        if body_id in self.global_functions_inv:
+            return self.global_functions_inv[body_id]
+        else:
+            return None
+
+    def get_global_body_id(self, global_name):
+        """Gets the body id of the global function with the given name.
+           Returns None if no known global exists with the given name."""
+        if global_name in self.global_functions:
+            return self.global_functions[global_name]
+        else:
+            return None
+
+    def register_compiled(self, body_id, compiled_function, function_name=None):
+        """Registers a compiled entry point with the JIT."""
+        # Get the function's name.
+        actual_function_name = self.generate_function_name(body_id, function_name)
+        # Map the body id to the given parameter list.
+        self.jitted_entry_points[body_id] = actual_function_name
+        self.jit_globals[actual_function_name] = compiled_function
+        if function_name is not None:
+            self.register_global(body_id, function_name)
+
+        if body_id in self.todo_entry_points:
+            self.todo_entry_points.remove(body_id)
+
+    def __lookup_compiled_body_impl(self, body_id):
+        """Looks up a compiled function by body id. Returns a matching function,
+           or None if no function was found."""
+        if body_id is not None and body_id in self.jitted_entry_points:
+            return self.jit_globals[self.jitted_entry_points[body_id]]
+        else:
+            return None
+
+    def __lookup_external_body_impl(self, global_name, body_id):
+        """Looks up an external function by global name. Returns a matching function,
+           or None if no function was found."""
+        if global_name is not None and self.compiled_function_lookup is not None:
+            result = self.compiled_function_lookup(global_name)
+            if result is not None and body_id is not None:
+                self.register_compiled(body_id, result, global_name)
+
+            return result
+        else:
+            return None
+
+    def lookup_compiled_body(self, body_id):
+        """Looks up a compiled function by body id. Returns a matching function,
+           or None if no function was found."""
+        result = self.__lookup_compiled_body_impl(body_id)
+        if result is not None:
+            return result
+        else:
+            global_name = self.get_global_name(body_id)
+            return self.__lookup_external_body_impl(global_name, body_id)
+
+    def lookup_compiled_function(self, global_name):
+        """Looks up a compiled function by global name. Returns a matching function,
+           or None if no function was found."""
+        body_id = self.get_global_body_id(global_name)
+        result = self.__lookup_compiled_body_impl(body_id)
+        if result is not None:
+            return result
+        else:
+            return self.__lookup_external_body_impl(global_name, body_id)
+
+    def jit_signature(self, body_id):
+        """Acquires the signature for the given body id node, which consists of the
+           parameter variables, parameter name and a flag that tells if the given function
+           is mutable."""
+        if body_id not in self.jitted_parameters:
+            signature_id, = yield [("RRD", [body_id, "body"])]
+            signature_id = signature_id[0]
+            param_set_id, is_mutable = yield [
+                ("RD", [signature_id, "params"]),
+                ("RD", [signature_id, "mutable"])]
+            if param_set_id is None:
+                self.jitted_parameters[body_id] = ([], [], is_mutable)
+            else:
+                param_name_ids, = yield [("RDK", [param_set_id])]
+                param_names = yield [("RV", [n]) for n in param_name_ids]
+                #NOTE Patch up strange links...
+                param_names = [i for i in param_names if i is not None]
+                param_vars = yield [("RD", [param_set_id, k]) for k in param_names]
+
+                #NOTE that variables might not be in the correct order, as we just read them out!
+                lst = sorted([(name, var) for name, var in zip(param_names, param_vars)])
+                param_vars = [i[1] for i in lst]
+                param_names = [i[0] for i in lst]
+                self.jitted_parameters[body_id] = (param_vars, param_names, is_mutable)
+
+        raise primitive_functions.PrimitiveFinished(self.jitted_parameters[body_id])
+
+    def check_jittable(self, body_id, suggested_name=None):
+        """Checks if the function with the given body id is obviously non-jittable. If it's
+           non-jittable, then a `JitCompilationFailedException` exception is thrown."""
+        if body_id is None:
+            raise ValueError('body_id cannot be None: ' + suggested_name)
+
+    def jit_define_function(self, function_name, function_def):
+        """Converts the given tree-IR function definition to Python code, defines it,
+           and extracts the resulting function."""
+        # The comment below makes pylint shut up about our (hopefully benign) use of exec here.
+        # pylint: disable=I0011,W0122
+        if self.jit_code_log_function is not None:
+            self.jit_code_log_function(function_def)
+
+        # Convert the function definition to Python code, and compile it.
+        code_generator = tree_ir.PythonGenerator()
+        function_def.generate_python_def(code_generator)
+        source_map_name = self.get_source_map_name(function_name)
+        if source_map_name is not None:
+            self.jit_globals[source_map_name] = code_generator.source_map_builder.source_map
+        exec(str(code_generator), self.jit_globals)
+
+        # Extract the compiled function from the JIT global state.
+        return self.jit_globals[function_name]
+
+    def jit_delete_function(self, function_name):
+        """Deletes the function with the given function name."""
+        del self.jit_globals[function_name]
+import modelverse_kernel.primitives as primitive_functions
+
+class JitCompilationFailedException(Exception):
+    """A type of exception that is raised when the jit fails to compile a function."""
+    pass

+ 1 - 4
kernel/modelverse_kernel/main.py

@@ -121,10 +121,7 @@ class ModelverseKernel(object):
         signatures = yield [("RDN", [primitives, f]) for f in keys]
         bodies = yield [("RD", [f, "body"]) for f in signatures]
         for i in range(len(keys)):
-            self.jit.register_compiled(
-                bodies[i],
-                getattr(source, function_names[i]),
-                function_names[i])
+            self.jit.register_compiled(bodies[i], getattr(source, function_names[i]), function_names[i])
 
     def print_instruction(self, inst, indent, nested_indent=None):
         """

+ 2 - 6
kernel/modelverse_kernel/request_handler.py

@@ -88,10 +88,6 @@ class RequestHandler(object):
                     # Looks like we're done here.
                     return None
 
-    def set_finished_requests_flag(self):
-        """Sets the finished_requests flag in the top-of-stack tuple."""
-        self.generator_stack[-1]["finished_requests"] = True
-
     def push_generator(self, gen):
         """Pushes a new generator onto the stack."""
         dd = defaultdict(lambda : None)
@@ -124,7 +120,7 @@ class RequestHandler(object):
                 # The list of requests might be empty now. If so, then flag this
                 # batch of requests as finished.
                 if not requests:
-                    self.set_finished_requests_flag()
+                    self.generator_stack[-1]["finished_requests"] = True
 
                 # Handle the request.
                 self.handlers[elem[0]](elem[1])
@@ -139,7 +135,7 @@ class RequestHandler(object):
 
         # We couldn't find a known request in the batch of requests, so we might as well
         # handle them all at once then.
-        self.set_finished_requests_flag()
+        self.generator_stack[-1]["finished_requests"] = True
         return requests
 
     def execute_call(self, request_args):

+ 1 - 1
wrappers/modelverse_SCCD.py

@@ -1,7 +1,7 @@
 """
 Generated by Statechart compiler by Glenn De Jonghe, Joeri Exelmans, Simon Van Mierlo, and Yentl Van Tendeloo (for the inspiration)
 
-Date:   Tue Apr 24 13:10:29 2018
+Date:   Tue Apr 24 13:24:04 2018
 
 Model author: Yentl Van Tendeloo
 Model name:   MvK Server