Beispiel #1
0
    def variable_decl(self, tree: Tree):
        nodes = tree.children
        identifier = nodes[0].value
        value = self.transform_helper(nodes[2])

        if isinstance(value, RIALFunction):
            variable = self.module.builder.alloca(value.function_type,
                                                  name=identifier)
            self.module.builder.store(self.module.builder.load(value),
                                      variable)
            variable = RIALVariable(identifier,
                                    str(value.function_type).replace(
                                        "i8*", "Char[]"),
                                    value.function_type,
                                    variable,
                                    identified_variable=True)
        elif isinstance(value, RIALVariable):
            if value.identified_variable or not value.is_variable:
                variable = self.module.builder.alloca(value.llvm_type,
                                                      name=identifier)
                self.module.builder.store(
                    value.get_loaded_if_variable(self.module), variable)
                variable = RIALVariable(identifier, value.rial_type,
                                        value.llvm_type, variable)
            else:
                variable = value
        else:
            raise TypeError(value, nodes)

        self.module.current_block.add_named_value(identifier, variable)

        return variable
Beispiel #2
0
    def array_access(self, tree: Tree):
        nodes = tree.children
        variable: RIALVariable = self.transform_helper(nodes[0])
        index: RIALVariable = self.transform_helper(nodes[1])

        if isinstance(variable, ir.Type):
            ty: ir.Type = variable
            number = index
            assert isinstance(number, RIALVariable)

            name = map_llvm_to_type(ty)

            if isinstance(number.value, ir.Constant):
                number = number.value.constant
                arr_type = ir.ArrayType(ty, number)
                allocated = self.module.builder.alloca(arr_type)
                name = f"{name}[{number}]"
            elif number.is_variable:
                number = self.module.builder.load(number.value)
                arr_type = ty.as_pointer()
                allocated = self.module.builder.alloca(ty, number)
                allocated = self.module.builder.gep(allocated, [Int32(0)])
                name = f"{name}[]"
            else:
                number = number.value
                arr_type = ty.as_pointer()
                allocated = self.module.builder.alloca(ty, number)
                allocated = self.module.builder.gep(allocated, [Int32(0)])
                name = f"{name}[]"

            return RIALVariable(f"array_{name}", name, arr_type, allocated)

        if not variable.rial_type.endswith("]"):
            raise TypeError(variable)

        # Check if it's a "GEP array" as we only need one index then
        if isinstance(variable.value,
                      ir.GEPInstr) or variable.rial_type.endswith("[]"):
            indices = [index.get_loaded_if_variable(self.module)]
        else:
            indices = [Int32(0), index.get_loaded_if_variable(self.module)]

        var = self.module.builder.gep(variable.value, indices)

        return RIALVariable(
            f"{variable.name}[{index}]", variable.array_element_type,
            isinstance(variable.llvm_type, ir.ArrayType)
            and variable.llvm_type.element or variable.llvm_type.pointee, var)
Beispiel #3
0
 def not_rule(self, nodes):
     return Tree('equal', [
         nodes[0],
         Token('EQUAL', '=='),
         RIALVariable("number", "Int1", ir.IntType(1),
                      ir.Constant(ir.IntType(1), 0))
     ])
    def struct_body(self, tree: Tree):
        nodes = tree.children
        collected = {'properties': [], 'function_decls': []}

        for node in nodes:
            if isinstance(node, Tree):
                if node.data == "struct_property_declaration":
                    variable = node.children
                    acc_modifier = variable[0].access_modifier
                    llvm_type = self.module.get_definition(variable[1])

                    if isinstance(llvm_type, RIALIdentifiedStructType):
                        rial_type = llvm_type.name
                    else:
                        rial_type = map_llvm_to_type(llvm_type)

                    variable_name = variable[2].value
                    variable_value = None

                    if len(variable) > 3:
                        variable_value = variable[3]
                    collected['properties'].append(
                        RIALVariable(variable_name, rial_type, llvm_type,
                                     variable_value, acc_modifier))
                elif node.data == "function_decl" or node.data == "attributed_function_decl":
                    collected['function_decls'].append(node)
        return collected
Beispiel #5
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 #6
0
    def char(self, nodes):
        value = nodes[0].value.strip("'")
        # Parse escape codes to be correct
        value = eval("'{}'".format(value))
        name = ".const.char.%s" % good_hash(value)

        if len(value) == 0:
            value = '\00'

        value = ord(value)
        const_char = ir.Constant(LLVMUIntType(8), value)

        return RIALVariable(name, "Char", const_char.type, const_char)
