Exemple #1
0
class LLVMGen:
    builder: Optional[IRBuilder]
    current_func: Optional[RIALFunction]
    conditional_block: Optional[LLVMBlock]
    end_block: Optional[LLVMBlock]
    current_block: Optional[LLVMBlock]
    current_struct: Optional[RIALIdentifiedStructType]
    currently_unsafe: bool

    def __init__(self):
        self.current_block = None
        self.conditional_block = None
        self.end_block = None
        self.current_func = None
        self.builder = None
        self.current_struct = None
        self.currently_unsafe = False

    def _get_by_identifier(self, identifier: str, variable: Optional = None) -> Optional:
        if isinstance(variable, RIALVariable):
            variable = variable.backing_value

        if not variable is None and hasattr(variable, 'type') and isinstance(variable.type, PointerType):
            if isinstance(variable.type.pointee, RIALIdentifiedStructType):
                struct = ParserState.find_struct(variable.type.pointee.name)

                if struct is None:
                    return None

                if not self.check_struct_access_allowed(struct):
                    raise PermissionError(f"Tried accesssing struct {struct.name}")

                prop = struct.definition.properties[identifier]

                if prop is None:
                    return None

                # Check property access
                if not self.check_property_access_allowed(struct, prop[1]):
                    raise PermissionError(f"Tried to access property {prop[1].name} but it was not allowed!")

                variable = self.builder.gep(variable, [ir.Constant(ir.IntType(32), 0),
                                                       ir.Constant(ir.IntType(32), prop[0])])
        else:
            # Search local variables
            variable = self.current_block.get_named_value(identifier)

            # Search for a global variable
            if variable is None:
                glob = ParserState.find_global(identifier)

                # Check if in same module
                if glob is not None:
                    if glob.backing_value.parent.name != ParserState.module().name:
                        glob_current_module = ParserState.module().get_global_safe(glob.name)

                        if glob_current_module is not None:
                            variable = glob_current_module
                        else:
                            # TODO: Check if global access is allowed
                            variable = self.gen_global(glob.name, None, glob.backing_value.type.pointee,
                                                       glob.access_modifier, "external",
                                                       glob.backing_value.global_constant)
                    else:
                        variable = glob.backing_value

        # If variable is none, just do a full function search
        if variable is None:
            variable = ParserState.find_function(identifier)

            if variable is None:
                variable = ParserState.find_function(mangle_function_name(identifier, []))

            # Check if in same module
            if variable is not None:
                if variable.module.name != ParserState.module().name:
                    variable_current_module = ParserState.module().get_global_safe(variable.name)

                    if variable_current_module is not None:
                        variable = variable_current_module
                    else:
                        variable = self.create_function_with_type(variable.name, variable.name, variable.function_type,
                                                                  variable.linkage,
                                                                  variable.calling_convention,
                                                                  variable.definition)

        return variable

    def get_exact_definition(self, identifier: str):
        identifiers = identifier.split('.')
        variable = None

        for ident in identifiers:
            variable = self._get_by_identifier(ident, variable)

        # Search for the full name
        if variable is None:
            variable = self._get_by_identifier(identifier, variable)

        if variable is None:
            return None

        if isinstance(variable, RIALFunction):
            return variable

        if isinstance(variable, RIALVariable):
            return variable

        return RIALVariable(identifier, map_llvm_to_type(variable.type.pointee), variable)

    def get_definition(self, identifier: str):
        variable = self.get_exact_definition(identifier)

        # Search with module specifier
        if variable is None:
            if ':' in identifier:
                parts = identifier.split(':')
                module_name = ':'.join(parts[0:-1])

                if CompilationManager.check_module_already_compiled(module_name):
                    return None

                if ParserState.add_dependency_and_wait(module_name):
                    return self.get_definition(identifier)

                return None

        return variable

    def gen_integer(self, number: int, length: int, unsigned: bool = False):
        return ir.Constant((unsigned and LLVMUIntType(length) or ir.IntType(length)), number)

    def gen_half(self, number: float):
        return ir.Constant(ir.HalfType(), number)

    def gen_float(self, number: float):
        return ir.Constant(ir.FloatType(), number)

    def gen_double(self, number: float):
        return ir.Constant(ir.DoubleType(), number)

    def gen_global(self, name: str, value: Optional[ir.Constant], ty: Type, access_modifier: RIALAccessModifier,
                   linkage: str,
                   constant: bool):
        rial_variable = ParserState.module().get_rial_variable(name)

        if rial_variable is not None:
            return rial_variable.backing_value

        glob = ir.GlobalVariable(ParserState.module(), ty, name=name)
        glob.linkage = linkage
        glob.global_constant = constant

        if value is not None:
            glob.initializer = value

        rial_variable = RIALVariable(name, map_llvm_to_type(ty), glob,
                                     access_modifier)
        ParserState.module().global_variables.append(rial_variable)

        return glob

    def gen_string_lit(self, name: str, value: str):
        value = eval("'{}'".format(value))

        # TODO: Remove the always-added \0 once we don't need that anymore
        arr = bytearray(value.encode("utf-8") + b"\x00")
        const_char_arr = ir.Constant(ir.ArrayType(ir.IntType(8), len(arr)), arr)
        glob = self.gen_global(name, const_char_arr, const_char_arr.type, RIALAccessModifier.PRIVATE, "private", True)

        return glob

    def gen_load_if_necessary(self, value):
        if isinstance(value, AllocaInstr) or isinstance(value, Argument) or isinstance(value, FormattedConstant) or (
                hasattr(value, 'type') and isinstance(value.type, PointerType)):
            return self.builder.load(value)
        return value

    def gen_var_if_necessary(self, value):
        # Check if variable is not:
        #   - Pointer
        #   - Alloca (Variable), inherently pointer
        #   - Argument (always pointer)
        #   - Type is pointer (e.g. getelementptr instruction)
        if not (isinstance(value, PointerType) or isinstance(value, AllocaInstr) or isinstance(value, Argument)
                or isinstance(value.type, PointerType)):
            allocad = self.builder.alloca(value.type)
            self.builder.store(value, allocad)
            return allocad
        return value

    def gen_addition(self, left, right):
        left = self.gen_load_if_necessary(left)
        right = self.gen_load_if_necessary(right)

        if isinstance(left.type, ir.IntType):
            return self.builder.add(left, right)

        if isinstance(left.type, ir.FloatType) or isinstance(left.type, ir.DoubleType):
            return self.builder.fadd(left, right)

        return None

    def gen_subtraction(self, left, right):
        left = self.gen_load_if_necessary(left)
        right = self.gen_load_if_necessary(right)

        if isinstance(left.type, ir.IntType):
            return self.builder.sub(left, right)

        if isinstance(left.type, ir.FloatType) or isinstance(left.type, ir.DoubleType):
            return self.builder.fsub(left, right)

        return None

    def gen_multiplication(self, left, right):
        left = self.gen_load_if_necessary(left)
        right = self.gen_load_if_necessary(right)

        if isinstance(left.type, ir.IntType):
            return self.builder.mul(left, right)

        if isinstance(left.type, ir.FloatType) or isinstance(left.type, ir.DoubleType):
            return self.builder.fmul(left, right)

        return None

    def gen_division(self, left, right):
        left = self.gen_load_if_necessary(left)
        right = self.gen_load_if_necessary(right)

        if isinstance(left.type, LLVMUIntType):
            return self.builder.udiv(left, right)

        if isinstance(left.type, ir.IntType):
            return self.builder.sdiv(left, right)

        if isinstance(left.type, ir.FloatType) or isinstance(left.type, ir.DoubleType):
            return self.builder.fdiv(left, right)

        return None

    def gen_comparison(self, comparison: str, left, right):
        left = left
        right = right

        if isinstance(left.type, LLVMUIntType) or isinstance(left.type, ir.PointerType):
            return self.builder.icmp_unsigned(comparison, left, right)

        if isinstance(left.type, ir.IntType):
            return self.builder.icmp_signed(comparison, left, right)

        if isinstance(left.type, ir.FloatType) or isinstance(left.type, ir.DoubleType):
            return self.builder.fcmp_ordered(comparison, left, right)

        return None

    def gen_shorthand(self, variable, value, operation):
        loaded = self.builder.load(variable)
        value = self.gen_load_if_necessary(value)
        mathed = None

        if operation == "+":
            mathed = self.gen_addition(loaded, value)
        elif operation == "-":
            mathed = self.gen_subtraction(loaded, value)
        elif operation == "*":
            mathed = self.gen_multiplication(loaded, value)
        elif operation == "/":
            mathed = self.gen_division(loaded, value)

        self.builder.store(mathed, variable)

        return mathed

    def gen_function_call(self, possible_function_names: List[str],
                          llvm_args: List) -> Optional[CallInstr]:
        func = None
        # Check if it's actually a local variable
        for function_name in possible_function_names:
            var = self.get_definition(function_name)

            if var is not None:
                func = var

        # Try to find by function name
        if func is None:
            for function_name in possible_function_names:
                func = ParserState.find_function(function_name)

                if func is not None:
                    break

        # Try to find by function name but enable canonical name
        if func is None:
            rial_arg_types = [map_llvm_to_type(arg.type) for arg in llvm_args]

            for function_name in possible_function_names:
                func = ParserState.find_function(function_name, rial_arg_types)

                if func is not None:
                    break

        if func is None:
            return None

        if isinstance(func, RIALVariable):
            func = func.backing_value

        if isinstance(func, ir.PointerType) and isinstance(func.pointee, RIALFunction):
            func = func.pointee
        elif isinstance(func, GlobalVariable) or isinstance(func, AllocaInstr):
            loaded_func = self.builder.load(func)
            call = self.builder.call(loaded_func, llvm_args)
            return call

        # Check if call is allowed
        if not self.check_function_call_allowed(func):
            raise PermissionError(f"Tried calling function {func.name} from {self.current_func.name}")

        # Check if function is declared in current module
        if ParserState.module().get_global_safe(func.name) is None:
            func = self.create_function_with_type(func.name, func.canonical_name, func.function_type, func.linkage,
                                                  func.calling_convention, func.definition)

        args = list()

        # Gen a load if necessary
        for i, arg in enumerate(llvm_args):
            llvm_arg = func.definition.rial_args[i].llvm_type

            if llvm_arg == arg.type:
                args.append(arg)
                continue

            args.append(self.builder.load(arg))

        # Check type matching
        for i, arg in enumerate(args):
            if len(func.args) > i and arg.type != func.args[i].type:
                # Check for base types
                ty = isinstance(arg.type, PointerType) and arg.type.pointee or arg.type
                func_arg_type = isinstance(func.args[i].type, PointerType) and func.args[i].type.pointee or func.args[
                    i].type

                if isinstance(ty, RIALIdentifiedStructType):
                    struct = ParserState.find_struct(ty.name)

                    if struct is not None:
                        found = False

                        # Check if a base struct matches the type expected
                        # TODO: Recursive check
                        for base_struct in struct.definition.base_structs:
                            if base_struct == func_arg_type.name:
                                args.remove(arg)
                                args.insert(i, self.builder.bitcast(arg, ir.PointerType(base_struct)))
                                found = True
                                break
                        if found:
                            continue

                    # TODO: SLOC information
                raise TypeError(
                    f"Function {func.name} expects a {func.args[i].type} but got a {arg.type}")

        # Gen call
        return self.builder.call(func, args)

    def gen_no_op(self):
        self.gen_function_call(["rial:builtin:settings:nop_function"], [])

    def check_function_call_allowed(self, func: RIALFunction):
        # Unsafe in safe context is big no-no
        if func.definition.unsafe and not self.currently_unsafe:
            return False
        # Public is always okay
        if func.definition.access_modifier == RIALAccessModifier.PUBLIC:
            return True
        # Internal only when it's the same TLM
        if func.definition.access_modifier == RIALAccessModifier.INTERNAL:
            return func.module.name.split(':')[0] == ParserState.module().name.split(':')[0]
        # Private is harder
        if func.definition.access_modifier == RIALAccessModifier.PRIVATE:
            # Allowed if not in struct and in current module
            if func.definition.struct == "" and func.module.name == ParserState.module().name:
                return True
            # Allowed if in same struct irregardless of module
            if self.current_struct is not None and self.current_struct.name == func.definition.struct:
                return True
        return False

    def check_property_access_allowed(self, struct: RIALIdentifiedStructType, prop: RIALVariable):
        # Same struct, anything goes
        if self.current_struct is not None and self.current_struct.name == struct.name:
            return True

        # Unless it's private it's okay
        if prop.access_modifier == RIALAccessModifier.PRIVATE:
            return False

        return True

    def check_struct_access_allowed(self, struct: RIALIdentifiedStructType):
        # Public is always okay
        if struct.definition.access_modifier == RIALAccessModifier.PUBLIC:
            return True

        # Private and same module
        if struct.definition.access_modifier == RIALAccessModifier.PRIVATE:
            return struct.module_name == ParserState.module().name

        # Internal and same TLM
        if struct.definition.access_modifier == RIALAccessModifier.INTERNAL:
            return struct.module_name.split(':')[0] == ParserState.module().name.split(':')[0]

        return False

    def declare_nameless_variable_from_rial_type(self, rial_type: str, value):
        returned_type = ParserState.map_type_to_llvm_no_pointer(rial_type)

        if isinstance(returned_type, ir.VoidType):
            return value
        returned_value = self.builder.alloca(returned_type)

        if isinstance(ParserState.map_type_to_llvm(rial_type), PointerType):
            self.builder.store(self.builder.load(value), returned_value)
        else:
            self.builder.store(value, returned_value)

        return returned_value

    def assign_non_constant_global_variable(self, glob: GlobalVariable, value):
        if self.current_func.name != "global_ctor":
            raise PermissionError()

        if isinstance(value, AllocaInstr):
            value = self.builder.load(value)
        elif isinstance(value, PointerType):
            value = self.builder.load(value.pointee)
        elif isinstance(value, FormattedConstant):
            value = self.builder.load(value)

        self.builder.store(value, glob)

    def declare_non_constant_global_variable(self, identifier: str, value, access_modifier: RIALAccessModifier,
                                             linkage: str):
        """
        Needs to be called with create_in_global_ctor or otherwise the store/load operations are going to fail.
        :param identifier:
        :param value:
        :param access_modifier:
        :param linkage:
        :return:
        """
        if ParserState.module().get_global_safe(identifier) is not None:
            return None

        if self.current_func.name != "global_ctor":
            return None

        if isinstance(value, AllocaInstr):
            variable = self.gen_global(identifier, null(value.type.pointee), value.type.pointee, access_modifier,
                                       linkage, False)
        elif isinstance(value, PointerType):
            variable = self.gen_global(identifier, null(value.pointee.type), value.pointee.type, access_modifier,
                                       linkage, False)
        elif isinstance(value, FormattedConstant) or isinstance(value, AllocaInstr):
            variable = self.gen_global(identifier, null(value.type.pointee), value.type.pointee, access_modifier,
                                       linkage, False)
        elif isinstance(value, Constant):
            variable = self.gen_global(identifier, null(value.type), value.type, access_modifier, linkage, False)
        else:
            variable = self.gen_global(identifier, null(value.type), value.type, access_modifier, linkage, False)

        self.assign_non_constant_global_variable(variable, value)

        return variable

    def declare_variable(self, identifier: str, value) -> Optional[AllocaInstr]:
        variable = self.current_block.get_named_value(identifier)
        if variable is not None:
            return None

        if isinstance(value, AllocaInstr) or isinstance(value, PointerType):
            variable = value
            variable.name = identifier
        elif isinstance(value, CastInstr) and value.opname == "inttoptr":
            variable = self.builder.alloca(value.type.pointee)
            variable.name = identifier
            rial_type = f"{map_llvm_to_type(value.type)}"
            variable.set_metadata('type',
                                  ParserState.module().add_metadata((rial_type,)))
            self.builder.store(self.builder.load(value), variable)
        elif isinstance(value, FormattedConstant):
            variable = self.builder.alloca(value.type.pointee)
            variable.name = identifier
            rial_type = f"{map_llvm_to_type(value.type)}"
            variable.set_metadata('type',
                                  ParserState.module().add_metadata((rial_type,)))
            self.builder.store(self.builder.load(value), variable)
        elif isinstance(value, Constant):
            variable = self.builder.alloca(value.type)
            variable.name = identifier
            rial_type = f"{map_llvm_to_type(value.type)}"
            variable.set_metadata('type',
                                  ParserState.module().add_metadata((rial_type,)))
            self.builder.store(value, variable)
        else:
            variable = self.builder.alloca(value.type)
            variable.name = identifier
            rial_type = f"{map_llvm_to_type(value.type)}"
            variable.set_metadata('type', ParserState.module().add_metadata((rial_type,)))
            self.builder.store(value, variable)

        self.current_block.add_named_value(identifier, variable)

        return variable

    def assign_to_variable(self, identifier: Union[str, Any], value):
        if isinstance(identifier, str):
            variable = self.get_definition(identifier)
        else:
            variable = identifier

        if variable is None:
            return None

        if isinstance(value, AllocaInstr):
            value = self.builder.load(value)
        elif isinstance(value, PointerType) and not isinstance(variable, PointerType):
            value = self.builder.load(value.pointee)

        self.builder.store(value, variable)

        return variable

    def create_block(self, block_name: str, parent: Optional[LLVMBlock] = None,
                     sibling: Optional[LLVMBlock] = None) -> \
            Optional[LLVMBlock]:
        if parent is None and sibling is None and block_name != "entry" and len(self.current_func.basic_blocks) > 0:
            return None

        block = self.builder.append_basic_block(block_name)
        llvmblock = create_llvm_block(block, parent, sibling)

        return llvmblock

    def create_loop(self, base_block_name: str, parent: LLVMBlock):
        (conditional_llvm_block, body_llvm_block, end_llvm_block) = self.create_conditional_block(base_block_name,
                                                                                                  parent)

        self.conditional_block = conditional_llvm_block
        self.end_block = end_llvm_block

        return conditional_llvm_block, body_llvm_block, end_llvm_block,

    def create_switch_blocks(self, base_block_name: str, parent: LLVMBlock, count_of_cases: int,
                             default_case: bool) -> \
            List[LLVMBlock]:
        blocks = list()

        for i in range(0, count_of_cases):
            blocks.append(self.create_block(f"{base_block_name}.case.{i}", parent, None))

        if default_case:
            blocks.append(self.create_block(f"{base_block_name}.default", parent))

        return blocks

    def create_conditional_block(self, base_block_name: str, parent: LLVMBlock) -> Tuple[
        LLVMBlock, LLVMBlock, LLVMBlock]:
        # Create three blocks, one condition, one body, and one after the loop
        conditional_llvm_block = self.create_block(f"{base_block_name}.condition", parent, None)
        body_llvm_block = self.create_block(f"{base_block_name}.body", conditional_llvm_block, None)
        end_llvm_block = self.create_block(f"{base_block_name}.end", None, parent)

        return conditional_llvm_block, body_llvm_block, end_llvm_block,

    def create_conditional_block_with_else(self, base_block_name: str, parent: LLVMBlock):
        (conditional_block, body_block, else_block) = self.create_conditional_block(base_block_name, parent)

        # Transform end block into else block
        else_block.block.name = f"{base_block_name}.else"
        else_block.sibling = None
        else_block.parent = conditional_block

        # Create new end block
        end_block = self.create_block(f"{base_block_name}.if_else.end", None, conditional_block.parent)

        return conditional_block, body_block, else_block, end_block

    def create_jump_if_not_exists(self, target_block: LLVMBlock) -> Optional[Branch]:
        if self.current_block.block.terminator is None:
            return self.builder.branch(target_block.block)

        return None

    def create_conditional_jump(self, condition, true_block: LLVMBlock, false_block: LLVMBlock,
                                true_branch_weight: int = 50, false_branch_weight: int = 50) -> ConditionalBranch:
        # Check if condition is a variable, we need to load that for LLVM
        condition = self.gen_load_if_necessary(condition)

        cbranch = self.builder.cbranch(condition, true_block.block, false_block.block)

        cbranch.set_weights([true_branch_weight, false_branch_weight])

        return cbranch

    def create_jump(self, target_block: LLVMBlock):
        return self.builder.branch(target_block.block)

    def enter_block(self, llvmblock: LLVMBlock):
        self.current_block = llvmblock
        self.builder.position_at_start(self.current_block.block)

    def enter_block_end(self, llvmblock: LLVMBlock):
        self.current_block = llvmblock
        self.builder.position_at_end(self.current_block.block)

    def create_identified_struct(self, name: str, linkage: str,
                                 rial_access_modifier: RIALAccessModifier,
                                 base_llvm_structs: List[RIALIdentifiedStructType],
                                 body: List[RIALVariable]) -> RIALIdentifiedStructType:
        # Build normal struct and switch out with RIALStruct
        struct = ParserState.module().context.get_identified_type(name)
        rial_struct = RIALIdentifiedStructType(struct.context, struct.name, struct.packed)
        ParserState.module().context.identified_types[struct.name] = rial_struct
        struct = rial_struct
        ParserState.module().structs.append(struct)

        # Create metadata definition
        struct_def = StructDefinition(rial_access_modifier)

        # Build body and body definition
        props_def = dict()
        props = list()
        prop_offset = 0
        for deriv in base_llvm_structs:
            for prop in deriv.definition.properties.values():
                props.append(ParserState.map_type_to_llvm(prop[1].rial_type))
                props_def[prop[1].name] = (prop_offset, prop[1])
                prop_offset += 1

            struct_def.base_structs.append(deriv.name)
        for bod in body:
            props.append(ParserState.map_type_to_llvm(bod.rial_type))
            props_def[bod.name] = (prop_offset, bod)
            prop_offset += 1

        struct.set_body(*tuple(props))
        struct.module_name = ParserState.module().name
        self.current_struct = struct

        struct_def.properties = props_def

        # Store def in metadata
        struct.definition = struct_def

        return struct

    def finish_struct(self):
        self.current_struct = None
        self.current_func = None
        self.current_block = None

    def create_function_with_type(self, name: str, canonical_name: str, ty: FunctionType,
                                  linkage: str,
                                  calling_convention: str,
                                  function_def: FunctionDefinition) -> RIALFunction:
        """
        Creates an IR Function with the specified arguments. NOTHING MORE.
        :param canonical_name:
        :param canonical_name:
        :param function_def:
        :param name:
        :param ty:
        :param linkage:
        :param calling_convention:
        :return:
        """

        # Create function with specified linkage (internal -> module only)
        func = RIALFunction(ParserState.module(), ty, name=name, canonical_name=canonical_name)
        func.linkage = linkage
        func.calling_convention = calling_convention
        func.definition = function_def

        # Set argument names
        for i, arg in enumerate(func.args):
            arg.name = function_def.rial_args[i].name

        ParserState.module().rial_functions.append(func)

        return func

    def create_function_body(self, func: RIALFunction, rial_arg_types: List[str]):
        self.current_func = func

        # Create entry block
        bb = func.append_basic_block("entry")
        llvm_bb = create_llvm_block(bb)
        self.current_block = llvm_bb

        if self.builder is None:
            self.builder = IRBuilder(bb)

        self.builder.position_at_start(bb)

        # Allocate new variables for the passed arguments
        for i, arg in enumerate(func.args):
            # Don't copy variables that are a pointer
            if isinstance(arg.type, PointerType):
                variable = RIALVariable(arg.name, rial_arg_types[i], arg)
            else:
                allocated_arg = self.builder.alloca(arg.type)
                self.builder.store(arg, allocated_arg)
                variable = RIALVariable(arg.name, rial_arg_types[i], allocated_arg)
            self.current_block.add_named_value(arg.name, variable)

    def finish_current_block(self):
        if self.current_block.block.terminator is None:
            self.builder.position_at_end(self.current_block.block)
            self.builder.ret_void()
        self.current_block = self.current_block.parent

    def finish_current_func(self):
        # If we're in release mode
        # Reorder all possible allocas to the start of the function
        if CompilationManager.config.raw_opts.release:
            entry: Block = self.current_func.entry_basic_block
            pos = entry.instructions.index(
                next((instr for instr in reversed(entry.instructions) if isinstance(instr, AllocaInstr)),
                     entry.terminator))
            allocas: List[Tuple[AllocaInstr, Block]] = list()

            for block in self.current_func.blocks:
                block: Block
                # Skip first block
                if block == entry:
                    continue
                for instr in block.instructions:
                    if isinstance(instr, AllocaInstr):
                        allocas.append((instr, block))
            for instr_block in allocas:
                if instr_block is not None:
                    instr_block[1].instructions.remove(instr_block[0])
                    entry.instructions.insert(pos, instr_block[0])
                    pos += 1
                    # Insert nop if block is empty
                    if len(instr_block[1].instructions) == 0:
                        self.builder.position_before(instr_block[1].terminator)
                        self.gen_no_op()
        self.current_func = None

    def create_return_statement(self, statement=VoidType()):
        if isinstance(statement, VoidType):
            return self.builder.ret_void()

        return self.builder.ret(statement)

    def finish_loop(self):
        self.conditional_block = None
        self.end_block = None

    def create_function_type(self, llvm_return_type: Type, llvm_arg_types: List[Type], var_args: bool):
        return ir.FunctionType(llvm_return_type, tuple(llvm_arg_types), var_arg=var_args)

    @contextmanager
    def create_in_global_ctor(self):
        current_block = self.current_block
        current_func = self.current_func
        current_struct = self.current_struct
        conditional_block = self.conditional_block
        end_block = self.end_block
        pos = self.builder is not None and self.builder._anchor or 0

        func = ParserState.module().get_global_safe('global_ctor')

        if func is None:
            func_type = self.create_function_type(ir.VoidType(), [], False)
            func = self.create_function_with_type('global_ctor', 'global_ctor', func_type, "internal", "ccc",
                                                  FunctionDefinition('void'))
            self.create_function_body(func, [])
            struct_type = ir.LiteralStructType([ir.IntType(32), func_type.as_pointer(), ir.IntType(8).as_pointer()])
            glob_value = ir.Constant(ir.ArrayType(struct_type, 1), [ir.Constant.literal_struct(
                [ir.Constant(ir.IntType(32), 65535), func, NULL])])
            glob_type = ir.ArrayType(struct_type, 1)

            self.gen_global("llvm.global_ctors", glob_value, glob_type, RIALAccessModifier.PRIVATE, "appending", False)
        else:
            self.builder.position_before(func.entry_basic_block.terminator)
            self.current_func = func
            self.current_block = create_llvm_block(func.entry_basic_block)

        self.current_struct = None
        self.conditional_block = None
        self.end_block = None

        yield

        self.finish_current_block()
        self.finish_current_func()

        self.current_block = current_block
        self.current_func = current_func
        self.current_struct = current_struct
        self.conditional_block = conditional_block
        self.end_block = end_block
        self.builder._anchor = pos
        self.builder._block = self.current_block is not None and self.current_block.block or None

        return