Exemple #1
0
    async def compile(self, ctx: CompileContext):
        fn_type: Function = ctx.top_function.type
        if self.expr is None:
            if not isinstance(fn_type.returns, Void):
                raise self.error(
                    f"Void return in function of return type: '{fn_type.returns}'"
                )

            # all scopes but the function scope
            for i in reversed(ctx.scope_stack[1:]):
                ctx.emit(Epilog(i))

            ctx.emit(Return(ctx.top_function))
        else:
            expr_type = await self.expr.type

            if not fn_type.returns.implicitly_casts_to(expr_type):
                raise self.error(
                    f"Return type '{expr_type}' cannot be casted to '{fn_type.returns}'."
                )

            reg = await self.expr.compile(ctx)

            # all scopes but the function scope
            for i in reversed(ctx.scope_stack[1:]):
                ctx.emit(Epilog(i))

            if reg.size != fn_type.returns.size:
                reg0 = reg.resize(fn_type.returns.size, fn_type.returns.signed)
                ctx.emit(Resize(reg, reg0))
                reg = reg0
            ctx.emit(Return(ctx.top_function, reg))
Exemple #2
0
    async def compile(self, ctx: CompileContext) -> Register:
        op = {"|": "or", "^": "xor", "&": "and"}[self.op]

        lhs, rhs = await self.compile_meta(ctx)
        res = ctx.get_register(lhs.size, self.sign)
        ctx.emit(Binary(lhs, rhs, op, res))
        return res
Exemple #3
0
    async def compile(self, ctx: CompileContext) -> Register:
        lhs, rhs = await self.compile_meta(ctx)

        # if signed, emit signed comparison
        if self.sign:
            op = {
                '<=': CompType.leqs,
                '<': CompType.lts,
                '==': CompType.eq,
                '!=': CompType.neq,
                '>': CompType.gts,
                '>=': CompType.geqs
            }[self.op]
        else:
            op = {
                '<=': CompType.leq,
                '<': CompType.lt,
                '==': CompType.eq,
                '!=': CompType.neq,
                '>': CompType.gt,
                '>=': CompType.geq
            }[self.op]

        res = ctx.get_register(1)
        ctx.emit(Compare(lhs, rhs))
        ctx.emit(SetCmp(res, op))
        return res
Exemple #4
0
 async def load_lvalue(self, ctx: CompileContext) -> Register:
     reg: Register = await self.expr.compile(ctx)
     if reg.size != Pointer.size:
         reg0 = reg.resize(Pointer.size)
         ctx.emit(Resize(reg, reg0))
         reg = reg0
     return reg
Exemple #5
0
    async def compile(self, ctx: CompileContext) -> Register:
        var = await self.retrieve_variable()
        reg = ctx.get_register(
            var.type.size,
            var.type.signed)  # explicitly use the type size not the var size

        ctx.emit(LoadVar(var, reg))
        return reg
Exemple #6
0
    async def compile(self, ctx: CompileContext) -> Register:
        reg = await self.expr.compile(ctx)
        my_type = await self.type

        res = reg.resize(my_type.size, my_type.signed)
        if self.op == "::" and reg.size != res.size:
            ctx.emit(Resize(reg, res))  # emit resize operation
        return res
Exemple #7
0
    async def compile(self, ctx: CompileContext) -> Register:
        my_type = await self.type
        ptr = await self.load_lvalue(ctx)

        if isinstance(my_type, Void):
            raise self.error("Cannot dereference a void pointer.")

        reg = ctx.get_register(my_type.size, my_type.signed)
        ctx.emit(Mov(reg, Dereference(ptr, reg.size)))
        return reg
Exemple #8
0
    async def load_lvalue(self, ctx: CompileContext) -> Register:
        var = await self.retrieve_variable()

        # if we load the lvalue when requested, error here since this is disallowed
        if var.lvalue_is_rvalue:
            raise self.error(
                f"Variable '{self.name}' has no lvalue information.")
        reg = ctx.get_register(types.Pointer(self.var.type).size)
        ctx.emit(LoadVar(var, reg, lvalue=True))
        return reg
