Beispiel #1
0
    def return_rule(self, tree: Tree):
        nodes = tree.children

        if self.module.current_block.terminator is not None:
            raise PermissionError("Return after return")

        if len(nodes) == 0:
            self.module.builder.ret_void()
            return

        variable: RIALVariable = self.transform_helper(nodes[0])

        assert isinstance(variable, RIALVariable)

        if variable.rial_type != self.module.current_func.definition.rial_return_type:
            raise TypeError(
                variable.rial_type,
                self.module.current_func.definition.rial_return_type)

        if isinstance(variable.llvm_type, VoidType):
            self.module.builder.ret_void()
        elif is_builtin_type(variable.rial_type):
            self.module.builder.ret(
                variable.get_loaded_if_variable(self.module))
        # Special case for CStrings for now
        elif self.module.current_func.definition.rial_return_type == map_shortcut_to_type(
                "CString") and isinstance(variable.value.type.pointee,
                                          ir.PointerType):
            self.module.builder.ret(self.module.builder.load(variable.value))
        else:
            self.module.builder.ret(variable.value)
Beispiel #2
0
    def cast(self, tree: Tree):
        nodes = tree.children
        ty = self.module.get_definition(nodes[0])
        value: RIALVariable = self.transform_helper(nodes[1])

        if isinstance(ty, ir.Type) and is_builtin_type(map_llvm_to_type(ty)):
            # Simple cast for primitive to primitive
            if is_builtin_type(value.rial_type):
                cast_function = get_casting_function(value.llvm_type, ty)

                if hasattr(self.module.builder, cast_function):
                    casted = getattr(self.module.builder, cast_function)(
                        value.get_loaded_if_variable(self.module), ty)
                    return RIALVariable("cast", map_llvm_to_type(ty), ty,
                                        casted)
                else:
                    raise TypeError(
                        f"No casting function found for casting {value.rial_type} to {nodes[0]}"
                    )
            else:
                # Casting type to integer ("pointer") (unsafe!)
                with only_allowed_in_unsafe():
                    casted = self.module.builder.ptrtoint(value.value, ty)
                    return RIALVariable("cast", map_llvm_to_type(ty), ty,
                                        casted)
        elif isinstance(ty, RIALIdentifiedStructType):
            # Casting integer to type (unsafe!)
            if is_builtin_type(value.rial_type):
                with only_allowed_in_unsafe():
                    casted = self.module.builder.inttoptr(
                        value.get_loaded_if_variable(self.module),
                        ty.as_pointer())
                    return RIALVariable("cast", ty.name, ty, casted)
            else:
                # Simple type cast
                casted = self.module.builder.bitcast(value.value,
                                                     ty.as_pointer())
                return RIALVariable("cast", ty.name, ty, casted)

        raise TypeError(ty, value)
Beispiel #3
0
    def equal(self, tree: Tree):
        nodes = tree.children
        left: RIALVariable = self.transform_helper(nodes[0])
        comparison = nodes[1].value
        right: RIALVariable = self.transform_helper(nodes[2])

        assert isinstance(left, RIALVariable)
        assert isinstance(right, RIALVariable)
        assert isinstance(comparison, str)

        # If both are pointers to arbitrary struct then we need to compare the pointers and not what they contain.
        # At least until we got a Equals method or something to overwrite
        if is_builtin_type(left.rial_type) and left.is_variable:
            left_val = self.module.builder.load(left.value)
        else:
            left_val = left.value

        if is_builtin_type(right.rial_type) and right.is_variable:
            right_val = self.module.builder.load(right.value)
        else:
            right_val = right.value

        # Unsigned and pointers
        if isinstance(left.llvm_type, LLVMUIntType) or (not is_builtin_type(left.rial_type) and left.is_variable):
            result = self.module.builder.icmp_unsigned(comparison, left_val, right_val)
        # Signed
        elif isinstance(left.llvm_type, ir.IntType):
            result = self.module.builder.icmp_signed(comparison, left_val, right_val)
        # Floating
        elif isinstance(left.llvm_type, ir._BaseFloatType):
            result = self.module.builder.fcmp_ordered(comparison, left_val, right_val)
        else:
            raise TypeError(left, right)

        assert isinstance(result, ir.instructions.CompareInstr)

        return RIALVariable(comparison, "Int1", ir.IntType(1), result)
