Пример #1
0
    def fix_block_count(self, il_code):
        """Convert block and count so that block is in {1, 2, 4, 8}.
        The Rel commands require that block be in {1, 2, 4, 8}. If the given block value is not in this set, we multiply
        count and divide block by an appropriate value so that block is in {1, 2, 4, 8}, and then return the new value
        of block and the new value of count. In addition, this command moves `count` to a 32-bit value.
        """
        # Cache the value of fixed_block and fixed_count so it is not recomputed unnecessarily
        if self.fixed_block or self.fixed_count:
            return

        if not self.count:
            self.fixed_block, self.fixed_count = self.block, self.count
            return

        resized_count = set_type(self.count, ctypes.longint, il_code)

        sizes = [8, 4, 2, 1]
        if self.block in sizes:
            self.fixed_block, self.fixed_count = self.block, resized_count
            return

        # Select the biggest legal size that divides given block size
        for new_block in sizes:
            if self.block % new_block == 0: break

        self.fixed_block = new_block

        scale = ILValue(ctypes.longint)
        scale_factor = str(int(self.block / new_block))
        il_code.register_literal_var(scale, scale_factor)

        self.fixed_count = ILValue(ctypes.longint)
        il_code.add(math_cmds.Mult(self.fixed_count, resized_count, scale))
Пример #2
0
    def make_il(self, il_code, symbol_table, c):
        # ILValue for storing the output of this boolean operation
        out = ILValue(ctypes.integer)

        # ILValue for initial value of output variable.
        init = ILValue(ctypes.integer)
        il_code.register_literal_var(init, self.initial_value)

        # ILValue for other value of output variable.
        other = ILValue(ctypes.integer)
        il_code.register_literal_var(other, 1 - self.initial_value)

        # Label which immediately precedes the line which sets out to 0 or 1.
        set_out = il_code.get_label()

        # Label which skips the line which sets out to 0 or 1.
        end = il_code.get_label()

        err = f"'{str(self.op)}' operator requires scalar operands"
        left = self.left.make_il(il_code, symbol_table, c)
        if not left.ctype.is_scalar(): raise CompilerError(err, self.left.r)

        il_code.add(value_cmds.Set(out, init))
        il_code.add(self.jump_cmd(left, set_out))
        right = self.right.make_il(il_code, symbol_table, c)
        if not right.ctype.is_scalar(): raise CompilerError(err, self.right.r)

        il_code.add(self.jump_cmd(right, set_out))
        il_code.add(control_cmds.Jump(end))
        il_code.add(control_cmds.Label(set_out))
        il_code.add(value_cmds.Set(out, other))
        il_code.add(control_cmds.Label(end))
        return out
Пример #3
0
    def make_il(self, il_code, symbol_table, c):
        """ Make code for this node """

        expr = self.expr.make_il(il_code, symbol_table, c)
        if not expr.ctype.is_scalar():
            err = "'!' operator requires scalar operand"
            raise CompilerError(err, self.r)

        # ILValue for storing the output
        out = ILValue(ctypes.integer)

        # ILValue for zero.
        zero = ILValue(ctypes.integer)
        il_code.register_literal_var(zero, "0")

        # ILValue for one.
        one = ILValue(ctypes.integer)
        il_code.register_literal_var(one, "1")

        # Label which skips the line which sets out to 0.
        end = il_code.get_label()

        il_code.add(value_cmds.Set(out, one))
        il_code.add(control_cmds.JumpZero(expr, end))
        il_code.add(value_cmds.Set(out, zero))
        il_code.add(control_cmds.Label(end))

        return out