Exemple #9
0
    async def compile(self, ctx: CompileContext) -> Register:
        lhs, rhs = await self.compile_meta(ctx)

        res = ctx.get_register(lhs.size, self.sign)

        op = {"+": "add", "-": "sub"}[self.op]

        if isinstance(await self.type, Pointer):
            # adding a pointer with an integer multiplies the integer side by the pointed to type
            (ptr_type, non_ptr) = ((self.left_type, rhs) if isinstance(
                self.left_type, Pointer) else (self.right_type, lhs))

            if isinstance(ptr_type.to, Void):
                raise self.error("Cannot perform pointer arithmetic on pointer to void.")

            ctx.emit(Binary.mul(non_ptr, Immediate(ptr_type.to.size, non_ptr.size)))
        elif (op == "sub"
              and isinstance(self.left_type, Pointer)
              and isinstance(self.right_type, Pointer)):
            if isinstance(self.left_type.to, Void):
                raise self.error("Cannot perform pointer arithmetic on pointer to void.")

            if self.left_type != self.right_type:
                raise self.error("Both sides of pointer subtraction must be the same type.")

            # subtracting two pointers of equal type yields the number of elements between them
            ctx.emit(Binary(lhs, rhs, op, res))
            ctx.emit(Binary.udiv(res, Immediate(self.left_type.to.size, res.size)))
            return res

        ctx.emit(Binary(lhs, rhs, op, res))
        return res
Exemple #10
0
    async def compile_as_arr(self, ctx: CompileContext) -> Register:
        """Compile an array literal but inline the inner values."""

        if (isinstance((await self.type).to, types.Array)
                and (not isinstance(self.first_elem, ArrayLiteral))):
            # Maybe just cast the internal type to a pointer.
            raise self.error(
                "Internal type is of array type but is not a literal.")

        if self.var is None:
            self.var = ctx.declare_unique_variable(await self.type)
            self.var.lvalue_is_rvalue = True

        base = ctx.get_register(types.Pointer.size)
        index = ctx.get_register(types.Pointer.size)

        ctx.emit(LoadVar(self.var, base))
        ctx.emit(Mov(index, base))

        elem_size = (await self.type).to.size

        for i in self.exprs:
            await i.compile_as_arr_helper(ctx, index)

        # NOTE: will we ever hit this?
        if self.float_size:
            ctx.emit(Binary.add(index, Immediate(elem_size * self.float_size)))

        return base
Exemple #11
0
    async def compile(self, ctx: CompileContext) -> Register:
        ptr: Register = await self.load_lvalue(ctx)
        if ptr.size != Pointer.size:
            ptr0 = ptr.resize(Pointer.size)
            ctx.emit(Resize(ptr, ptr0))
            ptr = ptr0

        # indexes that leave an array type dont dereference
        if isinstance(await self.type, Array):
            return ptr

        my_type = await self.type
        res = ctx.get_register(my_type.size, my_type.signed)
        ctx.emit(Mov(res, Dereference(ptr, res.size)))
        return res
Exemple #12
0
    def emit_savevar(cls, ctx: CompileContext, save: ir_object.SaveVar):
        var = save.variable

        # we need an extra register to store the temporary address
        temp_reg = ctx.get_register(2)

        if var.stack_offset is not None:  # load from a stack address
            yield ir_object.Mov(
                temp_reg, encoder.SpecificRegisters.bas)  # grab base pointer
            # load offset off of the base pointer
            if var.stack_offset < 0:
                instr = ir_object.Binary.sub
            else:
                instr = ir_object.Binary.add
            yield instr(
                temp_reg,
                ir_object.Immediate(abs(var.stack_offset), temp_reg.size))
        elif var.global_offset is not None:
            yield ir_object.Mov(temp_reg, var.global_offset)
        else:
            raise InternalCompileException(
                f"Variable had no stack or global offset: {var}")

        # emit the dereference and store
        yield ir_object.Mov(ir_object.Dereference(temp_reg, save.from_.size),
                            save.from_)