Beispiel #4
0
    def shorthand_if(self, tree: Tree):
        nodes = tree.children
        name = f"{self.module.current_block.name}.shorthand_conditional"
        conditional_block = self.module.builder.create_block(
            f"{name}.condition", parent=self.module.current_block)
        body_block = self.module.builder.create_block(f"{name}.body",
                                                      parent=conditional_block)
        else_block = self.module.builder.create_block(f"{name}.else",
                                                      parent=conditional_block)
        end_block = self.module.builder.create_block(
            f"{name}.end", sibling=self.module.current_block)
        old_conditional_block = self.module.conditional_block
        old_end_block = self.module.end_block

        # Create condition
        self.module.builder.create_jump(conditional_block)
        self.module.builder.enter_block(conditional_block)
        cond: RIALVariable = self.transform_helper(nodes[0])
        assert isinstance(cond, RIALVariable)
        self.module.builder.create_conditional_jump(
            cond.get_loaded_if_variable(self.module), body_block, else_block)

        # Create body
        self.module.builder.enter_block(body_block)
        true_value: RIALVariable = self.transform_helper(nodes[1])
        assert isinstance(true_value, RIALVariable)
        # Builtins can be passed but need to be loaded
        if is_builtin_type(true_value.rial_type):
            true_val = true_value.get_loaded_if_variable(self.module)
            ty = true_value.llvm_type
        # Pointer to first element for still normal arrays
        elif re.match(r".+\[[0-9]+\]$", true_value.rial_type) is not None:
            true_val = self.module.builder.gep(true_value.value,
                                               [Int32(0), Int32(0)])
            ty = true_val.type
        else:
            true_val = true_value.value
            ty = true_value.llvm_type

        # Jump out of body
        if not self.module.current_block.is_terminated:
            self.module.builder.create_jump(end_block)

        # Create else
        self.module.builder.enter_block(else_block)
        false_value: RIALVariable = self.transform_helper(nodes[2])
        assert isinstance(false_value, RIALVariable)
        # Builtins can be passed but need to be loaded
        if is_builtin_type(false_value.rial_type):
            false_val = false_value.get_loaded_if_variable(self.module)
        # Pointer to first element for still normal arrays
        elif re.match(r".+\[[0-9]+\]$", false_value.rial_type) is not None:
            false_val = self.module.builder.gep(false_value.value,
                                                [Int32(0), Int32(0)])
        else:
            false_val = false_value.value

        # Jump out of else
        if not self.module.current_block.is_terminated:
            self.module.builder.create_jump(end_block)

        # Leave conditional block
        self.module.builder.enter_block(end_block)

        # PHI the values
        phi = self.module.builder.phi(ty)
        phi.add_incoming(true_val, body_block)
        phi.add_incoming(false_val, else_block)
        self.module.conditional_block = old_conditional_block
        self.module.end_block = old_end_block

        return RIALVariable("phi", map_llvm_to_type(ty), ty, phi)