Пример #4
0
    def make_il(self, il_code, symbol_table, c):
        """ Make code for this node """
        lval = self.expr.lvalue(il_code, symbol_table, c)

        if not lval or not lval.modable():
            err = f"operand of {self.descr} operator not a modifiable lvalue"
            raise CompilerError(err, self.expr.r)

        val = self.expr.make_il(il_code, symbol_table, c)
        one = ILValue(val.ctype)
        if val.ctype.is_arith():
            il_code.register_literal_var(one, 1)
        elif val.ctype.is_pointer() and val.ctype.arg.is_complete():
            il_code.register_literal_var(one, val.ctype.arg.size)
        elif val.ctype.is_pointer():
            err = "invalid arithmetic on pointer to incomplete type"
            raise CompilerError(err, self.expr.r)
        else:
            err = f"invalid type for {self.descr} operator"
            raise CompilerError(err, self.expr.r)

        new_val = ILValue(val.ctype)

        if self.return_new:
            il_code.add(self.cmd(new_val, val, one))
            lval.set_to(new_val, il_code, self.expr.r)
            return new_val
        else:
            old_val = ILValue(val.ctype)
            il_code.add(value_cmds.Set(old_val, val))
            il_code.add(self.cmd(new_val, val, one))
            lval.set_to(new_val, il_code, self.expr.r)
            return old_val
Пример #5
0
def get_size(ctype, num, il_code):
    """Return ILValue representing total size of `num` objects of given ctype.
        ctype - CType of object to count.
        num - Integral ILValue representing number of these objects.
    """

    long_num = set_type(num, ctypes.longint, il_code)
    total = ILValue(ctypes.longint)
    size = ILValue(ctypes.longint)
    il_code.register_literal_var(size, str(ctype.size))
    il_code.add(math_cmds.Mult(total, long_num, size))

    return total
Пример #6
0
    def _lvalue(self, il_code, symbol_table, c):
        struct_addr = self.head.make_il(il_code, symbol_table, c)
        if not struct_addr.ctype.is_pointer():
            err = "first argument of '->' must have pointer type"
            raise CompilerError(err, self.r)

        offset, ctype = self.get_offset_info(struct_addr.ctype.arg)
        shift = ILValue(ctypes.longint)
        il_code.register_literal_var(shift, str(offset))

        out = ILValue(PointerCType(ctype))
        il_code.add(math_cmds.Add(out, struct_addr, shift))
        return IndirectLValue(out)
Пример #7
0
    def make_il(self, il_code, symbol_table, c):
        """ Make code for this node """

        left = self.left.make_il(il_code, symbol_table, c)
        right = self.right.make_il(il_code, symbol_table, c)

        if self.check_type(left, right):
            left, right = arith_convert(left, right, il_code)

            if left.literal and right.literal:
                # If NotImplementedError is raised, continue with execution.
                try:
                    val = self.arith_const(
                        shift_into_range(left.literal.val, left.ctype),
                        shift_into_range(right.literal.val, right.ctype),
                        left.ctype)
                    out = ILValue(left.ctype)
                    il_code.register_literal_var(out, val)
                    return out

                except NotImplementedError:
                    pass

            return self.arith(left, right, il_code)

        else:
            return self.nonarith(left, right, il_code)
Пример #8
0
 def val(self, il_code):
     self.fix_block_count(il_code)
     out = ILValue(self.ctype())
     il_code.add(
         value_cmds.ReadRel(out, self.base, self.fixed_block,
                            self.fixed_count))
     return out
Пример #9
0
    def do_body(self, il_code, symbol_table, c):
        """ Create code for function body. Caller must check that this function has a body """
        is_main = self.identifier.content == "main"

        for param in self.param_names:
            if not param:
                err = "function definition missing parameter name"
                raise CompilerError(err, self.span)

        if is_main:
            self.check_main_type()

        c = c.set_return(self.ctype.ret)
        il_code.start_func(self.identifier.content)

        symbol_table.new_scope()

        num_params = len(self.ctype.args)
        iterations = zip(self.ctype.args, self.param_names, range(num_params))
        for ctype, param, i in iterations:
            arg = symbol_table.add_variable(param, ctype, symbol_table.DEFINED,
                                            None, symbol_table.AUTOMATIC)
            il_code.add(value_cmds.LoadArg(arg, i))

        self.body.make_il(il_code, symbol_table, c, no_scope=True)
        if not il_code.always_returns() and is_main:
            zero = ILValue(ctypes.integer)
            il_code.register_literal_var(zero, 0)
            il_code.add(control_cmds.Return(zero))
        elif not il_code.always_returns():
            il_code.add(control_cmds.Return(None))

        symbol_table.end_scope()