Exemple #13
0
    def emit_loadvar(cls, ctx: CompileContext, load: ir_object.LoadVar):  # pylint: disable=unused-argument
        var = load.variable

        if var.lvalue_is_rvalue and load.lvalue:
            raise InternalCompileException(
                f"Variable: {var} is marked that it's rvalue "
                "is it's lvalue and a lvalue load was requested.")

        # we need an extra register to store the temporary address
        temp_reg = ctx.get_register(2)

        if var.stack_offset is not None:  # load from a stack address
            yield ir_object.Mov(
                temp_reg, encoder.SpecificRegisters.bas)  # grab base pointer
            # load offset off of the base pointer
            if var.stack_offset < 0:
                instr = ir_object.Binary.sub
            else:
                instr = ir_object.Binary.add
            yield instr(
                temp_reg,
                ir_object.Immediate(abs(var.stack_offset), temp_reg.size))
        elif var.global_offset is not None:
            yield ir_object.Mov(temp_reg, var.global_offset)
        else:
            raise InternalCompileException(
                f"Variable had no stack or global offset: {var}")

        if var.lvalue_is_rvalue or load.lvalue:
            yield ir_object.Mov(load.to, temp_reg)
        else:
            yield ir_object.Mov(load.to,
                                ir_object.Dereference(temp_reg, load.to.size))
Exemple #14
0
    async def compile(self, ctx: CompileContext) -> Register:
        reg: Register = (await self.expr.compile(ctx))

        optype: Type = await self.type
        if not optype.signed:
            if self.op == "+":
                return reg  # '+' is a noop on unsigned types
            if self.op == "-":
                raise self.error("Unary negate has no meaning on unsigned types.")

        op = {"~": "binv",
              "!": "linv",
              "-": "neg",
              "+": "pos"}[self.op]

        ctx.emit(Unary(reg, op))
        return reg
Exemple #15
0
    async def compile(self, ctx: CompileContext) -> Register:
        lhs, rhs = await self.compile_meta(ctx)
        if rhs.sign:
            raise self.right.error(
                "RHS operand to a binary shift op must be unsigned.")

        res = ctx.get_register(lhs.size, self.sign)

        if self.op == "<<":
            op = "shl"
        elif self.op == ">>" and lhs.sign:
            op = "sar"
        else:
            op = "shr"

        ctx.emit(Binary(lhs, rhs, op, res))
        return res
Exemple #16
0
    async def compile(self, ctx: CompileContext) -> Register:
        lhs, rhs = await self.compile_meta(ctx)

        res = ctx.get_register(lhs.size, self.sign)

        if self.op == "*":
            op = "mul"
        elif self.op == "%":
            if self.sign:
                op = "imod"
            else:
                op = "umod"
        elif self.op == "/" and self.sign:
            op = "idiv"
        else:
            op = "udiv"

        ctx.emit(Binary(lhs, rhs, op, res))
        return res
Exemple #17
0
    async def compile_meta(self,
                           ctx: CompileContext) -> Tuple[Register, Register]:
        """Binary expression meta compile, returns registers of both side
        Both registers returned have equal size."""
        await self.resolve_types()  # force type resolution to typecheck

        lhs: Register = await self.left.compile(ctx)
        rhs: Register = await self.right.compile(ctx)

        # resize to the largest operand
        if lhs.size < rhs.size:
            lhs0 = lhs.resize(rhs.size, rhs.sign)
            ctx.emit(Resize(lhs, lhs0))
            lhs = lhs0
        elif rhs.size < lhs.size:
            rhs0 = rhs.resize(lhs.size, lhs.sign)
            ctx.emit(Resize(rhs, rhs0))
            rhs = rhs0

        return lhs, rhs
Exemple #18
0
    async def compile(self, ctx: CompileContext) -> Register:
        rhs: Register = (await self.right.compile(ctx))
        lhs: Register = (await self.left.load_lvalue(ctx))

        lhs_type = await self.left.type
        rhs_type = await self.right.type
        lhs_sign = lhs_type.signed
        lhs_size = lhs_type.size

        if not rhs_type.implicitly_casts_to(lhs_type):
            raise self.right.error(f"Incompatible rhs type '{rhs_type}' assignment to type '{lhs_type}'")

        if lhs_type.const:
            raise self.error("cannot assign to const type.")

        if lhs_size != rhs.size:
            rhs_ = rhs.resize(lhs_size, lhs_sign)
            ctx.emit(Resize(rhs, rhs_))
            rhs = rhs_
        ctx.emit(Mov(Dereference(lhs, rhs.size), rhs))
        return rhs