Beispiel #5
0
    def function_decl(self, tree: Tree):
        nodes = tree.children
        modifier: DeclarationModifier = nodes[0]

        if isinstance(modifier, MetadataToken):
            return tree

        access_modifier: AccessModifier = modifier.access_modifier
        unsafe: bool = modifier.unsafe
        linkage = access_modifier.get_linkage()
        name = nodes[2].value
        llvm_return_type = self.module.get_definition(nodes[1])

        if isinstance(llvm_return_type, RIALIdentifiedStructType):
            return_type = llvm_return_type.name
        else:
            return_type = map_shortcut_to_type('.'.join(nodes[1]))

        # Builtins can be passed as raw values
        if is_builtin_type(return_type):
            llvm_return_type = llvm_return_type
        # Pointer to first element for still normal arrays
        elif re.match(r".+\[[0-9]+\]$", return_type) is not None:
            llvm_return_type = llvm_return_type.as_pointer()
        elif isinstance(llvm_return_type, RIALIdentifiedStructType):
            llvm_return_type = llvm_return_type.as_pointer()
        else:
            llvm_return_type = llvm_return_type

        args: List[RIALVariable] = list()

        # Add class as implicit parameter
        if self.module.current_struct is not None and not modifier.static:
            args.append(
                RIALVariable("this", self.module.current_struct.name,
                             self.module.current_struct, None))

        args.extend(self.visit(nodes[3]))

        llvm_args = list()

        # Map RIAL args to llvm arg types
        for arg in args:
            if arg.name.endswith("..."):
                continue

            # Pointer for array and struct types
            if isinstance(arg.llvm_type,
                          RIALIdentifiedStructType) or isinstance(
                              arg.llvm_type, ArrayType):
                llvm_type = arg.llvm_type.as_pointer()
            else:
                llvm_type = arg.llvm_type
            llvm_args.append(llvm_type)

        # If the function has the NoMangleAttribute we need to use the normal name
        if 'noduplicate' in self.attributes:
            if modifier.static:
                raise PermissionError(
                    "NoMangle for static function doesn't work")
            full_function_name = name
        else:
            if modifier.static:
                if self.module.current_struct is None:
                    raise PermissionError(
                        "Static functions can only be declared in types")
                full_function_name = self.module.get_unique_function_name(
                    f"{self.module.current_struct.name}::{name}",
                    [arg.rial_type for arg in args])
            else:
                full_function_name = self.module.get_unique_function_name(
                    name, [arg.rial_type for arg in args])

        # Check if main method
        if name.endswith("main") and self.module.name.endswith("main"):
            self.attributes.add('alwaysinline')

            # Check that main method returns either Int32 or void
            if return_type != "Int32" and return_type != "Void":
                log_fail(
                    f"Main method must return an integer status code or void, {return_type} given!"
                )

        # Hasn't been declared previously, redeclare the function type here
        func_type = FunctionType(llvm_return_type, llvm_args, False)

        # Create the actual function in IR
        func = RIALFunction(self.module, func_type, full_function_name, name)
        func.linkage = linkage
        func.calling_convention = self.default_cc
        func.definition = FunctionDefinition(
            return_type, access_modifier, args,
            self.module.current_struct is not None
            and self.module.current_struct or "", unsafe)
        func.attributes = self.attributes

        # Update args
        for i, arg in enumerate(func.args):
            arg.name = args[i].name
            args[i].value = arg

        # If it has no body we do not need to go through it later as it's already declared with this method.
        if not len(nodes) > 4:
            with self.module.create_or_enter_function_body(func):
                self.module.builder.ret_void()
            raise Discard()

        token = nodes[2]
        metadata_token = MetadataToken(token.type, token.value)
        metadata_token.metadata["func"] = func
        metadata_token.metadata["body_start"] = 4
        nodes.remove(token)
        nodes.insert(0, metadata_token)

        return Tree('function_decl', nodes)