Beispiel #7
0
    def math(self, tree: Tree):
        nodes = tree.children
        left: RIALVariable = self.transform_helper(nodes[0])
        op = nodes[1].type
        right: RIALVariable = self.transform_helper(nodes[2])

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

        if left.rial_type != right.rial_type:
            raise TypeError(left, op, right)

        result = None
        left_val = left.get_loaded_if_variable(self.module)
        right_val = right.get_loaded_if_variable(self.module)

        if op == "PLUS":
            if isinstance(left.llvm_type, ir.IntType):
                result = self.module.builder.add(left_val, right_val)
            elif isinstance(left.llvm_type, ir.types._BaseFloatType):
                result = self.module.builder.fadd(left_val, right_val)
        elif op == "MINUS":
            if isinstance(left.llvm_type, ir.IntType):
                result = self.module.builder.sub(left_val, right_val)
            elif isinstance(left.llvm_type, ir.types._BaseFloatType):
                result = self.module.builder.fsub(left_val, right_val)
        elif op == "MUL":
            if isinstance(left.llvm_type, ir.IntType):
                result = self.module.builder.mul(left_val, right_val)
            elif isinstance(left.llvm_type, ir.types._BaseFloatType):
                result = self.module.builder.fmul(left_val, right_val)
        elif op == "DIV":
            if isinstance(left.llvm_type, LLVMUIntType):
                result = self.module.builder.udiv(left_val, right_val)
            elif isinstance(left.llvm_type, ir.IntType):
                result = self.module.builder.sdiv(left_val, right_val)
            elif isinstance(left.llvm_type, ir.types._BaseFloatType):
                result = self.module.builder.fdiv(left_val, right_val)
        elif op == "REM":
            if isinstance(left.llvm_type, LLVMUIntType):
                result = self.module.builder.urem(left_val, right_val)
            elif isinstance(left.llvm_type, ir.IntType):
                result = self.module.builder.srem(left_val, right_val)
            elif isinstance(left.llvm_type, ir.types._BaseFloatType):
                result = self.module.builder.frem(left_val, right_val)

        if result is None:
            raise TypeError(left, op, right)

        return RIALVariable(f"tmp_{op}", left.rial_type, left.llvm_type, result)
Beispiel #8
0
    def _create_function_body(self, func: RIALFunction):
        with self._enter_function_body(func):
            # Allocate new variables for passed arguments unless they're already pointers
            for i, arg in enumerate(func.args):
                if func.definition.rial_args[i].is_variable:
                    variable = func.definition.rial_args[i]
                else:
                    variable = self.builder.alloca(
                        func.definition.rial_args[i].llvm_type)
                    self.builder.store(arg, variable)
                    variable = RIALVariable(
                        arg.name, func.definition.rial_args[i].rial_type,
                        arg.type, variable)

                self.current_block.add_named_value(arg.name, variable)

            yield
Beispiel #9
0
    def function_decl_args(self, tree: Tree):
        nodes = tree.children
        args: List[RIALVariable] = list()

        i = 0
        var_args = False
        while i < len(nodes):
            if var_args:
                raise KeyError()

            # Params
            if not isinstance(nodes[i], List):
                var_args = True
                i += 1

            ty = self.module.get_definition(nodes[i])
            i += 1
            name = nodes[i].value
            i += 1

            if var_args:
                name = f"{name}..."

            if isinstance(ty, RIALVariable):
                rial_type = ty.rial_type
                llvm_type = ty.llvm_type
            elif isinstance(ty, RIALIdentifiedStructType):
                rial_type = ty.name
                llvm_type = ty
            elif isinstance(ty, RIALModule):
                raise KeyError(ty)
            elif isinstance(ty, RIALFunction):
                rial_type = str(ty.function_type)
                llvm_type = ty.function_type
            elif isinstance(ty, Type):
                rial_type = map_shortcut_to_type('.'.join(nodes[i - 2]))
                llvm_type = ty
            else:
                rial_type = None
                llvm_type = None

            args.append(RIALVariable(name, rial_type, llvm_type, None))

        return args
