|
@@ -1,725 +0,0 @@
|
|
|
-"""Naively converts bytecode IR to tree IR."""
|
|
|
-
|
|
|
-import modelverse_jit.bytecode_ir as bytecode_ir
|
|
|
-import modelverse_jit.tree_ir as tree_ir
|
|
|
-import modelverse_jit.runtime as jit_runtime
|
|
|
-import modelverse_kernel.primitives as primitive_functions
|
|
|
-
|
|
|
-def get_parameter_names(compiled_function):
|
|
|
- """Gets the given compiled function's parameter names."""
|
|
|
- if hasattr(compiled_function, '__code__'):
|
|
|
- return compiled_function.__code__.co_varnames[
|
|
|
- :compiled_function.__code__.co_argcount]
|
|
|
- elif hasattr(compiled_function, '__init__'):
|
|
|
- return get_parameter_names(compiled_function.__init__)[1:]
|
|
|
- else:
|
|
|
- raise ValueError("'compiled_function' must be a function or a type.")
|
|
|
-
|
|
|
-def apply_intrinsic(intrinsic_function, named_args):
|
|
|
- """Applies the given intrinsic to the given sequence of named arguments."""
|
|
|
- param_names = get_parameter_names(intrinsic_function)
|
|
|
- if tuple(param_names) == tuple([n for n, _ in named_args]):
|
|
|
- # Perfect match. Yay!
|
|
|
- return intrinsic_function(**dict(named_args))
|
|
|
- else:
|
|
|
- # We'll have to store the arguments into locals to preserve
|
|
|
- # the order of evaluation.
|
|
|
- stored_args = [(name, tree_ir.StoreLocalInstruction(None, arg)) for name, arg in named_args]
|
|
|
- arg_value_dict = dict([(name, arg.create_load()) for name, arg in stored_args])
|
|
|
- store_instructions = [instruction for _, instruction in stored_args]
|
|
|
- return tree_ir.CompoundInstruction(
|
|
|
- tree_ir.create_block(*store_instructions),
|
|
|
- intrinsic_function(**arg_value_dict))
|
|
|
-
|
|
|
-def retrieve_task_root():
|
|
|
- """Creates an instruction that stores the task_root variable in a local."""
|
|
|
- return tree_ir.StoreLocalInstruction(
|
|
|
- 'task_root', load_task_root())
|
|
|
-
|
|
|
-def load_task_root():
|
|
|
- """Creates an instruction that loads the task_root variable."""
|
|
|
- return tree_ir.LoadIndexInstruction(
|
|
|
- tree_ir.LoadLocalInstruction(jit_runtime.KWARGS_PARAMETER_NAME),
|
|
|
- tree_ir.LiteralInstruction('task_root'))
|
|
|
-
|
|
|
-def load_kernel():
|
|
|
- """Creates an instruction that loads the Modelverse kernel."""
|
|
|
- return tree_ir.LoadIndexInstruction(
|
|
|
- tree_ir.LoadLocalInstruction(jit_runtime.KWARGS_PARAMETER_NAME),
|
|
|
- tree_ir.LiteralInstruction('mvk'))
|
|
|
-
|
|
|
-def create_access(pointer):
|
|
|
- """Creates a tree that loads the given pointer's value."""
|
|
|
- # Accessing a variable is pretty easy. It really just boils
|
|
|
- # down to reading the value corresponding to the 'value' key
|
|
|
- # of the variable.
|
|
|
- #
|
|
|
- # value, = yield [("RD", [returnvalue, "value"])]
|
|
|
- #
|
|
|
- return tree_ir.ReadDictionaryValueInstruction(
|
|
|
- pointer,
|
|
|
- tree_ir.LiteralInstruction('value'))
|
|
|
-
|
|
|
-def create_assign(pointer, value):
|
|
|
- """Creates a tree that assigns the given value to the given pointer."""
|
|
|
- # Assignments work like this:
|
|
|
- #
|
|
|
- # value_link = yield [("RDE", [variable, "value"])]
|
|
|
- # _, _ = yield [("CD", [variable, "value", value]),
|
|
|
- # ("DE", [value_link])]
|
|
|
- #
|
|
|
- variable = tree_ir.StoreLocalInstruction(None, pointer)
|
|
|
- value = tree_ir.StoreLocalInstruction(None, value)
|
|
|
- value_link = tree_ir.StoreLocalInstruction(
|
|
|
- 'value_link',
|
|
|
- tree_ir.ReadDictionaryEdgeInstruction(
|
|
|
- variable.create_load(),
|
|
|
- tree_ir.LiteralInstruction('value')))
|
|
|
-
|
|
|
- return tree_ir.create_block(
|
|
|
- variable,
|
|
|
- value,
|
|
|
- value_link,
|
|
|
- tree_ir.CreateDictionaryEdgeInstruction(
|
|
|
- variable.create_load(),
|
|
|
- tree_ir.LiteralInstruction('value'),
|
|
|
- value.create_load()),
|
|
|
- tree_ir.DeleteEdgeInstruction(
|
|
|
- value_link.create_load()))
|
|
|
-
|
|
|
-def create_input(use_input_function=False):
|
|
|
- """Creates an instruction that pops a value from the input queue."""
|
|
|
- # Possible alternative to the explicit syntax tree: just call the jit_runtime.__get_input
|
|
|
- # function.
|
|
|
- use_input_function = True
|
|
|
- if use_input_function:
|
|
|
- return tree_ir.create_jit_call(
|
|
|
- tree_ir.LoadGlobalInstruction(jit_runtime.GET_INPUT_FUNCTION_NAME),
|
|
|
- [],
|
|
|
- tree_ir.LoadLocalInstruction(jit_runtime.KWARGS_PARAMETER_NAME))
|
|
|
-
|
|
|
- # The plan is to generate this tree:
|
|
|
- #
|
|
|
- # value = None
|
|
|
- # while True:
|
|
|
- # _input = yield [("RD", [task_root, "input"])]
|
|
|
- # value = yield [("RD", [_input, "value"])]
|
|
|
- #
|
|
|
- # if value is None:
|
|
|
- # kwargs['mvk'].success = False # to avoid blocking
|
|
|
- # yield None # nop/interrupt
|
|
|
- # else:
|
|
|
- # break
|
|
|
- #
|
|
|
- # _next = yield [("RD", [_input, "next"])]
|
|
|
- # yield [("CD", [task_root, "input", _next])]
|
|
|
- # yield [("CE", [jit_locals, value])]
|
|
|
- # yield [("DN", [_input])]
|
|
|
-
|
|
|
- task_root = retrieve_task_root()
|
|
|
- _input = tree_ir.StoreLocalInstruction(
|
|
|
- None,
|
|
|
- tree_ir.ReadDictionaryValueInstruction(
|
|
|
- task_root.create_load(),
|
|
|
- tree_ir.LiteralInstruction('input')))
|
|
|
-
|
|
|
- value = tree_ir.StoreLocalInstruction(
|
|
|
- None,
|
|
|
- tree_ir.ReadDictionaryValueInstruction(
|
|
|
- _input.create_load(),
|
|
|
- tree_ir.LiteralInstruction('value')))
|
|
|
-
|
|
|
- raise primitive_functions.PrimitiveFinished(
|
|
|
- tree_ir.CompoundInstruction(
|
|
|
- tree_ir.create_block(
|
|
|
- task_root,
|
|
|
- value.create_store(tree_ir.LiteralInstruction(None)),
|
|
|
- tree_ir.LoopInstruction(
|
|
|
- tree_ir.create_block(
|
|
|
- _input,
|
|
|
- value,
|
|
|
- tree_ir.SelectInstruction(
|
|
|
- tree_ir.BinaryInstruction(
|
|
|
- value.create_load(),
|
|
|
- 'is',
|
|
|
- tree_ir.LiteralInstruction(None)),
|
|
|
- tree_ir.create_block(
|
|
|
- tree_ir.StoreMemberInstruction(
|
|
|
- load_kernel(),
|
|
|
- 'success',
|
|
|
- tree_ir.LiteralInstruction(False)),
|
|
|
- tree_ir.NopInstruction()),
|
|
|
- tree_ir.BreakInstruction()))),
|
|
|
- tree_ir.CreateDictionaryEdgeInstruction(
|
|
|
- task_root.create_load(),
|
|
|
- tree_ir.LiteralInstruction('input'),
|
|
|
- tree_ir.ReadDictionaryValueInstruction(
|
|
|
- _input.create_load(),
|
|
|
- tree_ir.LiteralInstruction('next'))),
|
|
|
- tree_ir.CreateEdgeInstruction(
|
|
|
- tree_ir.LoadLocalInstruction(jit_runtime.LOCALS_NODE_NAME),
|
|
|
- value.create_load()),
|
|
|
- tree_ir.DeleteNodeInstruction(_input.create_load())),
|
|
|
- value.create_load()))
|
|
|
-
|
|
|
-def create_output(output_value):
|
|
|
- """Creates an output instruction that outputs the given value."""
|
|
|
- # The plan is to basically generate this tree:
|
|
|
- #
|
|
|
- # value = <some tree>
|
|
|
- # last_output, last_output_link, new_last_output = \
|
|
|
- # yield [("RD", [task_root, "last_output"]),
|
|
|
- # ("RDE", [task_root, "last_output"]),
|
|
|
- # ("CN", []),
|
|
|
- # ]
|
|
|
- # _, _, _, _ = \
|
|
|
- # yield [("CD", [last_output, "value", value]),
|
|
|
- # ("CD", [last_output, "next", new_last_output]),
|
|
|
- # ("CD", [task_root, "last_output", new_last_output]),
|
|
|
- # ("DE", [last_output_link])
|
|
|
- # ]
|
|
|
- # yield None
|
|
|
-
|
|
|
- value_local = tree_ir.StoreLocalInstruction('value', output_value)
|
|
|
-
|
|
|
- store_task_root = retrieve_task_root()
|
|
|
- last_output = tree_ir.StoreLocalInstruction(
|
|
|
- 'last_output',
|
|
|
- tree_ir.ReadDictionaryValueInstruction(
|
|
|
- store_task_root.create_load(),
|
|
|
- tree_ir.LiteralInstruction('last_output')))
|
|
|
-
|
|
|
- last_output_link = tree_ir.StoreLocalInstruction(
|
|
|
- 'last_output_link',
|
|
|
- tree_ir.ReadDictionaryEdgeInstruction(
|
|
|
- store_task_root.create_load(),
|
|
|
- tree_ir.LiteralInstruction('last_output')))
|
|
|
-
|
|
|
- new_last_output = tree_ir.StoreLocalInstruction(
|
|
|
- 'new_last_output',
|
|
|
- tree_ir.CreateNodeInstruction())
|
|
|
-
|
|
|
- return tree_ir.create_block(
|
|
|
- value_local,
|
|
|
- store_task_root,
|
|
|
- last_output,
|
|
|
- last_output_link,
|
|
|
- new_last_output,
|
|
|
- tree_ir.CreateDictionaryEdgeInstruction(
|
|
|
- last_output.create_load(),
|
|
|
- tree_ir.LiteralInstruction('value'),
|
|
|
- value_local.create_load()),
|
|
|
- tree_ir.CreateDictionaryEdgeInstruction(
|
|
|
- last_output.create_load(),
|
|
|
- tree_ir.LiteralInstruction('next'),
|
|
|
- new_last_output.create_load()),
|
|
|
- tree_ir.CreateDictionaryEdgeInstruction(
|
|
|
- store_task_root.create_load(),
|
|
|
- tree_ir.LiteralInstruction('last_output'),
|
|
|
- new_last_output.create_load()),
|
|
|
- tree_ir.DeleteEdgeInstruction(last_output_link.create_load()),
|
|
|
- tree_ir.NopInstruction())
|
|
|
-
|
|
|
-def create_indirect_call(target, argument_list):
|
|
|
- """Creates an indirect call to the function defined by the node with the id computed
|
|
|
- by the first argument."""
|
|
|
- # Call the __call_function function to run the interpreter, like so:
|
|
|
- #
|
|
|
- # __call_function(function_id, { first_param_name : first_param_val, ... }, **kwargs)
|
|
|
- #
|
|
|
- dict_literal = tree_ir.DictionaryLiteralInstruction(
|
|
|
- [(tree_ir.LiteralInstruction(key), val) for key, val in argument_list])
|
|
|
- return tree_ir.create_jit_call(
|
|
|
- tree_ir.LoadGlobalInstruction(jit_runtime.CALL_FUNCTION_NAME),
|
|
|
- [('function_id', target), ('named_arguments', dict_literal)],
|
|
|
- tree_ir.LoadLocalInstruction(jit_runtime.KWARGS_PARAMETER_NAME))
|
|
|
-
|
|
|
-def create_return(return_value):
|
|
|
- """Creates a return statement that returns the given return value."""
|
|
|
- return tree_ir.ReturnInstruction(
|
|
|
- tree_ir.CompoundInstruction(
|
|
|
- return_value,
|
|
|
- tree_ir.DeleteEdgeInstruction(
|
|
|
- tree_ir.LoadLocalInstruction(jit_runtime.LOCALS_EDGE_NAME))))
|
|
|
-
|
|
|
-def with_debug_info_trace(instruction, debug_info, function_name):
|
|
|
- """Prepends the given instruction with a tracing instruction that prints
|
|
|
- the given debug information and function name."""
|
|
|
- if debug_info is None and function_name is None:
|
|
|
- return instruction
|
|
|
- else:
|
|
|
- return tree_ir.create_block(
|
|
|
- tree_ir.PrintInstruction(
|
|
|
- tree_ir.LiteralInstruction(
|
|
|
- jit_runtime.format_trace_message(
|
|
|
- debug_info, function_name,
|
|
|
- jit_runtime.BASELINE_JIT_ORIGIN_NAME))),
|
|
|
- instruction)
|
|
|
-
|
|
|
-class LocalNameMap(object):
|
|
|
- """A map that converts local variable nodes to identifiers."""
|
|
|
- def __init__(self, local_mapping=None):
|
|
|
- if local_mapping is None:
|
|
|
- local_mapping = {}
|
|
|
- self.local_mapping = local_mapping
|
|
|
-
|
|
|
- def get_local_name(self, local_variable_id):
|
|
|
- """Gets the name for the local variable node with the given id."""
|
|
|
- if local_variable_id not in self.local_mapping:
|
|
|
- self.local_mapping[local_variable_id] = 'local%d' % local_variable_id
|
|
|
- return self.local_mapping[local_variable_id]
|
|
|
-
|
|
|
-class AnalysisState(object):
|
|
|
- """The state of a bytecode analysis call graph."""
|
|
|
- def __init__(self, jit, body_id, task_root, local_mapping, max_instructions=None):
|
|
|
- self.analyzed_instructions = set()
|
|
|
- self.function_vars = set()
|
|
|
- self.local_vars = set()
|
|
|
- self.body_id = body_id
|
|
|
- self.max_instructions = max_instructions
|
|
|
- self.task_root = task_root
|
|
|
- self.jit = jit
|
|
|
- self.local_name_map = LocalNameMap(local_mapping)
|
|
|
- self.function_name = jit.jitted_entry_points[body_id]
|
|
|
- self.enclosing_loop_instruction = None
|
|
|
-
|
|
|
- def register_local_var(self, local_id):
|
|
|
- """Registers the given variable node id as a local."""
|
|
|
- if local_id in self.function_vars:
|
|
|
- raise jit_runtime.JitCompilationFailedException(
|
|
|
- "Local is used as target of function call.")
|
|
|
- self.local_vars.add(local_id)
|
|
|
-
|
|
|
- def register_function_var(self, local_id):
|
|
|
- """Registers the given variable node id as a function."""
|
|
|
- if local_id in self.local_vars:
|
|
|
- raise jit_runtime.JitCompilationFailedException(
|
|
|
- "Local is used as target of function call.")
|
|
|
- self.function_vars.add(local_id)
|
|
|
-
|
|
|
- def analyze(self, instruction):
|
|
|
- """Tries to build an intermediate representation from the instruction with the
|
|
|
- given id."""
|
|
|
- # Check the analyzed_instructions set for instruction_id to avoid
|
|
|
- # infinite loops.
|
|
|
- if instruction in self.analyzed_instructions:
|
|
|
- raise jit_runtime.JitCompilationFailedException(
|
|
|
- 'Cannot jit non-tree instruction graph.')
|
|
|
- elif (self.max_instructions is not None and
|
|
|
- len(self.analyzed_instructions) > self.max_instructions):
|
|
|
- raise jit_runtime.JitCompilationFailedException(
|
|
|
- 'Maximum number of instructions exceeded.')
|
|
|
-
|
|
|
- self.analyzed_instructions.add(instruction)
|
|
|
- instruction_type = type(instruction)
|
|
|
- if instruction_type in self.instruction_analyzers:
|
|
|
- # Analyze the instruction itself.
|
|
|
- outer_result, = yield [
|
|
|
- ("CALL_ARGS", [self.instruction_analyzers[instruction_type], (self, instruction)])]
|
|
|
- if instruction.debug_information is not None:
|
|
|
- if self.jit.tracing_enabled:
|
|
|
- outer_result = with_debug_info_trace(
|
|
|
- outer_result, instruction.debug_information, self.function_name)
|
|
|
- if self.jit.source_maps_enabled:
|
|
|
- outer_result = tree_ir.DebugInfoInstruction(
|
|
|
- outer_result, instruction.debug_information)
|
|
|
-
|
|
|
- # Check if the instruction has a 'next' instruction.
|
|
|
- if instruction.next_instruction is None:
|
|
|
- raise primitive_functions.PrimitiveFinished(outer_result)
|
|
|
- else:
|
|
|
- next_result, = yield [
|
|
|
- ("CALL_ARGS", [self.analyze, (instruction.next_instruction,)])]
|
|
|
- raise primitive_functions.PrimitiveFinished(
|
|
|
- tree_ir.CompoundInstruction(
|
|
|
- outer_result,
|
|
|
- next_result))
|
|
|
- else:
|
|
|
- raise jit_runtime.JitCompilationFailedException(
|
|
|
- "Unknown instruction type: '%s'" % type(instruction))
|
|
|
-
|
|
|
- def analyze_all(self, instruction_ids):
|
|
|
- """Tries to compile a list of IR trees from the given list of instruction ids."""
|
|
|
- results = []
|
|
|
- for inst in instruction_ids:
|
|
|
- analyzed_inst, = yield [("CALL_ARGS", [self.analyze, (inst,)])]
|
|
|
- results.append(analyzed_inst)
|
|
|
-
|
|
|
- raise primitive_functions.PrimitiveFinished(results)
|
|
|
-
|
|
|
- def analyze_return(self, instruction):
|
|
|
- """Tries to analyze the given 'return' instruction."""
|
|
|
- if instruction.value is None:
|
|
|
- raise primitive_functions.PrimitiveFinished(
|
|
|
- create_return(
|
|
|
- tree_ir.EmptyInstruction()))
|
|
|
- else:
|
|
|
- retval, = yield [("CALL_ARGS", [self.analyze, (instruction.value,)])]
|
|
|
- raise primitive_functions.PrimitiveFinished(
|
|
|
- create_return(retval))
|
|
|
-
|
|
|
- def analyze_if(self, instruction):
|
|
|
- """Tries to analyze the given 'if' instruction."""
|
|
|
- if instruction.else_clause is None:
|
|
|
- (cond_r, true_r), = yield [
|
|
|
- ("CALL_ARGS",
|
|
|
- [self.analyze_all,
|
|
|
- ([instruction.condition, instruction.if_clause],)])]
|
|
|
- false_r = tree_ir.EmptyInstruction()
|
|
|
- else:
|
|
|
- (cond_r, true_r, false_r), = yield [
|
|
|
- ("CALL_ARGS",
|
|
|
- [self.analyze_all,
|
|
|
- ([instruction.condition, instruction.if_clause, instruction.else_clause],)])]
|
|
|
-
|
|
|
- raise primitive_functions.PrimitiveFinished(
|
|
|
- tree_ir.SelectInstruction(
|
|
|
- tree_ir.ReadValueInstruction(cond_r),
|
|
|
- true_r,
|
|
|
- false_r))
|
|
|
-
|
|
|
- def analyze_while(self, instruction):
|
|
|
- """Tries to analyze the given 'while' instruction."""
|
|
|
- # Analyze the condition.
|
|
|
- cond_r, = yield [("CALL_ARGS", [self.analyze, (instruction.condition,)])]
|
|
|
- # Store the old enclosing loop on the stack, and make this loop the
|
|
|
- # new enclosing loop.
|
|
|
- old_loop_instruction = self.enclosing_loop_instruction
|
|
|
- self.enclosing_loop_instruction = instruction
|
|
|
- body_r, = yield [("CALL_ARGS", [self.analyze, (instruction.body,)])]
|
|
|
- # Restore hte old enclosing loop.
|
|
|
- self.enclosing_loop_instruction = old_loop_instruction
|
|
|
- if self.jit.nop_insertion_enabled:
|
|
|
- create_loop_body = lambda check, body: tree_ir.create_block(
|
|
|
- check,
|
|
|
- body_r,
|
|
|
- tree_ir.NopInstruction())
|
|
|
- else:
|
|
|
- create_loop_body = tree_ir.CompoundInstruction
|
|
|
-
|
|
|
- raise primitive_functions.PrimitiveFinished(
|
|
|
- tree_ir.LoopInstruction(
|
|
|
- create_loop_body(
|
|
|
- tree_ir.SelectInstruction(
|
|
|
- tree_ir.ReadValueInstruction(cond_r),
|
|
|
- tree_ir.EmptyInstruction(),
|
|
|
- tree_ir.BreakInstruction()),
|
|
|
- body_r)))
|
|
|
-
|
|
|
- def analyze_constant(self, instruction):
|
|
|
- """Tries to analyze the given 'constant' (literal) instruction."""
|
|
|
- raise primitive_functions.PrimitiveFinished(
|
|
|
- tree_ir.LiteralInstruction(instruction.constant_id))
|
|
|
-
|
|
|
- def analyze_output(self, instruction):
|
|
|
- """Tries to analyze the given 'output' instruction."""
|
|
|
- value_val, = yield [("CALL_ARGS", [self.analyze, (instruction.value,)])]
|
|
|
- raise primitive_functions.PrimitiveFinished(create_output(value_val))
|
|
|
-
|
|
|
- def analyze_input(self, _):
|
|
|
- """Tries to analyze the given 'input' instruction."""
|
|
|
- raise primitive_functions.PrimitiveFinished(create_input(self.jit.input_function_enabled))
|
|
|
-
|
|
|
- def analyze_resolve(self, instruction):
|
|
|
- """Tries to analyze the given 'resolve' instruction."""
|
|
|
- # To resolve a variable, we'll do something along the
|
|
|
- # lines of:
|
|
|
- #
|
|
|
- # if 'local_var' in locals():
|
|
|
- # tmp = local_var
|
|
|
- # else:
|
|
|
- # _globals, = yield [("RD", [task_root, "globals"])]
|
|
|
- # global_var, = yield [("RD", [_globals, var_name])]
|
|
|
- #
|
|
|
- # if global_var is None:
|
|
|
- # raise Exception("Not found as global: %s" % (var_name))
|
|
|
- #
|
|
|
- # tmp = global_var
|
|
|
-
|
|
|
- name = self.local_name_map.get_local_name(instruction.variable.node_id)
|
|
|
-
|
|
|
- if instruction.variable.name is None:
|
|
|
- raise primitive_functions.PrimitiveFinished(
|
|
|
- tree_ir.LoadLocalInstruction(name))
|
|
|
-
|
|
|
- task_root = retrieve_task_root()
|
|
|
- global_var = tree_ir.StoreLocalInstruction(
|
|
|
- 'global_var',
|
|
|
- tree_ir.ReadDictionaryValueInstruction(
|
|
|
- tree_ir.ReadDictionaryValueInstruction(
|
|
|
- task_root.create_load(),
|
|
|
- tree_ir.LiteralInstruction('globals')),
|
|
|
- tree_ir.LiteralInstruction(instruction.variable.name)))
|
|
|
-
|
|
|
- err_block = tree_ir.SelectInstruction(
|
|
|
- tree_ir.BinaryInstruction(
|
|
|
- global_var.create_load(),
|
|
|
- 'is',
|
|
|
- tree_ir.LiteralInstruction(None)),
|
|
|
- tree_ir.RaiseInstruction(
|
|
|
- tree_ir.CallInstruction(
|
|
|
- tree_ir.LoadGlobalInstruction('Exception'),
|
|
|
- [tree_ir.LiteralInstruction(
|
|
|
- jit_runtime.GLOBAL_NOT_FOUND_MESSAGE_FORMAT % instruction.variable.name)
|
|
|
- ])),
|
|
|
- tree_ir.EmptyInstruction())
|
|
|
-
|
|
|
- raise primitive_functions.PrimitiveFinished(
|
|
|
- tree_ir.SelectInstruction(
|
|
|
- tree_ir.LocalExistsInstruction(name),
|
|
|
- tree_ir.LoadLocalInstruction(name),
|
|
|
- tree_ir.CompoundInstruction(
|
|
|
- tree_ir.create_block(
|
|
|
- task_root,
|
|
|
- global_var,
|
|
|
- err_block),
|
|
|
- global_var.create_load())))
|
|
|
-
|
|
|
- def analyze_declare(self, instruction):
|
|
|
- """Tries to analyze the given 'declare' function."""
|
|
|
- self.register_local_var(instruction.variable.node_id)
|
|
|
-
|
|
|
- name = self.local_name_map.get_local_name(instruction.variable.node_id)
|
|
|
-
|
|
|
- # The following logic declares a local:
|
|
|
- #
|
|
|
- # if 'local_name' not in locals():
|
|
|
- # local_name, = yield [("CN", [])]
|
|
|
- # yield [("CE", [LOCALS_NODE_NAME, local_name])]
|
|
|
-
|
|
|
- raise primitive_functions.PrimitiveFinished(
|
|
|
- tree_ir.SelectInstruction(
|
|
|
- tree_ir.LocalExistsInstruction(name),
|
|
|
- tree_ir.EmptyInstruction(),
|
|
|
- tree_ir.create_new_local_node(
|
|
|
- name,
|
|
|
- tree_ir.LoadLocalInstruction(jit_runtime.LOCALS_NODE_NAME))))
|
|
|
-
|
|
|
- def analyze_global(self, instruction):
|
|
|
- """Tries to analyze the given 'global' (declaration) instruction."""
|
|
|
- # To declare a variable, we'll do something along the
|
|
|
- # lines of:
|
|
|
- #
|
|
|
- # _globals, = yield [("RD", [task_root, "globals"])]
|
|
|
- # global_var, = yield [("RD", [_globals, var_name])]
|
|
|
- #
|
|
|
- # if global_var is not None:
|
|
|
- # yield [("DE", [global_var])]
|
|
|
- # global_var, = yield [("CN", [])]
|
|
|
- # yield [("CD", [_globals, var_name, global_var])]
|
|
|
- #
|
|
|
- # tmp = global_var
|
|
|
-
|
|
|
- task_root = retrieve_task_root()
|
|
|
- _globals = tree_ir.StoreLocalInstruction(
|
|
|
- '_globals',
|
|
|
- tree_ir.ReadDictionaryValueInstruction(
|
|
|
- task_root.create_load(),
|
|
|
- tree_ir.LiteralInstruction('globals')))
|
|
|
-
|
|
|
- global_var = tree_ir.StoreLocalInstruction(
|
|
|
- 'global_var',
|
|
|
- tree_ir.ReadDictionaryEdgeInstruction(
|
|
|
- _globals.create_load(),
|
|
|
- tree_ir.LiteralInstruction(instruction.variable.name)))
|
|
|
-
|
|
|
- raise primitive_functions.PrimitiveFinished(
|
|
|
- tree_ir.CompoundInstruction(
|
|
|
- tree_ir.create_block(
|
|
|
- task_root,
|
|
|
- _globals,
|
|
|
- global_var,
|
|
|
- tree_ir.SelectInstruction(
|
|
|
- tree_ir.BinaryInstruction(
|
|
|
- global_var.create_load(),
|
|
|
- 'is not',
|
|
|
- tree_ir.LiteralInstruction(None)),
|
|
|
- tree_ir.create_block(
|
|
|
- tree_ir.DeleteEdgeInstruction(
|
|
|
- global_var.create_load())),
|
|
|
- tree_ir.EmptyInstruction()),
|
|
|
- global_var.create_store(
|
|
|
- tree_ir.CreateNodeInstruction()),
|
|
|
- tree_ir.CreateDictionaryEdgeInstruction(
|
|
|
- _globals.create_load(),
|
|
|
- tree_ir.LiteralInstruction(
|
|
|
- instruction.variable.name),
|
|
|
- global_var.create_load())),
|
|
|
- global_var.create_load()))
|
|
|
-
|
|
|
- def analyze_assign(self, instruction):
|
|
|
- """Tries to analyze the given 'assign' instruction."""
|
|
|
- (var_r, value_r), = yield [
|
|
|
- ("CALL_ARGS", [self.analyze_all, ([instruction.pointer, instruction.value],)])]
|
|
|
-
|
|
|
- raise primitive_functions.PrimitiveFinished(create_assign(var_r, value_r))
|
|
|
-
|
|
|
- def analyze_access(self, instruction):
|
|
|
- """Tries to analyze the given 'access' instruction."""
|
|
|
- var_r, = yield [("CALL_ARGS", [self.analyze, (instruction.pointer,)])]
|
|
|
- raise primitive_functions.PrimitiveFinished(create_access(var_r))
|
|
|
-
|
|
|
- def analyze_direct_call(self, callee_id, callee_name, argument_list):
|
|
|
- """Tries to analyze a direct 'call' instruction."""
|
|
|
- body_id, = yield [("RD", [callee_id, jit_runtime.FUNCTION_BODY_KEY])]
|
|
|
-
|
|
|
- # Make this function dependent on the callee.
|
|
|
- if body_id in self.jit.compilation_dependencies:
|
|
|
- self.jit.compilation_dependencies[body_id].add(self.body_id)
|
|
|
-
|
|
|
- # Figure out if the function might be an intrinsic.
|
|
|
- intrinsic = self.jit.get_intrinsic(callee_name)
|
|
|
-
|
|
|
- if intrinsic is None:
|
|
|
- if callee_name is not None:
|
|
|
- self.jit.register_global(body_id, callee_name)
|
|
|
- compiled_func = self.jit.lookup_compiled_function(callee_name)
|
|
|
- else:
|
|
|
- compiled_func = None
|
|
|
-
|
|
|
- if compiled_func is None:
|
|
|
- # Compile the callee.
|
|
|
- yield [
|
|
|
- ("CALL_ARGS", [self.jit.jit_compile, (self.task_root, body_id, callee_name)])]
|
|
|
-
|
|
|
- # Get the callee's name.
|
|
|
- compiled_func_name = self.jit.get_compiled_name(body_id)
|
|
|
-
|
|
|
- # This handles the corner case where a constant node is called, like
|
|
|
- # 'call(constant(9), ...)'. In this case, `callee_name` is `None`
|
|
|
- # because 'constant(9)' doesn't give us a name. However, we can look up
|
|
|
- # the name of the function at a specific node. If that turns out to be
|
|
|
- # an intrinsic, then we still want to pick the intrinsic over a call.
|
|
|
- intrinsic = self.jit.get_intrinsic(compiled_func_name)
|
|
|
-
|
|
|
- # Analyze the argument dictionary.
|
|
|
- named_args, = yield [("CALL_ARGS", [self.analyze_arguments, (argument_list,)])]
|
|
|
-
|
|
|
- if intrinsic is not None:
|
|
|
- raise primitive_functions.PrimitiveFinished(
|
|
|
- apply_intrinsic(intrinsic, named_args))
|
|
|
- else:
|
|
|
- raise primitive_functions.PrimitiveFinished(
|
|
|
- tree_ir.create_jit_call(
|
|
|
- tree_ir.LoadGlobalInstruction(compiled_func_name),
|
|
|
- named_args,
|
|
|
- tree_ir.LoadLocalInstruction(jit_runtime.KWARGS_PARAMETER_NAME)))
|
|
|
-
|
|
|
- def analyze_arguments(self, argument_list):
|
|
|
- """Analyzes the given parameter-to-value mapping."""
|
|
|
- named_args = []
|
|
|
- for param_name, arg in argument_list:
|
|
|
- param_val, = yield [("CALL_ARGS", [self.analyze, (arg,)])]
|
|
|
- named_args.append((param_name, param_val))
|
|
|
-
|
|
|
- raise primitive_functions.PrimitiveFinished(named_args)
|
|
|
-
|
|
|
- def analyze_indirect_call(self, target, argument_list):
|
|
|
- """Analyzes a call to an unknown function."""
|
|
|
- # First off, let's analyze the callee and the argument list.
|
|
|
- func_val, = yield [("CALL_ARGS", [self.analyze, (target,)])]
|
|
|
- named_args, = yield [("CALL_ARGS", [self.analyze_arguments, (argument_list,)])]
|
|
|
- func_val = tree_ir.StoreLocalInstruction(None, func_val)
|
|
|
- raise primitive_functions.PrimitiveFinished(
|
|
|
- tree_ir.create_block(
|
|
|
- func_val,
|
|
|
- create_indirect_call(func_val.create_load(), named_args)))
|
|
|
-
|
|
|
- def try_analyze_direct_call(self, target, argument_list):
|
|
|
- """Tries to analyze the given 'call' instruction as a direct call."""
|
|
|
- if not self.jit.direct_calls_allowed:
|
|
|
- raise jit_runtime.JitCompilationFailedException(
|
|
|
- 'Direct calls are not allowed by the JIT.')
|
|
|
-
|
|
|
- # Figure out what the 'func' instruction's type is.
|
|
|
- if isinstance(target, bytecode_ir.AccessInstruction):
|
|
|
- # 'access(resolve(var))' instructions are translated to direct calls.
|
|
|
- if isinstance(target.pointer, bytecode_ir.ResolveInstruction):
|
|
|
- self.register_function_var(target.pointer.variable.node_id)
|
|
|
- resolved_var_name = target.pointer.variable.name
|
|
|
-
|
|
|
- if self.jit.thunks_enabled:
|
|
|
- # Analyze the argument dictionary.
|
|
|
- named_args, = yield [("CALL_ARGS", [self.analyze_arguments, (argument_list,)])]
|
|
|
-
|
|
|
- # Try to resolve the callee as an intrinsic.
|
|
|
- intrinsic = self.jit.get_intrinsic(resolved_var_name)
|
|
|
- if intrinsic is not None:
|
|
|
- raise primitive_functions.PrimitiveFinished(
|
|
|
- apply_intrinsic(intrinsic, named_args))
|
|
|
-
|
|
|
- # Otherwise, build a thunk.
|
|
|
- thunk_name = self.jit.jit_thunk_global(target.pointer.variable.name)
|
|
|
- raise primitive_functions.PrimitiveFinished(
|
|
|
- tree_ir.create_jit_call(
|
|
|
- tree_ir.LoadGlobalInstruction(thunk_name),
|
|
|
- named_args,
|
|
|
- tree_ir.LoadLocalInstruction(jit_runtime.KWARGS_PARAMETER_NAME)))
|
|
|
- else:
|
|
|
- # Try to look up the name as a global.
|
|
|
- _globals, = yield [("RD", [self.task_root, "globals"])]
|
|
|
- global_var, = yield [("RD", [_globals, resolved_var_name])]
|
|
|
- global_val, = yield [("RD", [global_var, "value"])]
|
|
|
-
|
|
|
- if global_val is not None:
|
|
|
- result, = yield [("CALL_ARGS", [self.analyze_direct_call, (
|
|
|
- global_val, resolved_var_name, argument_list)])]
|
|
|
- raise primitive_functions.PrimitiveFinished(result)
|
|
|
- elif isinstance(target, bytecode_ir.ConstantInstruction):
|
|
|
- # 'const(func_id)' instructions are also translated to direct calls.
|
|
|
- result, = yield [("CALL_ARGS", [self.analyze_direct_call, (
|
|
|
- target.constant_id, None, argument_list)])]
|
|
|
- raise primitive_functions.PrimitiveFinished(result)
|
|
|
-
|
|
|
- raise jit_runtime.JitCompilationFailedException(
|
|
|
- "Cannot JIT function calls that target an unknown value as direct calls.")
|
|
|
-
|
|
|
- def analyze_call(self, instruction):
|
|
|
- """Tries to analyze the given 'call' instruction."""
|
|
|
- def handle_exception(_):
|
|
|
- # Looks like we'll have to compile it as an indirect call.
|
|
|
- gen = self.analyze_indirect_call(instruction.target, instruction.argument_list)
|
|
|
- result, = yield [("CALL", [gen])]
|
|
|
- raise primitive_functions.PrimitiveFinished(result)
|
|
|
-
|
|
|
- # Try to analyze the call as a direct call.
|
|
|
- yield [("TRY", [])]
|
|
|
- yield [("CATCH", [jit_runtime.JitCompilationFailedException, handle_exception])]
|
|
|
- result, = yield [
|
|
|
- ("CALL_ARGS",
|
|
|
- [self.try_analyze_direct_call, (instruction.target, instruction.argument_list)])]
|
|
|
- yield [("END_TRY", [])]
|
|
|
- raise primitive_functions.PrimitiveFinished(result)
|
|
|
-
|
|
|
- def analyze_break(self, instruction):
|
|
|
- """Tries to analyze the given 'break' instruction."""
|
|
|
- if instruction.loop == self.enclosing_loop_instruction:
|
|
|
- raise primitive_functions.PrimitiveFinished(tree_ir.BreakInstruction())
|
|
|
- else:
|
|
|
- raise jit_runtime.JitCompilationFailedException(
|
|
|
- "Multilevel 'break' is not supported by the baseline JIT.")
|
|
|
-
|
|
|
- def analyze_continue(self, instruction):
|
|
|
- """Tries to analyze the given 'continue' instruction."""
|
|
|
- if instruction.loop == self.enclosing_loop_instruction:
|
|
|
- raise primitive_functions.PrimitiveFinished(tree_ir.ContinueInstruction())
|
|
|
- else:
|
|
|
- raise jit_runtime.JitCompilationFailedException(
|
|
|
- "Multilevel 'continue' is not supported by the baseline JIT.")
|
|
|
-
|
|
|
- instruction_analyzers = {
|
|
|
- bytecode_ir.SelectInstruction : analyze_if,
|
|
|
- bytecode_ir.WhileInstruction : analyze_while,
|
|
|
- bytecode_ir.ReturnInstruction : analyze_return,
|
|
|
- bytecode_ir.ConstantInstruction : analyze_constant,
|
|
|
- bytecode_ir.ResolveInstruction : analyze_resolve,
|
|
|
- bytecode_ir.DeclareInstruction : analyze_declare,
|
|
|
- bytecode_ir.GlobalInstruction : analyze_global,
|
|
|
- bytecode_ir.AssignInstruction : analyze_assign,
|
|
|
- bytecode_ir.AccessInstruction : analyze_access,
|
|
|
- bytecode_ir.OutputInstruction : analyze_output,
|
|
|
- bytecode_ir.InputInstruction : analyze_input,
|
|
|
- bytecode_ir.CallInstruction : analyze_call,
|
|
|
- bytecode_ir.BreakInstruction : analyze_break,
|
|
|
- bytecode_ir.ContinueInstruction : analyze_continue
|
|
|
- }
|