Beispiel #6
0
    def extension_function_decl(self, tree: Tree):
        nodes = tree.children
        modifier: DeclarationModifier = nodes[0]

        if isinstance(modifier, MetadataToken):
            return tree

        if modifier.static:
            raise PermissionError("Extension functions cannot be static")

        access_modifier: AccessModifier = modifier.access_modifier
        unsafe: bool = modifier.unsafe
        linkage = access_modifier.get_linkage()
        name = nodes[2].value
        llvm_return_type = self.module.get_definition(nodes[1])

        if isinstance(llvm_return_type, RIALIdentifiedStructType):
            return_type = llvm_return_type.name
        else:
            return_type = map_shortcut_to_type('.'.join(nodes[1]))

        # Builtins can be passed as raw values
        if is_builtin_type(return_type):
            llvm_return_type = llvm_return_type
        # Pointer to first element for still normal arrays
        elif re.match(r".+\[[0-9]+\]$", return_type) is not None:
            llvm_return_type = llvm_return_type.as_pointer()
        elif isinstance(llvm_return_type, RIALIdentifiedStructType):
            llvm_return_type = llvm_return_type.as_pointer()
        else:
            llvm_return_type = llvm_return_type

        # Extension functions cannot be declared inside other classes.
        if self.module.current_struct is not None:
            log_fail(
                f"Extension function {name} cannot be declared inside another class!"
            )
            raise Discard()

        args: List[RIALVariable] = list()
        this_type = self.module.get_definition(nodes[3])

        if isinstance(this_type, RIALIdentifiedStructType):
            rial_type = this_type.name
        else:
            rial_type = map_shortcut_to_type('.'.join(nodes[3]))

        args.append(RIALVariable(nodes[4].value, rial_type, this_type, None))

        if isinstance(nodes[5],
                      Tree) and nodes[5].data == "function_decl_args":
            args.extend(self.visit(nodes[5]))
            body_start = 6
        else:
            body_start = 5

        # If the function has the NoMangleAttribute we need to use the normal name
        if 'noduplicate' in self.attributes:
            full_function_name = name
        else:
            full_function_name = self.module.get_unique_function_name(
                name, [arg.rial_type for arg in args])

        llvm_args = list()

        # Map RIAL args to llvm arg types
        for arg in args:
            if arg.name.endswith("..."):
                continue

            # Builtins can be passed as raw values
            if is_builtin_type(arg.rial_type):
                llvm_args.append(arg.llvm_type)
            # Pointer to first element for still normal arrays
            elif re.match(r".+\[[0-9]+\]$", arg.rial_type) is not None:
                llvm_args.append(arg.llvm_type.as_pointer())
            elif isinstance(arg.llvm_type, RIALIdentifiedStructType):
                llvm_args.append(arg.llvm_type.as_pointer())
            else:
                llvm_args.append(arg.llvm_type)

        # Hasn't been declared previously, redeclare the function type here
        func_type = FunctionType(llvm_return_type, llvm_args)

        # Create the actual function in IR
        func = RIALFunction(self.module, func_type, full_function_name, name)
        func.linkage = linkage
        func.calling_convention = self.default_cc
        func.definition = FunctionDefinition(return_type, access_modifier,
                                             args, args[0].rial_type, unsafe)

        # Update args
        for i, arg in enumerate(func.args):
            arg.name = args[i].name
            args[i].value = arg

        # If it has no body we can discard it now.
        if len(nodes) <= body_start:
            with self.module.create_or_enter_function_body(func):
                self.module.builder.ret_void()
            raise Discard()

        token = nodes[2]
        metadata_token = MetadataToken(token.type, token.value)
        metadata_token.metadata["func"] = func
        metadata_token.metadata["body_start"] = body_start
        nodes.remove(token)
        nodes.insert(0, metadata_token)

        return Tree('function_decl', nodes)
Beispiel #7
0
    def external_function_decl(self, tree: Tree):
        nodes = tree.children
        modifier: DeclarationModifier = nodes[0]

        if isinstance(modifier, MetadataToken):
            return tree

        if modifier.static:
            raise PermissionError("External functions cannot be static")

        access_modifier: AccessModifier = modifier.access_modifier
        unsafe: bool = modifier.unsafe
        name = nodes[2].value
        llvm_return_type = self.module.get_definition(nodes[1])

        if isinstance(llvm_return_type, RIALIdentifiedStructType):
            return_type = llvm_return_type.name
        else:
            return_type = map_shortcut_to_type('.'.join(nodes[1]))

        # Builtins can be passed as raw values
        if is_builtin_type(return_type):
            llvm_return_type = llvm_return_type
        # Pointer to first element for still normal arrays
        elif re.match(r".+\[[0-9]+\]$", return_type) is not None:
            llvm_return_type = llvm_return_type.as_pointer()
        elif isinstance(llvm_return_type, RIALIdentifiedStructType):
            llvm_return_type = llvm_return_type.as_pointer()
        else:
            llvm_return_type = llvm_return_type

        # External functions cannot be declared in a struct
        if self.module.current_struct is not None:
            log_fail(
                f"External function {name} cannot be declared inside a class!")
            raise Discard()

        args: List[RIALVariable] = self.visit(nodes[3])

        llvm_args = list()

        # Map RIAL args to llvm arg types
        for arg in args:
            if arg.name.endswith("..."):
                continue

            # Pointer for array and struct types
            if isinstance(arg.llvm_type,
                          RIALIdentifiedStructType) or isinstance(
                              arg.llvm_type, ArrayType):
                llvm_type = arg.llvm_type.as_pointer()
            else:
                llvm_type = arg.llvm_type
            llvm_args.append(llvm_type)

        var_args = len(args) > 0 and args[-1].name.endswith("...")

        # Hasn't been declared previously, redeclare the function type here
        func_type = FunctionType(llvm_return_type, llvm_args, var_args)

        # Create function definition
        func = RIALFunction(self.module, func_type, name, name)
        func.linkage = "external"
        func.calling_convention = "ccc"
        func.definition = FunctionDefinition(return_type,
                                             access_modifier,
                                             args,
                                             unsafe=True)
        func.attributes = self.attributes

        # Update args
        for i, arg in enumerate(func.args):
            arg.name = args[i].name
            args[i].value = arg

        raise Discard()