Exemple #19
0
    async def compile(self, ctx: CompileContext) -> Register:

        fun_typ: Function = await self.fun.type
        if not isinstance(fun_typ, Function):
            raise self.error("Called object is not a function.")


        # if we have varargs, just make sure we have enough arguments to fill the required arguments
        if fun_typ.varargs:
            invalid_len = len(self.args) < len(fun_typ.args)
        else:
            invalid_len = len(self.args) != len(fun_typ.args)

        if invalid_len:
            raise self.error(
                "Incorrect number of args to function.\n"
                f"Expected {len(fun_typ.args)} got {len(self.args)}")

        # check that the argument types are valid
        # If this is a varargs function then the extra args wont be typechecked
        arg_types = [(i, (await i.type)) for i in self.args]

        # zip the types from the declaration with the types in the call expression
        arg_iterator = enumerate(zip(fun_typ.args, arg_types))

        for arg_n, (lhs_type, (rhs_obj, rhs_type)) in arg_iterator:
            if not rhs_type.implicitly_casts_to(lhs_type):
                raise rhs_obj.error(
                    f"Argument {arg_n} to call '{self.fun.identifier}' was of "
                    f"type {rhs_type} instead of expected {lhs_type} and cannot be casted."
                )

        params = []

        for arg, typ in zip_longest(self.args, fun_typ.args):
            arg_reg = await arg.compile(ctx)

            if typ is not None and arg_reg.size != typ.size:
                arg_reg0 = arg_reg.resize(typ.size, typ.signed)
                ctx.emit(Resize(arg_reg, arg_reg0))
                arg_reg = arg_reg0

            params.append(arg_reg)

        fun: Register = await self.fun.compile(ctx)

        if isinstance(fun_typ.returns, Void):
            ctx.emit(Call(params, fun))
        else:
            await self.size
            result_reg = ctx.get_register(fun_typ.returns.size,
                                          fun_typ.returns.signed)
            ctx.emit(Call(params, fun, result_reg))
            return result_reg
Exemple #20
0
    async def compile(self, ctx: CompileContext) -> Register:
        my_type = await self.type

        ptr: Register = await self.arg.load_lvalue(ctx)
        size = my_type.size
        res, temp = (ctx.get_register(size, my_type.signed),
                     ctx.get_register(size))

        increment = 1
        if isinstance(my_type, Pointer):
            increment = my_type.to.size

        ctx.emit(Mov(res, Dereference(ptr, res.size)))
        ctx.emit(Binary(res, Immediate(increment, size), self.op, temp))
        ctx.emit(Mov(Dereference(ptr, temp.size), temp))
        return res
Exemple #21
0
    async def compile(self, ctx: CompileContext) -> Register:
        my_type = await self.type

        ptr: Register = await self.load_lvalue(ctx)
        tmp = ctx.get_register(my_type.size, my_type.signed)

        increment = 1
        # in the case of pointer increments, increment by the size of the pointer's underlying type
        if isinstance(my_type, Pointer):
            increment = my_type.to.size

        ctx.emit(Mov(tmp, Dereference(ptr, tmp.size)))
        ctx.emit(Binary(tmp, Immediate(increment, tmp.size), self.op))
        ctx.emit(Mov(Dereference(ptr, tmp.size), tmp))
        return tmp
