Browse Source

Porting over primitives to the new infrastructure

Yentl Van Tendeloo 7 years ago
parent
commit
5b37a96dac
2 changed files with 452 additions and 0 deletions
  1. 2 0
      kernel/modelverse_kernel/compiled.py
  2. 450 0
      kernel/modelverse_kernel/new_primitives.py

+ 2 - 0
kernel/modelverse_kernel/compiled.py

@@ -1,6 +1,7 @@
 from modelverse_kernel.primitives import PrimitiveFinished
 import time
 
+"""
 def get_superclasses(a, b, **remainder):
     model, name = a, b
     model_dict, tm_dict, name_value = yield [("RD", [a, "model"]), 
@@ -275,3 +276,4 @@ def instantiate_node(a, b, c, **remainder):
     yield [("CD", [typing, name, b])]
 
     raise PrimitiveFinished(name_node)
+"""

+ 450 - 0
kernel/modelverse_kernel/new_primitives.py

@@ -0,0 +1,450 @@
+import time as python_time
+import json
+import sys
+
+class PrimitiveFinished(Exception):
+    """Exception to indicate the result value of a primitive, as a return cannot be used."""
+    def __init__(self, value):
+        Exception.__init__(self)
+        self.result = value
+
+class InterpretedFunctionFinished(Exception):
+    """Exception to indicate the result value of an interpreted function, as a return
+       cannot be used."""
+    def __init__(self, value):
+        Exception.__init__(self)
+        self.result = value
+
+class SleepKernel(Exception):
+    """Exception to indicate the kernel to sleep for some time."""
+    def __init__(self, timeout, interruptable):
+        Exception.__init__(self)
+        self.timeout = timeout
+        self.interruptable = interruptable
+
+# Functions annotated with __exception_return use the JIT's calling convention instead of
+# the kernel's: returns are handled by throwing a PrimitiveFinished exception; the caller's
+# returnvalue is not modified.
+#
+# ### Rationale for __exception_return
+#
+# __exception_return is a useful mechanism because it allows us to have a __call_function
+# implementation that has O(1) state read overhead. A previous implementation of
+# __call_function checked if the caller's frame had been popped whenever
+# ModelverseKernel.execute_yield threw a StopIteration exception. However, that incurs O(n) overhead
+# _per call,_ where n is the number of StopIteration exceptions that are thrown during the call.
+# O(n) is pretty bad, but this actually becomes O(n * m) when m calls to __call_function are
+# nested. And that's just not acceptable.
+# __exception_return requires kernel support, but I think the complexity gains are well worth it;
+# I reckon JIT-to-interpreter switches aren't going to get a whole lot cheaper than this.
+EXCEPTION_RETURN_KEY = "__exception_return"
+"""A dictionary key for functions which request that the kernel throw a InterpretedFunctionFinished
+   exception with the return value instead of injecting the return value in the caller's frame."""
+
+def integer_subtraction(a, b, **remainder):
+    if 'value' not in a:
+        a['value'], = yield [("RV", [a['id']])]
+    if 'value' not in b:
+        b['value'], = yield [("RV", [b['id']])]
+    result = {'value': a['value'] - b['value']}
+    raise PrimitiveFinished(result)
+
+def integer_addition(a, b, **remainder):
+    if 'value' not in a:
+        a['value'], = yield [("RV", [a['id']])]
+    if 'value' not in b:
+        b['value'], = yield [("RV", [b['id']])]
+    result = {'value': a['value'] + b['value']}
+    raise PrimitiveFinished(result)
+
+def integer_multiplication(a, b, **remainder):
+    if 'value' not in a:
+        a['value'], = yield [("RV", [a['id']])]
+    if 'value' not in b:
+        b['value'], = yield [("RV", [b['id']])]
+    result = {'value': a['value'] * b['value']}
+    raise PrimitiveFinished(result)
+
+def integer_division(a, b, **remainder):
+    if 'value' not in a:
+        a['value'], = yield [("RV", [a['id']])]
+    if 'value' not in b:
+        b['value'], = yield [("RV", [b['id']])]
+    result = {'value': int(a['value']) // b['value']}
+    raise PrimitiveFinished(result)
+
+def integer_lt(a, b, **remainder):
+    if 'value' not in a:
+        a['value'], = yield [("RV", [a['id']])]
+    if 'value' not in b:
+        b['value'], = yield [("RV", [b['id']])]
+    result = {'value': a['value'] < b['value']}
+    raise PrimitiveFinished(result)
+
+def bool_and(a, b, **remainder):
+    if 'value' not in a:
+        a['value'], = yield [("RV", [a['id']])]
+    if 'value' not in b:
+        b['value'], = yield [("RV", [b['id']])]
+    result = {'value': a['value'] and b['value']}
+    raise PrimitiveFinished(result)
+
+def bool_or(a, b, **remainder):
+    if 'value' not in a:
+        a['value'], = yield [("RV", [a['id']])]
+    if 'value' not in b:
+        b['value'], = yield [("RV", [b['id']])]
+    result = {'value': a['value'] or b['value']}
+    raise PrimitiveFinished(result)
+
+def bool_not(a, **remainder):
+    if 'value' not in a:
+        a['value'], = yield [("RV", [a['id']])]
+    result = {'value': not a['value']}
+    raise PrimitiveFinished(result)
+
+def float_subtraction(a, b, **remainder):
+    if 'value' not in a:
+        a['value'], = yield [("RV", [a['id']])]
+    if 'value' not in b:
+        b['value'], = yield [("RV", [b['id']])]
+    result = {'value': a['value'] - b['value']}
+    raise PrimitiveFinished(result)
+
+def float_addition(a, b, **remainder):
+    if 'value' not in a:
+        a['value'], = yield [("RV", [a['id']])]
+    if 'value' not in b:
+        b['value'], = yield [("RV", [b['id']])]
+    result = {'value': a['value'] + b['value']}
+    raise PrimitiveFinished(result)
+
+def float_multiplication(a, b, **remainder):
+    if 'value' not in a:
+        a['value'], = yield [("RV", [a['id']])]
+    if 'value' not in b:
+        b['value'], = yield [("RV", [b['id']])]
+    result = {'value': a['value'] * b['value']}
+    raise PrimitiveFinished(result)
+
+def float_division(a, b, **remainder):
+    if 'value' not in a:
+        a['value'], = yield [("RV", [a['id']])]
+    if 'value' not in b:
+        b['value'], = yield [("RV", [b['id']])]
+    result = {'value': float(a['value']) / float(b['value'])}
+    raise PrimitiveFinished(result)
+
+def float_lt(a, b, **remainder):
+    if 'value' not in a:
+        a['value'], = yield [("RV", [a['id']])]
+    if 'value' not in b:
+        b['value'], = yield [("RV", [b['id']])]
+    result = {'value': a['value'] < b['value']}
+    raise PrimitiveFinished(result)
+
+def string_join(a, b, **remainder):
+    if 'value' not in a:
+        a['value'], = yield [("RV", [a['id']])]
+    if 'value' not in b:
+        b['value'], = yield [("RV", [b['id']])]
+    result = {'value': str(a['value']) + str(b['value'])}
+    raise PrimitiveFinished(result)
+
+def string_split(a, b, **remainder):
+    # TODO make non-primitive, though compiled
+    a_value, b_value = yield [("RV", [a]), ("RV", [b])]
+    result = a_value.split(b_value)
+    elems = yield [("CN", [])] + [("CNV", [v]) for v in result]
+    new_val = elems[0]
+    yield [("CD", [new_val, i, v]) for i, v in enumerate(elems[1:])]
+    raise PrimitiveFinished(new_val)
+
+    if 'value' not in a:
+        a['value'], = yield [("RV", [a['id']])]
+    if 'value' not in b:
+        b['value'], = yield [("RV", [b['id']])]
+    result = a['value'].split(b['value'])
+    elems = yield [("CN", [])] + [("CNV", [v]) for v in result]
+    new_val = elems[0]
+    yield [("CD", [new_val, i, v]) for i, v in enumerate(elems[1:])]
+    raise PrimitiveFinished({'id': new_val})
+
+def string_get(a, b, **remainder):
+    a_value, b_value =  yield [("RV", [a]), ("RV", [b])]
+    result, = yield [("CNV", [a_value[b_value]])]
+    raise PrimitiveFinished(result)
+
+def string_len(a, **remainder):
+    a_value, = yield [("RV", [a])]
+    result, = yield [("CNV", [len(a_value)])]
+    raise PrimitiveFinished(result)
+
+def value_eq(a, b, **remainder):
+    a_value, b_value =  yield [("RV", [a]), ("RV", [b])]
+    result, = yield [("CNV", [a_value == b_value])]
+    raise PrimitiveFinished(result)
+
+def element_eq(a, b, **remainder):
+    result, = yield [("CNV", [a == b])]
+    raise PrimitiveFinished(result)
+
+def cast_string(a, **remainder):
+    a_value, = yield [("RV", [a])]
+    if isinstance(a_value, dict):
+        result, = yield [("CNV", [str(a_value["value"])])]
+    else:
+        result, = yield [("CNV", [str(a_value)])]
+    raise PrimitiveFinished(result)
+
+def cast_float(a, **remainder):
+    a_value, = yield [("RV", [a])]
+    result, = yield [("CNV", [float(a_value)])]
+    raise PrimitiveFinished(result)
+
+def cast_boolean(a, **remainder):
+    a_value, = yield [("RV", [a])]
+    result, = yield [("CNV", [bool(a_value)])]
+    raise PrimitiveFinished(result)
+
+def cast_integer(a, **remainder):
+    a_value, = yield [("RV", [a])]
+    result, = yield [("CNV", [int(a_value)])]
+    raise PrimitiveFinished(result)
+
+def cast_value(a, **remainder):
+    a_value, = yield [("RV", [a])]
+    if isinstance(a_value, dict):
+        # Action or type
+        value = a_value["value"]
+    else:
+        value = json.dumps(a_value)
+    result, = yield [("CNV", [value])]
+    raise PrimitiveFinished(result)
+
+def cast_id(a, **remainder):
+    result, = yield [("CNV", ["%s" % (a)])]
+    raise PrimitiveFinished(result)
+
+def list_insert(a, b, c, **remainder):
+    # TODO make non-primitive, though compiled
+    a_outgoing, c_value = yield [("RO", [a]), ("RV", [c])]
+    links = yield [("RD", [a, i]) for i in range(c_value, len(a_outgoing))] + \
+                  [("RDE", [a, i]) for i in range(c_value, len(a_outgoing))]
+
+    if sys.version_info[0] < 3:
+        values = links[:len(links)/2]
+        edges = links[len(links)/2:]
+    else:
+        values = links[:len(links) // 2]
+        edges = links[len(links) // 2:]
+
+    yield [("CD", [a, c_value, b])] + \
+          [("CD", [a, c_value + 1 + index, value]) for index, value in enumerate(values)] + \
+          [("DE", [i]) for i in edges]
+    raise PrimitiveFinished(a)
+
+def list_delete(a, b, **remainder):
+    # TODO make non-primitive, though compiled
+    a_outgoing, b_value = yield [("RO", [a]), ("RV", [b])]
+    links = yield [("RD", [a, i]) for i in range(b_value, len(a_outgoing))] + \
+                  [("RDE", [a, i]) for i in range(b_value, len(a_outgoing))]
+
+    if sys.version_info[0] < 3:
+        values = links[:len(links) / 2]
+        edges = links[len(links) / 2:]
+    else:
+        values = links[:len(links) // 2]
+        edges = links[len(links) // 2:]
+
+    yield [("CD", [a, b_value + index, value]) for index, value in enumerate(values[1:])] + \
+          [("DE", [i]) for i in edges]
+    raise PrimitiveFinished(a)
+
+def dict_add_fast(a, b, c, **remainder):
+    # TODO deprecate, as dict_add is now also efficient
+    v, = yield [("RV", [b])]
+    yield [("CD", [a, v, c])]
+    raise PrimitiveFinished(a)
+
+def dict_delete(a, b, **remainder):
+    b_value, = yield [("RV", [b])]
+    edge, = yield [("RDE", [a, b_value])]
+    if edge is None:
+        print("Failed dict_delete for value '%s'!" % b_value)
+        keys, = yield [("RDK", [a])]
+        keys = yield [("RV", [i]) for i in keys]
+        print("Keys: " + str(keys))
+        raise Exception()
+    yield [("DE", [edge])]
+    raise PrimitiveFinished(a)
+
+def dict_delete_node(a, b, **remainder):
+    edge, = yield [("RDNE", [a, b])]
+    if edge is None:
+        print("Failed dict_delete_node!")
+    yield [("DE", [edge])]
+    raise PrimitiveFinished(a)
+
+def dict_read(a, b, **remainder):
+    b_value, = yield [("RV", [b])]
+    result, = yield [("RD", [a, b_value])]
+    raise PrimitiveFinished(result)
+
+def dict_read_edge(a, b, **remainder):
+    b_value, = yield [("RV", [b])]
+    result, = yield [("RDE", [a, b_value])]
+    raise PrimitiveFinished(result)
+
+def dict_read_node(a, b, **remainder):
+    result, = yield [("RDN", [a, b])]
+    raise PrimitiveFinished(result)
+
+def dict_in(a, b, **remainder):
+    b_value, = yield [("RV", [b])]
+    value, = yield [("RD", [a, b_value])]
+    is_in = value is not None
+    result, = yield [("CNV", [is_in])]
+    raise PrimitiveFinished(result)
+
+def dict_in_node(a, b, **remainder):
+    value, = yield [("RDN", [a, b])]
+    result, = yield [("CNV", [value is not None])]
+    raise PrimitiveFinished(result)
+
+def dict_keys(a, **remainder):
+    keys, result = yield [("RDK", [a]), ("CN", [])]
+    edges = yield [("CE", [result, result]) for _ in keys]
+    _ = yield [("CE", [edge, key]) for edge, key in zip(edges, keys)]
+    raise PrimitiveFinished(result)
+
+def is_physical_int(a, **remainder):
+    t, = yield [("RV", [a])]
+    try:
+        result, = yield [("CNV", [isinstance(t, int) or isinstance(t, long)])]
+    except NameError:
+        result, = yield [("CNV", [isinstance(t, int)])]
+
+    raise PrimitiveFinished(result)
+
+def is_physical_string(a, **remainder):
+    t, = yield [("RV", [a])]
+    try:
+        result, = yield [("CNV", [isinstance(t, str) or isinstance(t, unicode)])]
+    except NameError:
+        result, = yield [("CNV", [isinstance(t, str)])]
+
+    raise PrimitiveFinished(result)
+
+def is_physical_float(a, **remainder):
+    t, = yield [("RV", [a])]
+    result, = yield [("CNV", [isinstance(t, float)])]
+    raise PrimitiveFinished(result)
+
+def is_physical_boolean(a, **remainder):
+    t, = yield [("RV", [a])]
+    result, = yield [("CNV", [isinstance(t, bool)])]
+    raise PrimitiveFinished(result)
+
+def is_physical_action(a, **remainder):
+    t, = yield [("RV", [a])]
+    result, = yield [("CNV", [isinstance(t, dict) and t["value"] in ["if", "while", "assign", "call", "break", "continue", "return", "resolve", "access", "constant", "global", "declare"]])]
+    raise PrimitiveFinished(result)
+
+def is_physical_none(a, **remainder):
+    t, = yield [("RV", [a])]
+    result, = yield [("CNV", [isinstance(t, dict) and t["value"] == "none"])]
+    raise PrimitiveFinished(result)
+
+def create_node(**remainder):
+    result, = yield [("CN", [])]
+    raise PrimitiveFinished(result)
+
+def create_edge(a, b, **remainder):
+    result, = yield [("CE", [a, b])]
+    raise PrimitiveFinished(result)
+
+def create_value(a, **remainder):
+    a_value, = yield [("RV", [a])]
+    result, = yield [("CNV", [a_value])]
+    raise PrimitiveFinished(result)
+
+def read_nr_out(a, **remainder):
+    outgoing, = yield [("RO", [a])]
+    result, = yield [("CNV", [len(outgoing)])]
+    raise PrimitiveFinished(result)
+
+def read_out(a, b, root, **remainder):
+    outgoing, b_value = yield [("RO", [a]), ("RV", [b])]
+    raise PrimitiveFinished(sorted(outgoing)[b_value] if len(outgoing) > b_value else root)
+
+def read_nr_in(a, **remainder):
+    incoming, = yield [("RI", [a])]
+    result, = yield [("CNV", [len(incoming)])]
+    raise PrimitiveFinished(result)
+
+def read_in(a, b, root, **remainder):
+    incoming, b_value = yield [("RI", [a]), ("RV", [b])]
+    raise PrimitiveFinished(sorted(incoming)[b_value] if len(incoming) > b_value else root)
+
+def read_edge_src(a, **remainder):
+    result, = yield [("RE", [a])]
+    raise PrimitiveFinished(result[0])
+
+def read_edge_dst(a, **remainder):
+    result, = yield [("RE", [a])]
+    raise PrimitiveFinished(result[1])
+
+def delete_element(a, **remainder):
+    edge, = yield [("RE", [a])]
+    if edge[0] is None:
+        # Not an edge:
+        yield [("DN", [a])]
+        result, = yield [("CNV", [False])]
+        raise PrimitiveFinished(result)
+    else:
+        yield [("DE", [a])]
+        result, = yield [("CNV", [True])]
+        raise PrimitiveFinished(result)
+
+def read_root(root, **remainder):
+    raise PrimitiveFinished(root)
+
+def is_edge(a, **remainder):
+    edge, = yield [("RE", [a])]
+    result, = yield [("CNV", [edge[0] is not None])]
+    raise PrimitiveFinished(result)
+
+def log(a, **remainder):
+    a_value, = yield [("RV", [a])]
+    print("== LOG == " + str(a_value))
+    raise PrimitiveFinished(a)
+
+def read_taskroot(task_root, **remainder):
+    raise PrimitiveFinished(task_root)
+
+def time(**remainder):
+    a, = yield [("CNV", [python_time.time()])]
+    raise PrimitiveFinished(a)
+
+def hash(a, **remainder):
+    a_value, = yield [("RV", [a])]
+    import hashlib
+    try:
+        b_value = hashlib.sha512(a_value).hexdigest()
+    except TypeError:
+        b_value = hashlib.sha512(a_value.encode()).hexdigest()
+    b, = yield [("CNV", [b_value])]
+    raise PrimitiveFinished(b)
+
+def __sleep(a, b, **remainder):
+    timeout, interruptable = yield [("RV", [a]), ("RV", [b])]
+    yield [("SLEEP", [timeout, interruptable])]
+    raise PrimitiveFinished(a)
+
+def is_error(a, **remainder):
+    if a is None:
+        result, = yield [("CNV", [True])]
+    else:
+        result, = yield [("CNV", [False])]
+    raise PrimitiveFinished(result)