Beispiel #10
0
    def llvm_ir(self, tree: Tree):
        # Check for unsafe
        with only_allowed_in_unsafe(
                "llvm_ir is only allowed in unsafe functions or blocks!"):
            pass
        nodes = tree.children
        llvm_ir: str = nodes[0].value.strip("\"")
        llvm_ir = eval("'{}'".format(llvm_ir))
        ty = None
        return_name = None

        for node in nodes[1:]:
            if isinstance(node, Tree):
                ty = self.transform_helper(node)
            elif isinstance(node, Token):
                return_name = node.value.strip("\"")

        self.module.builder.ir(ty, llvm_ir)

        if return_name is not None:
            if ty is None:
                raise KeyError(
                    "Need to specify type if you want to use @llvm_ir as a variable!"
                )
            if isinstance(ty, RIALIdentifiedStructType):
                ty_name = ty.name
            else:
                ty_name = map_llvm_to_type(ty)
            variable = self.module.builder.alloca(ty)
            self.module.builder.store(
                LLVMIRInstruction(ty, f"%\"{return_name}\""), variable)
            variable = RIALVariable("llvm_ir", ty_name, ty, variable)
            if return_name in self.module.current_block.named_values:
                raise KeyError(f"{return_name} has been previously defined!")
            self.module.current_block.add_named_value(return_name, variable)
            return variable

        raise Discard()
Beispiel #11
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 #12
0
    def declare_global(
            self,
            name: str,
            rial_type: str,
            llvm_type: ir.Type,
            linkage: str,
            initializer: Optional[ir.Constant],
            access_modifier: AccessModifier = AccessModifier.PRIVATE,
            constant: bool = False):
        if self.get_global_safe(name) is not None:
            raise KeyError(name)

        glob = ir.GlobalVariable(self, llvm_type, name)
        glob.linkage = linkage
        glob.global_constant = constant

        if initializer is not None:
            glob.initializer = initializer

        variable = RIALVariable(name, rial_type, llvm_type, glob,
                                access_modifier)
        self.global_variables[name] = variable

        return variable
Beispiel #13
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
Beispiel #14
0
 def false(self, nodes):
     return RIALVariable("false", "Int1", FALSE.type, FALSE)
Beispiel #15
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 #16
0
 def number(self, nodes: List):
     value: str = nodes[0].value
     number = convert_number_to_constant(value)
     return RIALVariable("number", map_llvm_to_type(number.type),
                         number.type, number)
Beispiel #17
0
    def sizeof(self, tree: Tree):
        nodes = tree.children
        variable: Optional[RIALVariable] = self.transform_helper(nodes[0])

        assert variable is None or isinstance(
            variable, RIALVariable) or isinstance(variable, ir.Type)

        # "Variable" is actually a type name that we need to extract and then get the size of
        if variable is None:
            ty = self.module.get_definition(
                [node.value for node in nodes[0].children])

            if ty is None:
                raise TypeError(nodes)

            if isinstance(ty, RIALIdentifiedStructType):
                name = ty.name
            else:
                name = map_llvm_to_type(ty)

            size = Int32(CompilationManager.codegen.get_size(ty))

            return RIALVariable(f"sizeof_{name}", "Int32", Int32, size)
        elif isinstance(variable, ir.Type):
            size = Int32(CompilationManager.codegen.get_size(variable))

            return RIALVariable(f"sizeof_{variable}", "Int32", Int32, size)
        elif isinstance(variable.llvm_type, ir.ArrayType) and not isinstance(
                variable.value, ir.GEPInstr):
            ty: ir.ArrayType = variable.llvm_type

            if isinstance(ty.count, int):
                size = CompilationManager.codegen.get_size(
                    ty.element) * ty.count
                size = Int32(size)
            elif isinstance(ty.count, ir.Constant):
                size = CompilationManager.codegen.get_size(
                    ty.element) * ty.count.constant
                size = Int32(size)
            else:
                size = CompilationManager.codegen.get_size(ty.element)
                size = self.module.builder.mul(
                    Int32(size), self.module.builder.load(ty.count))

            return RIALVariable(f"sizeof_{ty.element}[{ty.count}]", "Int32",
                                Int32, size)
        # If an array wasn't caught in the previous elif, then it doesn't have a set length and needs to be sized this way.
        elif isinstance(variable.value,
                        ir.GEPInstr) or variable.rial_type.endswith("]"):
            # This is worst case and practically only reserved for GEPs
            # This is worst case as it cannot be optimized away.
            base = self.module.builder.ptrtoint(
                self.module.builder.gep(variable.value, [Int32(0)]), Int32)
            val = self.module.builder.ptrtoint(
                self.module.builder.gep(variable.value, [Int32(1)]), Int32)
            size = self.module.builder.sub(val, base)

            return RIALVariable("sizeof_unknown", "Int32", Int32, size)
        else:
            size = Int32(
                CompilationManager.codegen.get_size(variable.llvm_type))

            return RIALVariable(f"sizeof_{variable.rial_type}", "Int32", Int32,
                                size)