Exemple #22
0
    async def load_lvalue(self, ctx: CompileContext) -> Register:
        atype = await self.arg.type

        if not isinstance(atype, (Pointer, Array)):
            raise self.error(f"Incompatible type to array index base {atype}")

        # don't allow void pointer arithmetic
        # This 'would' fail later with an error about requesting a register of size 0
        # But that would be less informative than catching the error now.
        if isinstance(atype.to, Void):
            raise self.error("Cannot perform pointer arithemetic on void pointers.")

        argument = await self.arg.compile(ctx)
        offset = await self.offset.compile(ctx)

        # get the size of the inner type if type.to is an array,
        # this will be the size of the internal array
        size = await self.size

        # make sure both the offset and the arguments are the correct size (size of a pointer)
        if argument.size != Pointer.size:
            argument0 = argument.resize(Pointer.size)
            ctx.emit(Resize(argument, argument0))
            argument = argument0

        if offset.size != Pointer.size:
            offset0 = offset.resize(Pointer.size)
            ctx.emit(Resize(offset, offset0))
            offset = offset0

        result = ctx.get_register(Pointer.size)
        # multiply to the size of the inner type of the pointer/ array
        ctx.emit(Binary.mul(offset, Immediate(size, offset.size)))
        ctx.emit(Binary.add(argument, offset, result))

        return result
Exemple #23
0
 async def compile_as_arr_helper(self, ctx: CompileContext, base: Register):
     if isinstance(self.first_elem, ArrayLiteral) and isinstance(
             await self.type, types.Array):
         for i in self.exprs:
             await i.compile_as_arr_helper(ctx, base)
     else:
         elem_type = (await self.type).to
         for i in self.exprs:
             r = await i.compile(ctx)
             if r.size != elem_type.size:
                 r0 = r.resize(elem_type.size, elem_type.signed)
                 ctx.emit(Resize(r, r0))
                 r = r0
             ctx.emit(Mov(Dereference(base, r.size), r))
             ctx.emit(
                 Binary.add(base, Immediate(r.size, types.Pointer.size)))
Exemple #24
0
 async def compile(self, ctx: CompileContext) -> Register:
     reg = ctx.get_register(self._type.size, self._type.signed)
     ctx.emit(Mov(reg, Immediate(self.lit, self._type.size)))
     return reg
Exemple #25
0
    async def compile_as_ref(self, ctx: CompileContext) -> Register:
        """Compile to the array literal and return a reference to the start.

        This function is for when an array of references is the wanted outcome.
        This function will not work on array types.
        """

        if self.var is None:
            self.var = ctx.declare_unique_variable(await self.type)
            self.var.lvalue_is_rvalue = True

        if (isinstance(self.first_elem, ArrayLiteral) and (not isinstance(
            (await self.type).to, types.Pointer))):
            raise self.error(
                "Cannot compile to references if internal array type is an array and not a pointer"
            )

        base = ctx.get_register(types.Pointer.size)
        index = ctx.get_register(types.Pointer.size)

        ctx.emit(LoadVar(self.var, base))
        ctx.emit(Mov(index, base))

        elem_type = (await self.type).to

        for i in self.exprs:
            r = await i.compile(ctx)

            if r.size != elem_type.size:
                r0 = r.resize(elem_type.size, elem_type.signed)
                ctx.emit(Resize(r, r0))
                r = r0

            ctx.emit(Mov(Dereference(index, r.size), r))
            ctx.emit(Binary.add(index, Immediate(r.size, types.Pointer.size)))

        if self.float_size:
            # fill in missing values
            ctx.emit(
                Binary.add(
                    index,
                    Immediate(elem_type.size * self.float_size, index.size)))

        return base
