123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118 |
- """Parses Modelverse bytecode graphs into bytecode ir."""
- import modelverse_jit.bytecode_ir as bytecode_ir
- import modelverse_jit.runtime as jit_runtime
- import modelverse_kernel.primitives as primitive_functions
- class BytecodeParser(object):
- """Parses bytecode graphs."""
- def __init__(self):
- self.parsed_nodes = {}
- def parse_instruction(self, node_id):
- """Parses the instruction node with the given identifier."""
- if node_id is None:
- raise primitive_functions.PrimitiveFinished(None)
- elif node_id in self.parsed_nodes:
- # We've already parsed this node, so return it right away.
- raise primitive_functions.PrimitiveFinished(self.parsed_nodes[node_id])
- instruction_val, = yield [("RV", [node_id])]
- instruction_type = instruction_val["value"]
- # Create an instruction and store it in the instruction dictionary.
- instruction = self.create_instruction(instruction_type)
- self.parsed_nodes[node_id] = instruction
- # Initialize the instruction from the node's data.
- yield [("CALL_ARGS", [self.initialize_instruction, (instruction, node_id)])]
- # Retrieve the debug information.
- debug_info, = yield [("RD", [node_id, "__debug"])]
- if debug_info is not None:
- debug_info, = yield [("RV", [debug_info])]
- instruction.debug_information = debug_info
- # Check if the instruction has a 'next' instruction.
- next_instr_id, = yield [("RD", [node_id, "next"])]
- if next_instr_id is not None:
- instruction.next_instruction, = yield [
- ("CALL_ARGS", [self.parse_instruction, (next_instr_id,)])]
- raise primitive_functions.PrimitiveFinished(instruction)
- def parse_variable(self, node_id):
- """Parses the given variable node."""
- var_name, = yield [("RV", [node_id])]
- raise primitive_functions.PrimitiveFinished(
- bytecode_ir.VariableNode(node_id, var_name))
- def __parse_node_unchecked(self, node_id, result_type):
- """Parses the given node as the specified type of object, without
- checking that the result actually conforms to said type."""
- if result_type is bytecode_ir.VariableNode:
- yield [("TAIL_CALL_ARGS", [self.parse_variable, (node_id,)])]
- elif result_type is int:
- raise primitive_functions.PrimitiveFinished(node_id)
- else:
- yield [("TAIL_CALL_ARGS", [self.parse_instruction, (node_id,)])]
- def parse_node(self, node_id, result_type):
- """Parses the given node as the specified type of object."""
- result, = yield [("CALL_ARGS", [self.__parse_node_unchecked, (node_id, result_type)])]
- if result is not None and not isinstance(result, result_type):
- raise jit_runtime.JitCompilationFailedException(
- "Parsed a node as an instance of '%s', expected an instance of '%s'." % (
- type(result).__name__, result_type.__name__))
- raise primitive_functions.PrimitiveFinished(result)
- def parse_arguments(self, first_argument_id):
- """Parses the parameter-to-argument mapping started by the specified first argument
- node."""
- next_param = first_argument_id
- named_args = []
- while next_param is not None:
- param_name_id, = yield [("RD", [next_param, "name"])]
- param_name, = yield [("RV", [param_name_id])]
- param_val_id, = yield [("RD", [next_param, "value"])]
- param_val, = yield [("CALL_ARGS", [self.parse_instruction, (param_val_id,)])]
- named_args.append((param_name, param_val))
- next_param, = yield [("RD", [next_param, "next_param"])]
- raise primitive_functions.PrimitiveFinished(named_args)
- def create_instruction(self, instruction_type):
- """Creates an instruction of the given type."""
- if instruction_type in bytecode_ir.INSTRUCTION_TYPE_MAPPING:
- return object.__new__(bytecode_ir.INSTRUCTION_TYPE_MAPPING[instruction_type])
- else:
- raise jit_runtime.JitCompilationFailedException(
- "Unknown instruction type: '%s'" % instruction_type)
- def initialize_call(self, call_instruction, node_id):
- """Initializes the given call instruction."""
- func_id, first_arg_id, = yield [
- ("RD", [node_id, "func"]),
- ("RD", [node_id, "params"])]
- func_val, = yield [("CALL_ARGS", [self.parse_instruction, (func_id,)])]
- named_args, = yield [("CALL_ARGS", [self.parse_arguments, (first_arg_id,)])]
- call_instruction.__init__(func_val, named_args)
- raise primitive_functions.PrimitiveFinished(None)
- def initialize_instruction(self, instruction, node_id):
- """Initializes the given instruction with data from the given node."""
- instr_type = type(instruction)
- # print("Initializing '%s' node" % instr_type)
- if instr_type is bytecode_ir.CallInstruction:
- # Call instructions are complicated, so they get special treatment.
- yield [("TAIL_CALL_ARGS", [self.initialize_call, (instruction, node_id)])]
- else:
- # Construct an argument list based on the `constructor_parameters` attribute
- # of the instruction type.
- arg_list = []
- for dict_key, ctor_param_ty in instr_type.constructor_parameters:
- arg_id, = yield [("RD", [node_id, dict_key])]
- arg, = yield [("CALL_ARGS", [self.parse_node, (arg_id, ctor_param_ty)])]
- arg_list.append(arg)
- instruction.__init__(*arg_list)
- raise primitive_functions.PrimitiveFinished(None)
|