Пример #10
0
    def nonarith(self, left, right, il_code):
        """ Check equality of non-arithmetic expressions """

        # If either operand is a null pointer constant, cast it to the other's pointer type.
        if left.ctype.is_pointer() and right.null_ptr_const:
            right = set_type(right, left.ctype, il_code)
        elif right.ctype.is_pointer() and left.null_ptr_const:
            left = set_type(left, right.ctype, il_code)

        # If both operands are not pointer types, quit now
        if not left.ctype.is_pointer() or not right.ctype.is_pointer():
            with report_err():
                err = "comparison between incomparable types"
                raise CompilerError(err, self.op.r)

        # If one side is pointer to void, cast the other to same.
        elif left.ctype.arg.is_void():
            check_cast(right, left.ctype, self.op.r)
            right = set_type(right, left.ctype, il_code)
        elif right.ctype.arg.is_void():
            check_cast(left, right.ctype, self.op.r)
            left = set_type(left, right.ctype, il_code)

        # If both types are still incompatible, warn!
        elif not left.ctype.compatible(right.ctype):
            with report_err():
                err = "comparison between distinct pointer types"
                raise CompilerError(err, self.op.r)

        # Now, we can do comparison
        out = ILValue(ctypes.integer)
        il_code.add(self.eq_il_cmd(out, left, right))
        return out
Пример #11
0
 def addr(self, il_code):
     self.fix_block_count(il_code)
     out = ILValue(PointerCType(self.ctype()))
     il_code.add(
         value_cmds.AddrRel(out, self.base, self.fixed_block,
                            self.fixed_count))
     return out
Пример #12
0
    def _lvalue(self, il_code, symbol_table, c):
        head_lv = self.head.lvalue(il_code, symbol_table, c)
        struct_ctype = head_lv.ctype() if head_lv else None
        offset, ctype = self.get_offset_info(struct_ctype)

        if isinstance(head_lv, DirectLValue):
            head_val = self.head.make_il(il_code, symbol_table, c)
            return RelativeLValue(ctype, head_val, offset)
        else:
            struct_addr = head_lv.addr(il_code)

            shift = ILValue(ctypes.longint)
            il_code.register_literal_var(shift, str(offset))

            out = ILValue(PointerCType(ctype))
            il_code.add(math_cmds.Add(out, struct_addr, shift))
            return IndirectLValue(out)
Пример #13
0
def set_type(il_value, ctype, il_code, output=None):
    """If necessary, emit code to cast given il_value to the given ctype. This function does no type checking and will
    never produce a warning or error.
    """
    if not output and il_value.ctype.compatible(ctype): return il_value
    elif output == il_value: return il_value
    elif not output and il_value.literal:
        output = ILValue(ctype)
        if ctype.is_integral():
            val = shift_into_range(il_value.literal.val, ctype)
        else:
            val = il_value.literal.val
        il_code.register_literal_var(output, val)
        return output
    else:
        if not output: output = ILValue(ctype)
        il_code.add(value_cmds.Set(output, il_value))
        return output
Пример #14
0
 def arith(self, left, right, il_code):
     """Return the result of this operation on given arithmetic operands. Promotions and conversions are done by
     caller, so the implementation of this function need not convert operands. A default implementation is provided,
     but this can be overridden by derived classes.
         left - ILValue for left operand.
         right - ILValue for right operand.
     """
     out = ILValue(left.ctype)
     il_code.add(self.default_il_cmd(out, left, right))
     return out
Пример #15
0
    def pointer_subsc(self, point, arith, il_code):
        """Return the LValue for this node. This function is called in the case where one operand is a pointer and the
        other operand is an integer.
        """
        if not point.ctype.arg.is_complete():
            err = "cannot subscript pointer to incomplete type"
            raise CompilerError(err, self.op.r)

        shift = get_size(point.ctype.arg, arith, il_code)
        out = ILValue(point.ctype)
        il_code.add(math_cmds.Add(out, point, shift))
        return IndirectLValue(out)