Exemple #26
0
    async def compile(self, ctx: CompileContext):
        my_type = await self.type

        if isinstance(my_type, Void):
            raise self.error("Cannot create variables with void type.")

        if self.val is None:  # just a declaration, no types so exit here
            var = ctx.declare_variable(self.name, my_type)
            if isinstance(my_type, Array):
                var.type = Pointer(my_type.to)
                var.lvalue_is_rvalue = True
            return

        if isinstance(self.val, ArrayLiteral):
            await self.val.insert_type(my_type)
            await self.val.check_types(my_type)

            # copy back the type of the literal to retrieve the size info
            my_type = await self.val.type

            var = ctx.declare_variable(self.name, my_type)
            if isinstance(my_type, Array):
                # setup storage location for the array
                var.lvalue_is_rvalue = True
                self.val.var = var
                await self.val.compile(ctx)
            else:
                reg: Register = await self.val.compile(ctx)
                if reg.size != var.size:
                    reg0 = reg.resize(var.size, var.type.signed)
                    ctx.emit(Resize(reg, reg0))
                    reg = reg0
                ctx.emit(SaveVar(var, reg))
            var.type = Pointer(var.type.to)
            #  var size is now set to the size of the array

        elif isinstance(self.val, ExpressionObject):
            val_type = await self.val.type

            if isinstance(val_type, Void):
                raise self.val.error("Cannot create variables with void type.")

            if not val_type.implicitly_casts_to(my_type):
                raise self.error(
                    f"Specified type {my_type} does not match value type {val_type}"
                )

            var = ctx.declare_variable(self.name, my_type)
            reg: Register = await self.val.compile(ctx)
            if reg.size != var.size:
                reg0 = reg.resize(var.size, var.type.signed)
                ctx.emit(Resize(reg, reg0))
                reg = reg0
            ctx.emit(SaveVar(var, reg))
Exemple #27
0
    async def compile(self, ctx: CompileContext):
        test_jump = JumpTarget()
        continue_jump = JumpTarget()
        end_jump = JumpTarget()

        # the start of the loop (test the condition)
        ctx.emit(test_jump)
        cond: Register = await self.cond.compile(ctx)  # evaluate condition

        # if nonzero, jump over the jump to the end
        # (Alternatively, test for zero and jump to end if zero, but this is 1 op (Jump), vs 3 (Test, Set, Jump))
        ctx.emit(Jump(continue_jump, cond))
        ctx.emit(Jump(end_jump))
        ctx.emit(continue_jump)
        await self.body.compile(ctx)
        ctx.emit(Jump(test_jump))
        ctx.emit(end_jump)
Exemple #28
0
    async def compile(self, ctx: CompileContext):
        cond: Register = await self.cond.compile(ctx)

        # if we have an else clause we rearrange to jump over that
        # instead of inverting the condition to jump over the truth case
        if self.else_:
            end_jump, else_jump = JumpTarget(), JumpTarget()

            ctx.emit(Jump(else_jump, cond))
            await self.else_.compile(ctx)
            ctx.emit(Jump(end_jump))
            ctx.emit(else_jump)
            await self.body.compile(ctx)
            ctx.emit(end_jump)

        else:
            if_body, end_jump = JumpTarget(), JumpTarget()

            ctx.emit(Jump(if_body, cond))
            ctx.emit(Jump(end_jump))
            ctx.emit(if_body)
            await self.body.compile(ctx)
            ctx.emit(end_jump)
Exemple #29
0
    async def compile(self, ctx: CompileContext) -> Register:

        lhs_type, rhs_type = await self.left.type, await self.right.type

        if not rhs_type.implicitly_casts_to(lhs_type):
            raise self.error(
                f"Right argument to boolean operator: '{self.right.matched_region}'\n"
                f"of type: {rhs_type} cannot be casted to left argument: '{self.left.matched_region}'\n"
                f"of type {lhs_type}")

        if isinstance(lhs_type, Void):
            raise self.left.error("Void type argument to boolean operator")
        if isinstance(rhs_type, Void):
            raise self.right.error("Void type argument to boolean operator")

        r1: Register = await self.left.compile(ctx)
        ctx.emit(Compare(r1, Immediate(0, r1.size)))
        target = JumpTarget()
        op = {'or': CompType.neq, 'and': CompType.eq}[self.op]

        cond = ctx.get_register(1)
        ctx.emit(SetCmp(cond, op))

        ctx.emit(Jump(target, cond))
        r2: Register = (await self.right.compile(ctx))
        if r2.size != r1.size:
            r2_ = r2.resize(r1.size, r1.sign)
            ctx.emit(Resize(r2, r2_))
            r2 = r2_
        ctx.emit(Mov(r1, r2))
        ctx.emit(target)
        return r1
Exemple #30
0
 async def compile(self, ctx: CompileContext):
     registers = [await i.compile(ctx) for i in self.exprs]
     for i in self.body:
         params = i.resolve_params(registers)
         ctx.emit(MachineInstr(i.name, i.size, params))