Beispiel #8
0
    def gen_function_call(self,
                          candidates: List,
                          arguments: List[RIALVariable],
                          implicit_parameter=None):
        if len(candidates) > 1:
            from rial.ir.RIALModule import RIALModule
            for duplicate in candidates:
                if isinstance(duplicate, RIALVariable):
                    # Check if wrong module
                    if implicit_parameter is not None and isinstance(
                            implicit_parameter, RIALModule):
                        if duplicate.is_global and duplicate.value.parent.name != implicit_parameter.name:
                            candidates.remove(duplicate)
                            continue
                    # Check if function
                    if not isinstance(duplicate.llvm_type, ir.FunctionType):
                        candidates.remove(duplicate)
                        continue
                elif isinstance(duplicate, RIALIdentifiedStructType):
                    # Check if wrong module
                    if implicit_parameter is not None and isinstance(
                            implicit_parameter, RIALModule):
                        if duplicate.module_name != implicit_parameter.name:
                            candidates.remove(duplicate)
                            continue
                elif isinstance(duplicate, RIALFunction):
                    # Check if wrong module
                    if implicit_parameter is not None and isinstance(
                            implicit_parameter, RIALModule):
                        if duplicate.module.name != implicit_parameter.name:
                            candidates.remove(duplicate)
                            continue
                else:
                    candidates.remove(duplicate)
                    continue

            if len(candidates) > 1:
                candids = list()
                for duplicate in candidates:
                    if isinstance(duplicate, RIALFunction):
                        # Check arguments
                        if len(arguments) != len(
                                duplicate.definition.rial_args):
                            continue

                        for i, rial_arg in enumerate(
                                duplicate.definition.rial_args):
                            if rial_arg.rial_type != arguments[i].rial_type:
                                candidates.remove(duplicate)
                                break

                        candids.append(duplicate)
                    elif isinstance(duplicate, RIALVariable):
                        # Check arguments against function_type parsed to rial_type
                        for i, arg in duplicate.llvm_type.args:
                            if arguments[i].rial_type != map_llvm_to_type(
                                    arg.type):
                                candidates.remove(duplicate)
                                break
                        candids.append(duplicate)
                candidates = candids

            if len(candidates) == 1:
                func = candidates[0]
            else:
                raise KeyError(candidates)
        elif len(candidates) == 0:
            raise KeyError(candidates)
        else:
            func = candidates[0]

        if isinstance(func, RIALFunction):
            if func.definition.unsafe:
                from rial.util.only_allowed_in_unsafe import only_allowed_in_unsafe
                with only_allowed_in_unsafe(
                        "Calls to unsafe or external functions are only allowed in unsafe blocks and functions!"
                ):
                    pass

            args = list()
            for arg in arguments:
                # Builtins can be passed but need to be loaded
                if is_builtin_type(arg.rial_type):
                    args.append(arg.get_loaded_if_variable(self.module))

                # Pointer to first element for still normal arrays
                elif re.match(r".+\[[0-9]+\]$", arg.rial_type) is not None:
                    args.append(self.gep(arg.value, [Int32(0), Int32(0)]))

                else:
                    args.append(arg.value)

            # Check if it exists in the current module
            if self.module.get_global_safe(func.name) is None:
                func = self.module.declare_function(
                    func.name, func.canonical_name, func.function_type,
                    func.linkage, func.calling_convention, func.definition)

            call_instr = self.call(func, args)

            return RIALVariable(
                f"call_{func.name}", func.definition.rial_return_type,
                self.module.get_definition(
                    func.definition.rial_return_type.split('.')), call_instr)
        elif isinstance(func, RIALIdentifiedStructType):
            from rial.ir.RIALModule import RIALModule
            mod: RIALModule = self.module
            funcs = mod.get_functions_by_canonical_name(
                f"{func.name}_constructor")
            allocad = self.alloca(func)
            variable = RIALVariable(f"{func.name}_allocad", func.name, func,
                                    allocad)
            arguments.insert(0, variable)

            # Only if we found at least one constructor. Some structs may not need constructors.
            if len(funcs) > 0:
                self.gen_function_call(funcs, arguments)
            elif len(arguments) > 1:
                raise KeyError(func.name, "constructor")

            return variable

        # TODO: Call to variable

        return None