Пример #16
0
    def make_il(self, il_code, symbol_table, c):
        """Make code for a literal number. This function does not actually make any code in the IL, it just returns a
        LiteralILValue that can be used in IL code by the caller.
        """
        v = int(str(self.number))

        if ctypes.int_min <= v <= ctypes.int_max:
            il_value = ILValue(ctypes.integer)
        elif ctypes.long_min <= v <= ctypes.long_max:
            il_value = ILValue(ctypes.longint)
        else:
            err = "integer literal too large to be represented by any integer type"
            raise CompilerError(err, self.number.r)

        il_code.register_literal_var(il_value, v)

        # Literal integer 0 is a null pointer constant
        if v == 0:
            il_value.null_ptr_const = True

        return il_value
Пример #17
0
    def make_il(self, il_code, symbol_table, c):
        """ Make code for this node """
        right = self.right.make_il(il_code, symbol_table, c)
        lvalue = self.left.lvalue(il_code, symbol_table, c)
        if not lvalue or not lvalue.modable():
            err = f"expression on left of '{str(self.op)}' is not assignable"
            raise CompilerError(err, self.left.r)

        if lvalue.ctype().is_pointer() and right.ctype.is_integral(
        ) and self.accept_pointer:

            if not lvalue.ctype().arg.is_complete():
                err = "invalid arithmetic on pointer to incomplete type"
                raise CompilerError(err, self.op.r)

            # Because of caching requirement of make_il and lvalue functions, we know this call won't regenerate code
            # for the left expression beyond just what's needed to get the value stored at the lvalue. This is important
            # in cases like ``*func() += 10`` where func() may have side effects if called twice.
            left = self.left.make_il(il_code, symbol_table, c)

            out = ILValue(left.ctype)
            shift = get_size(left.ctype.arg, right, il_code)

            il_code.add(self.command(out, left, shift))
            lvalue.set_to(out, il_code, self.op.r)
            return out

        elif lvalue.ctype().is_arith() and right.ctype.is_arith():
            left = self.left.make_il(il_code, symbol_table, c)
            out = ILValue(left.ctype)

            left, right = arith_convert(left, right, il_code)
            il_code.add(self.command(out, left, right))
            lvalue.set_to(out, il_code, self.op.r)
            return out

        else:
            err = f"invalid types for '{str(self.op)}' operator"
            raise CompilerError(err, self.op.r)
Пример #18
0
    def nonarith(self, left, right, il_code):
        """ Compare non-arithmetic expressions """

        if not left.ctype.is_pointer() or not right.ctype.is_pointer():
            err = "comparison between incomparable types"
            raise CompilerError(err, self.op.r)
        elif not left.ctype.compatible(right.ctype):
            err = "comparison between distinct pointer types"
            raise CompilerError(err, self.op.r)

        out = ILValue(ctypes.integer)
        il_code.add(self.comp_cmd(out, left, right))
        return out
Пример #19
0
    def nonarith(self, left, right, il_code):
        """ Make subtraction code if both operands are non-arithmetic type """

        if left.ctype.is_pointer() and right.ctype.is_pointer(
        ) and left.ctype.compatible(right.ctype):

            if not left.ctype.arg.is_complete(
            ) or not right.ctype.arg.is_complete():
                err = "invalid arithmetic on pointers to incomplete types"
                raise CompilerError(err, self.op.r)

            # Get raw difference in pointer values
            raw = ILValue(ctypes.longint)
            il_code.add(math_cmds.Subtr(raw, left, right))

            # Divide by size of object
            out = ILValue(ctypes.longint)
            size = ILValue(ctypes.longint)
            il_code.register_literal_var(size, str(left.ctype.arg.size))
            il_code.add(math_cmds.Div(out, raw, size))

            return out

        # Left operand is pointer to complete object type, and right operand is integer.
        elif left.ctype.is_pointer() and right.ctype.is_integral():
            if not left.ctype.arg.is_complete():
                err = "invalid arithmetic on pointer to incomplete type"
                raise CompilerError(err, self.op.r)

            out = ILValue(left.ctype)
            shift = get_size(left.ctype.arg, right, il_code)
            il_code.add(math_cmds.Subtr(out, left, shift))
            return out

        else:
            descr = "invalid operand types for subtraction"
            raise CompilerError(descr, self.op.r)
