"""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. 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 = # 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 None: # 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.ReadDictionaryValueInstruction( _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', tree_ir.LiteralInstruction(None)), tree_ir.create_block( global_var.create_store( tree_ir.CreateNodeInstruction()), tree_ir.CreateDictionaryEdgeInstruction( _globals.create_load(), tree_ir.LiteralInstruction( instruction.variable.name), global_var.create_load())), tree_ir.EmptyInstruction())), 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 }