Beispiel #18
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 #19
0
 def true(self, nodes):
     return RIALVariable("true", "Int1", TRUE.type, TRUE)
Beispiel #20
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 #21
0
    def _get_recursive_definition(
        self, identifier: str,
        variable: Optional[Union[RIALVariable, RIALFunction, ir.Module,
                                 RIALIdentifiedStructType, ir.Type]]
    ) -> Optional[Union[RIALVariable, RIALFunction, ir.Module,
                        RIALIdentifiedStructType, ir.Type]]:
        assert isinstance(identifier, str)

        if variable is not None:
            if isinstance(variable, RIALModule):
                return variable.get_definition([identifier])
            elif isinstance(variable, RIALFunction):
                return None
            elif isinstance(variable, RIALIdentifiedStructType):
                return None
            elif isinstance(variable, RIALVariable):
                if not isinstance(variable.llvm_type,
                                  RIALIdentifiedStructType):
                    return None

                struct: RIALIdentifiedStructType = variable.llvm_type

                # Check properties
                if identifier in struct.definition.properties:
                    if self.builder is None:
                        return None

                    prop = struct.definition.properties[identifier]
                    return RIALVariable(
                        identifier, prop[1].rial_type, prop[1].llvm_type,
                        self.builder.gep(variable.value,
                                         [Int32(0), Int32(prop[0])]),
                        prop[1].access_modifier)

                # Check functions
                from rial.compilation_manager import CompilationManager
                mod = struct.module_name == self.name and self or CompilationManager.modules[
                    struct.module_name]
                variable = mod.get_global_safe(identifier)

                if variable is not None:
                    if isinstance(variable, RIALFunction):
                        if len(variable.definition.rial_args
                               ) > 0 and variable.definition.rial_args[
                                   0].rial_type == struct.name:
                            return variable

            return None

        # Check builtin's first
        identifier = map_shortcut_to_type(identifier)
        variable = map_type_to_llvm(identifier)

        if variable is None:
            # Arrays
            match = re.match(r"^([^\[]+)\[([0-9]+)?\]$", identifier)

            if match is not None:
                ty = match.group(1)
                count = match.group(2)
                definition = self.get_definition([ty])
                if definition is not None:
                    if count is not None:
                        return ir.ArrayType(definition, int(count))
                    else:
                        return definition.as_pointer()

            # Function type
            match = re.match(r"^([^(]+)\(([^,)]+\s*,?\s*)*\)$", identifier)

            if match is not None:
                return_type = ""
                arg_types = list()
                var_args = False
                for i, group in enumerate(match.groups()):
                    if i == 0:
                        return_type = group.strip()
                    elif group == "...":
                        var_args = True
                    elif group is None:
                        break
                    else:
                        arg_types.append(group.strip())

                return ir.FunctionType(self.get_definition([
                    return_type
                ]), [self.get_definition([arg]) for arg in arg_types],
                                       var_args)

        # Check local variables first
        if variable is None and self.current_block is not None:
            variable = self.current_block.get_named_value(identifier)

        # Check module-local global variables next
        if variable is None:
            variable = identifier in self.global_variables and self.global_variables[
                identifier] or None

        # Check structs next
        if variable is None:
            variable = identifier in self.get_identified_types() and \
                       self.get_identified_types()[identifier] or None

        # Check functions next
        if variable is None:
            variable = self.get_global_safe(identifier)

        # Check module imports
        if variable is None:
            mod = identifier in self.dependencies and self.dependencies[
                identifier] or None

            if mod is not None:
                from rial.compilation_manager import CompilationManager
                variable = CompilationManager.modules[mod]

        # Check always imported last
        if variable is None and not self.name.startswith("rial:builtin:"):
            from rial.compilation_manager import CompilationManager
            for always_imported in CompilationManager.always_imported:
                mod = CompilationManager.modules[always_imported]
                variable = mod.get_definition([identifier])

                if variable is not None:
                    break

        return variable
Beispiel #22
0
 def null(self, nodes):
     return RIALVariable("null", "Int8", NULL.type, NULL)