Пример #20
0
 def make_il(self, il_code, symbol_table, c):
     """ Make code for this node """
     expr = self.expr.make_il(il_code, symbol_table, c)
     if not self.check_type(expr):
         err = f"{self.descr} requires {self.opnd_descr} type operand"
         raise CompilerError(err, self.expr.r)
     # perform integer promotion
     if expr.ctype.size < 4:
         expr = set_type(expr, ctypes.integer, il_code)
     if self.cmd:
         out = ILValue(expr.ctype)
         # perform constant folding
         if expr.literal:
             val = self.arith_const(expr.literal.val, expr.ctype)
             val = shift_into_range(val, expr.ctype)
             il_code.register_literal_var(out, val)
         else:
             il_code.add(self.cmd(out, expr))
         return out
     return expr
Пример #21
0
    def nonarith(self, left, right, il_code):
        """ Make addition code if either operand is non-arithmetic type """

        # One operand should be pointer to complete object type, and the other should be any integer type.
        if left.ctype.is_pointer() and right.ctype.is_integral():
            arith, pointer = right, left
        elif right.ctype.is_pointer() and left.ctype.is_integral():
            arith, pointer = left, right
        else:
            err = "invalid operand types for addition"
            raise CompilerError(err, self.op.r)

        if not pointer.ctype.arg.is_complete():
            err = "invalid arithmetic on pointer to incomplete type"
            raise CompilerError(err, self.op.r)

        # Multiply by size of objects
        out = ILValue(pointer.ctype)
        shift = get_size(pointer.ctype.arg, arith, il_code)
        il_code.add(math_cmds.Add(out, pointer, shift))
        return out
Пример #22
0
    def make_il(self, il_code, symbol_table, c):
        """ Make code for this node """

        # This is of function pointer type, so func.arg is the function type.
        func = self.func.make_il(il_code, symbol_table, c)

        if not func.ctype.is_pointer() or not func.ctype.arg.is_function():
            descr = "called object is not a function pointer"
            raise CompilerError(descr, self.func.r)
        elif func.ctype.arg.ret.is_incomplete(
        ) and not func.ctype.arg.ret.is_void():
            descr = "function returns non-void incomplete type"
            raise CompilerError(descr, self.func.r)

        if func.ctype.arg.no_info:
            final_args = self.get_args_without_prototype(
                il_code, symbol_table, c)
        else:
            final_args = self.get_args_with_prototype(func.ctype.arg, il_code,
                                                      symbol_table, c)

        ret = ILValue(func.ctype.arg.ret)
        il_code.add(control_cmds.Call(func, final_args, ret))
        return ret
Пример #23
0
 def val(self, il_code):
     out = ILValue(self.ctype())
     il_code.add(value_cmds.ReadAt(out, self.addr_val))
     return out
Пример #24
0
 def addr(self, il_code):
     out = ILValue(PointerCType(self.il_value.ctype))
     il_code.add(value_cmds.AddrOf(out, self.il_value))
     return out
Пример #25
0
 def _lvalue(self, il_code, symbol_table, c):
     il_value = ILValue(ArrayCType(ctypes.char, len(self.chars)))
     il_code.register_string_literal(il_value, self.chars)
     return DirectLValue(il_value)
Пример #26
0
 def arith(self, left, right, il_code):
     """ Check equality of arithmetic expressions """
     out = ILValue(ctypes.integer)
     il_code.add(self.eq_il_cmd(out, left, right))
     return out
Пример #27
0
 def arith(self, left, right, il_code):
     """ Compare arithmetic expressions """
     out = ILValue(ctypes.integer)
     il_code.add(self.comp_cmd(out, left, right))
     return out