def gen_dispatch_func_ir( builder: IRBuilder, fitem: FuncDef, main_func_name: str, dispatch_name: str, sig: FuncSignature, ) -> Tuple[FuncIR, Value]: """Create a dispatch function (a function that checks the first argument type and dispatches to the correct implementation) """ builder.enter(FuncInfo(fitem, dispatch_name)) setup_callable_class(builder) builder.fn_info.callable_class.ir.attributes['registry'] = dict_rprimitive builder.fn_info.callable_class.ir.attributes[ 'dispatch_cache'] = dict_rprimitive builder.fn_info.callable_class.ir.has_dict = True builder.fn_info.callable_class.ir.needs_getseters = True generate_singledispatch_callable_class_ctor(builder) generate_singledispatch_dispatch_function(builder, main_func_name, fitem) args, _, blocks, _, fn_info = builder.leave() dispatch_callable_class = add_call_to_callable_class( builder, args, blocks, sig, fn_info) builder.functions.append(dispatch_callable_class) add_get_to_callable_class(builder, fn_info) add_register_method_to_callable_class(builder, fn_info) func_reg = instantiate_callable_class(builder, fn_info) dispatch_func_ir = generate_dispatch_glue_native_function( builder, fitem, dispatch_callable_class.decl, dispatch_name) return dispatch_func_ir, func_reg
def __init__(self, current_module: str, types: Dict[Expression, Type], graph: Graph, errors: Errors, mapper: Mapper, pbv: PreBuildVisitor, visitor: IRVisitor, options: CompilerOptions) -> None: self.builder = LowLevelIRBuilder(current_module, mapper) self.builders = [self.builder] self.symtables = [OrderedDict()] # type: List[OrderedDict[SymbolNode, SymbolTarget]] self.runtime_args = [[]] # type: List[List[RuntimeArg]] self.function_name_stack = [] # type: List[str] self.class_ir_stack = [] # type: List[ClassIR] self.current_module = current_module self.mapper = mapper self.types = types self.graph = graph self.ret_types = [] # type: List[RType] self.functions = [] # type: List[FuncIR] self.classes = [] # type: List[ClassIR] self.final_names = [] # type: List[Tuple[str, RType]] self.callable_class_names = set() # type: Set[str] self.options = options # These variables keep track of the number of lambdas, implicit indices, and implicit # iterators instantiated so we avoid name conflicts. The indices and iterators are # instantiated from for-loops. self.lambda_counter = 0 self.temp_counter = 0 # These variables are populated from the first-pass PreBuildVisitor. self.free_variables = pbv.free_variables self.prop_setters = pbv.prop_setters self.encapsulating_funcs = pbv.encapsulating_funcs self.nested_fitems = pbv.nested_funcs.keys() self.fdefs_to_decorators = pbv.funcs_to_decorators self.visitor = visitor # This list operates similarly to a function call stack for nested functions. Whenever a # function definition begins to be generated, a FuncInfo instance is added to the stack, # and information about that function (e.g. whether it is nested, its environment class to # be generated) is stored in that FuncInfo instance. When the function is done being # generated, its corresponding FuncInfo is popped off the stack. self.fn_info = FuncInfo(INVALID_FUNC_DEF, '', '') self.fn_infos = [self.fn_info] # type: List[FuncInfo] # This list operates as a stack of constructs that modify the # behavior of nonlocal control flow constructs. self.nonlocal_control = [] # type: List[NonlocalControl] self.errors = errors # Notionally a list of all of the modules imported by the # module being compiled, but stored as an OrderedDict so we # can also do quick lookups. self.imports = OrderedDict() # type: OrderedDict[str, None]
def enter(self, fn_info: Union[FuncInfo, str] = '') -> None: if isinstance(fn_info, str): fn_info = FuncInfo(name=fn_info) self.builder = LowLevelIRBuilder(self.current_module, self.mapper) self.builders.append(self.builder) self.fn_info = fn_info self.fn_infos.append(self.fn_info) self.ret_types.append(none_rprimitive) if fn_info.is_generator: self.nonlocal_control.append(GeneratorNonlocalControl()) else: self.nonlocal_control.append(BaseNonlocalControl()) self.activate_block(BasicBlock())
def gen_func_item(builder: IRBuilder, fitem: FuncItem, name: str, sig: FuncSignature, cdef: Optional[ClassDef] = None, ) -> Tuple[FuncIR, Optional[Value]]: """Generate and return the FuncIR for a given FuncDef. If the given FuncItem is a nested function, then we generate a callable class representing the function and use that instead of the actual function. if the given FuncItem contains a nested function, then we generate an environment class so that inner nested functions can access the environment of the given FuncDef. Consider the following nested function: def a() -> None: def b() -> None: def c() -> None: return None return None return None The classes generated would look something like the following. has pointer to +-------+ +--------------------------> | a_env | | +-------+ | ^ | | has pointer to +-------+ associated with +-------+ | b_obj | -------------------> | b_env | +-------+ +-------+ ^ | +-------+ has pointer to | | c_obj | --------------------------+ +-------+ """ # TODO: do something about abstract methods. func_reg = None # type: Optional[Value] # We treat lambdas as always being nested because we always generate # a class for lambdas, no matter where they are. (It would probably also # work to special case toplevel lambdas and generate a non-class function.) is_nested = fitem in builder.nested_fitems or isinstance(fitem, LambdaExpr) contains_nested = fitem in builder.encapsulating_funcs.keys() is_decorated = fitem in builder.fdefs_to_decorators in_non_ext = False class_name = None if cdef: ir = builder.mapper.type_to_ir[cdef.info] in_non_ext = not ir.is_ext_class class_name = cdef.name builder.enter(FuncInfo(fitem, name, class_name, gen_func_ns(builder), is_nested, contains_nested, is_decorated, in_non_ext)) # Functions that contain nested functions need an environment class to store variables that # are free in their nested functions. Generator functions need an environment class to # store a variable denoting the next instruction to be executed when the __next__ function # is called, along with all the variables inside the function itself. if builder.fn_info.contains_nested or builder.fn_info.is_generator: setup_env_class(builder) if builder.fn_info.is_nested or builder.fn_info.in_non_ext: setup_callable_class(builder) if builder.fn_info.is_generator: # Do a first-pass and generate a function that just returns a generator object. gen_generator_func(builder) blocks, env, ret_type, fn_info = builder.leave() func_ir, func_reg = gen_func_ir(builder, blocks, sig, env, fn_info, cdef) # Re-enter the FuncItem and visit the body of the function this time. builder.enter(fn_info) setup_env_for_generator_class(builder) load_outer_envs(builder, builder.fn_info.generator_class) if builder.fn_info.is_nested and isinstance(fitem, FuncDef): setup_func_for_recursive_call(builder, fitem, builder.fn_info.generator_class) create_switch_for_generator_class(builder) add_raise_exception_blocks_to_generator_class(builder, fitem.line) else: load_env_registers(builder) gen_arg_defaults(builder) if builder.fn_info.contains_nested and not builder.fn_info.is_generator: finalize_env_class(builder) builder.ret_types[-1] = sig.ret_type # Add all variables and functions that are declared/defined within this # function and are referenced in functions nested within this one to this # function's environment class so the nested functions can reference # them even if they are declared after the nested function's definition. # Note that this is done before visiting the body of this function. env_for_func = builder.fn_info # type: Union[FuncInfo, ImplicitClass] if builder.fn_info.is_generator: env_for_func = builder.fn_info.generator_class elif builder.fn_info.is_nested or builder.fn_info.in_non_ext: env_for_func = builder.fn_info.callable_class if builder.fn_info.fitem in builder.free_variables: # Sort the variables to keep things deterministic for var in sorted(builder.free_variables[builder.fn_info.fitem], key=lambda x: x.name): if isinstance(var, Var): rtype = builder.type_to_rtype(var.type) builder.add_var_to_env_class(var, rtype, env_for_func, reassign=False) if builder.fn_info.fitem in builder.encapsulating_funcs: for nested_fn in builder.encapsulating_funcs[builder.fn_info.fitem]: if isinstance(nested_fn, FuncDef): # The return type is 'object' instead of an RInstance of the # callable class because differently defined functions with # the same name and signature across conditional blocks # will generate different callable classes, so the callable # class that gets instantiated must be generic. builder.add_var_to_env_class( nested_fn, object_rprimitive, env_for_func, reassign=False ) builder.accept(fitem.body) builder.maybe_add_implicit_return() if builder.fn_info.is_generator: populate_switch_for_generator_class(builder) blocks, env, ret_type, fn_info = builder.leave() if fn_info.is_generator: add_methods_to_generator_class(builder, fn_info, sig, env, blocks, fitem.is_coroutine) else: func_ir, func_reg = gen_func_ir(builder, blocks, sig, env, fn_info, cdef) calculate_arg_defaults(builder, fn_info, env, func_reg) return (func_ir, func_reg)