Esempio n. 1
0
 def init(self, expr_reg: Value, target_type: RType, reverse: bool) -> None:
     builder = self.builder
     self.reverse = reverse
     # Define target to contain the expression, along with the index that will be used
     # for the for-loop. If we are inside of a generator function, spill these into the
     # environment class.
     self.expr_target = builder.maybe_spill(expr_reg)
     if not reverse:
         index_reg = builder.add(LoadInt(0))
     else:
         index_reg = builder.binary_op(self.load_len(), builder.add(LoadInt(1)), '-', self.line)
     self.index_target = builder.maybe_spill_assignable(index_reg)
     self.target_type = target_type
Esempio n. 2
0
 def load_static_int(self, value: int) -> Value:
     """Loads a static integer Python 'int' object into a register."""
     if abs(value) > MAX_LITERAL_SHORT_INT:
         static_symbol = self.literal_static_name(value)
         return self.add(LoadStatic(int_rprimitive, static_symbol, ann=value))
     else:
         return self.add(LoadInt(value))
Esempio n. 3
0
    def gen_step(self) -> None:
        builder = self.builder
        line = self.line

        # Increment index register. If the range is known to fit in short ints, use
        # short ints.
        if (is_short_int_rprimitive(self.start_reg.type)
                and is_short_int_rprimitive(self.end_reg.type)):
            new_val = builder.primitive_op(
                unsafe_short_add, [builder.read(self.index_reg, line),
                                   builder.add(LoadInt(self.step))], line)

        else:
            new_val = builder.binary_op(
                builder.read(self.index_reg, line), builder.add(LoadInt(self.step)), '+', line)
        builder.assign(self.index_reg, new_val, line)
        builder.assign(self.index_target, new_val, line)
Esempio n. 4
0
 def gen_step(self) -> None:
     # Step to the next item.
     builder = self.builder
     line = self.line
     step = 1 if not self.reverse else -1
     builder.assign(self.index_target, builder.primitive_op(
         unsafe_short_add,
         [builder.read(self.index_target, line),
          builder.add(LoadInt(step))], line), line)
Esempio n. 5
0
 def init(self) -> None:
     builder = self.builder
     # Create a register to store the state of the loop index and
     # initialize this register along with the loop index to 0.
     zero = builder.add(LoadInt(0))
     self.index_reg = builder.maybe_spill_assignable(zero)
     self.index_target = builder.get_assignment_target(
         self.index)  # type: Union[Register, AssignmentTarget]
     builder.assign(self.index_target, zero, self.line)
Esempio n. 6
0
    def add_bool_branch(self, value: Value, true: BasicBlock,
                        false: BasicBlock) -> None:
        if is_runtime_subtype(value.type, int_rprimitive):
            zero = self.add(LoadInt(0))
            value = self.binary_op(value, zero, '!=', value.line)
        elif is_same_type(value.type, list_rprimitive):
            length = self.primitive_op(list_len_op, [value], value.line)
            zero = self.add(LoadInt(0))
            value = self.binary_op(length, zero, '!=', value.line)
        elif (isinstance(value.type, RInstance)
              and value.type.class_ir.is_ext_class
              and value.type.class_ir.has_method('__bool__')):
            # Directly call the __bool__ method on classes that have it.
            value = self.gen_method_call(value, '__bool__', [],
                                         bool_rprimitive, value.line)
        else:
            value_type = optional_value_type(value.type)
            if value_type is not None:
                is_none = self.binary_op(value, self.none_object(), 'is not',
                                         value.line)
                branch = Branch(is_none, true, false, Branch.BOOL_EXPR)
                self.add(branch)
                always_truthy = False
                if isinstance(value_type, RInstance):
                    # check whether X.__bool__ is always just the default (object.__bool__)
                    if (not value_type.class_ir.has_method('__bool__') and
                            value_type.class_ir.is_method_final('__bool__')):
                        always_truthy = True

                if not always_truthy:
                    # Optional[X] where X may be falsey and requires a check
                    branch.true = BasicBlock()
                    self.activate_block(branch.true)
                    # unbox_or_cast instead of coerce because we want the
                    # type to change even if it is a subtype.
                    remaining = self.unbox_or_cast(value, value_type,
                                                   value.line)
                    self.add_bool_branch(remaining, true, false)
                return
            elif not is_same_type(value.type, bool_rprimitive):
                value = self.primitive_op(bool_op, [value], value.line)
        self.add(Branch(value, true, false, Branch.BOOL_EXPR))
Esempio n. 7
0
 def gen_step(self) -> None:
     builder = self.builder
     line = self.line
     # We can safely assume that the integer is short, since we are not going to wrap
     # around a 63-bit integer.
     # NOTE: This would be questionable if short ints could be 32 bits.
     new_val = builder.primitive_op(
         unsafe_short_add, [builder.read(self.index_reg, line),
                            builder.add(LoadInt(1))], line)
     builder.assign(self.index_reg, new_val, line)
     builder.assign(self.index_target, new_val, line)
Esempio n. 8
0
def translate_len(builder: IRBuilder, expr: CallExpr,
                  callee: RefExpr) -> Optional[Value]:
    # Special case builtins.len
    if (len(expr.args) == 1 and expr.arg_kinds == [ARG_POS]):
        expr_rtype = builder.node_type(expr.args[0])
        if isinstance(expr_rtype, RTuple):
            # len() of fixed-length tuple can be trivially determined statically,
            # though we still need to evaluate it.
            builder.accept(expr.args[0])
            return builder.add(LoadInt(len(expr_rtype.types)))
    return None
Esempio n. 9
0
    def visit_call_expr(self, expr: CallExpr) -> Register:
        if isinstance(expr.callee, MemberExpr):
            is_module_call = self.is_module_member_expr(expr.callee)
            if expr.callee.expr in self.types and not is_module_call:
                target = self.translate_special_method_call(expr.callee, expr)
                if target:
                    return target

            # Either its a module call or translating to a special method call failed, so we have
            # to fallback to a PyCall
            function = self.accept(expr.callee)
            return self.py_call(function, expr.args, self.node_type(expr))

        assert isinstance(expr.callee, NameExpr)
        fn = expr.callee.name  # TODO: fullname
        if fn == 'len' and len(expr.args) == 1 and expr.arg_kinds == [ARG_POS]:
            target = self.alloc_target(IntRType())
            arg = self.accept(expr.args[0])

            expr_rtype = self.node_type(expr.args[0])
            if expr_rtype.name == 'list':
                self.add(PrimitiveOp(target, PrimitiveOp.LIST_LEN, arg))
            elif expr_rtype.name == 'sequence_tuple':
                self.add(
                    PrimitiveOp(target, PrimitiveOp.HOMOGENOUS_TUPLE_LEN, arg))
            elif isinstance(expr_rtype, TupleRType):
                self.add(LoadInt(target, len(expr_rtype.types)))
            else:
                assert False, "unsupported use of len"

        # Handle conversion to sequence tuple
        elif fn == 'tuple' and len(
                expr.args) == 1 and expr.arg_kinds == [ARG_POS]:
            target = self.alloc_target(SequenceTupleRType())
            arg = self.accept(expr.args[0])

            self.add(
                PrimitiveOp(target, PrimitiveOp.LIST_TO_HOMOGENOUS_TUPLE, arg))
        else:
            target_type = self.node_type(expr)
            if not (self.is_native_name_expr(expr.callee)):
                function = self.accept(expr.callee)
                return self.py_call(function, expr.args, target_type)

            target = self.alloc_target(target_type)
            args = [self.accept(arg) for arg in expr.args]
            self.add(Call(target, fn, args))
        return target
Esempio n. 10
0
 def test_register(self) -> None:
     self.temp = self.env.add_temp(IntRType())
     self.block.ops.append(LoadInt(self.temp, 5))
     fn = FuncIR('myfunc', [self.arg], ListRType(), [self.block], self.env)
     emitter = Emitter(EmitterContext())
     generate_native_function(fn, emitter)
     result = emitter.fragments
     assert_string_arrays_equal([
         'static PyObject *CPyDef_myfunc(CPyTagged cpy_r_arg) {\n',
         '    CPyTagged cpy_r_r0;\n',
         'CPyL0: ;\n',
         '    cpy_r_r0 = 10;\n',
         '}\n',
     ],
                                result,
                                msg='Generated code invalid')
Esempio n. 11
0
 def gen_condition(self) -> None:
     builder = self.builder
     line = self.line
     if self.reverse:
         # If we are iterating in reverse order, we obviously need
         # to check that the index is still positive. Somewhat less
         # obviously we still need to check against the length,
         # since it could shrink out from under us.
         comparison = builder.binary_op(builder.read(self.index_target, line),
                                        builder.add(LoadInt(0)), '>=', line)
         second_check = BasicBlock()
         builder.add_bool_branch(comparison, second_check, self.loop_exit)
         builder.activate_block(second_check)
     # For compatibility with python semantics we recalculate the length
     # at every iteration.
     len_reg = self.load_len()
     comparison = builder.binary_op(builder.read(self.index_target, line), len_reg, '<', line)
     builder.add_bool_branch(comparison, self.body_block, self.loop_exit)
Esempio n. 12
0
 def test_register(self) -> None:
     self.env.temp_index = 0
     op = LoadInt(5)
     self.block.ops.append(op)
     self.env.add_op(op)
     fn = FuncIR('myfunc', None, 'mod', FuncSignature([self.arg], list_rprimitive),
                 [self.block], self.env)
     emitter = Emitter(EmitterContext(['mod']))
     generate_native_function(fn, emitter, 'prog.py', 'prog')
     result = emitter.fragments
     assert_string_arrays_equal(
         [
             'static PyObject *CPyDef_myfunc(CPyTagged cpy_r_arg) {\n',
             '    CPyTagged cpy_r_r0;\n',
             'CPyL0: ;\n',
             '    cpy_r_r0 = 10;\n',
             '}\n',
         ],
         result, msg='Generated code invalid')
Esempio n. 13
0
 def gen_return(self, builder: 'IRBuilder', value: Value,
                line: int) -> None:
     # Assign an invalid next label number so that the next time __next__ is called, we jump to
     # the case in which StopIteration is raised.
     builder.assign(builder.fn_info.generator_class.next_label_target,
                    builder.add(LoadInt(-1)), line)
     # Raise a StopIteration containing a field for the value that should be returned. Before
     # doing so, create a new block without an error handler set so that the implicitly thrown
     # StopIteration isn't caught by except blocks inside of the generator function.
     builder.error_handlers.append(None)
     builder.goto_new_block()
     # Skip creating a traceback frame when we raise here, because
     # we don't care about the traceback frame and it is kind of
     # expensive since raising StopIteration is an extremely common case.
     # Also we call a special internal function to set StopIteration instead of
     # using RaiseStandardError because the obvious thing doesn't work if the
     # value is a tuple (???).
     builder.primitive_op(set_stop_iteration_value, [value],
                          NO_TRACEBACK_LINE_NO)
     builder.add(Unreachable())
     builder.error_handlers.pop()
Esempio n. 14
0
 def test_load_int(self) -> None:
     self.assert_emit(LoadInt(5), "cpy_r_r0 = 10;")
Esempio n. 15
0
    def visit_for_stmt(self, s: ForStmt) -> Register:
        if (isinstance(s.expr, CallExpr)
                and isinstance(s.expr.callee, RefExpr)
                and s.expr.callee.fullname == 'builtins.range'):
            self.push_loop_stack()

            # Special case for x in range(...)
            # TODO: Check argument counts and kinds; check the lvalue
            end = s.expr.args[0]
            end_reg = self.accept(end)

            # Initialize loop index to 0.
            index_reg = self.assign(s.index,
                                    IntExpr(0),
                                    IntRType(),
                                    IntRType(),
                                    declare_new=True)
            goto = Goto(INVALID_LABEL)
            self.add(goto)

            # Add loop condition check.
            top = self.new_block()
            goto.label = top.label
            branch = Branch(index_reg, end_reg, INVALID_LABEL, INVALID_LABEL,
                            Branch.INT_LT)
            self.add(branch)
            branches = [branch]

            body = self.new_block()
            self.set_branches(branches, True, body)
            s.body.accept(self)

            end_goto = Goto(INVALID_LABEL)
            self.add(end_goto)
            end_block = self.new_block()
            end_goto.label = end_block.label

            # Increment index register.
            one_reg = self.alloc_temp(IntRType())
            self.add(LoadInt(one_reg, 1))
            self.add(
                PrimitiveOp(index_reg, PrimitiveOp.INT_ADD, index_reg,
                            one_reg))

            # Go back to loop condition check.
            self.add(Goto(top.label))
            next = self.new_block()
            self.set_branches(branches, False, next)

            self.pop_loop_stack(end_block, next)
            return INVALID_REGISTER

        if self.node_type(s.expr).name == 'list':
            self.push_loop_stack()

            expr_reg = self.accept(s.expr)

            index_reg = self.alloc_temp(IntRType())
            self.add(LoadInt(index_reg, 0))

            one_reg = self.alloc_temp(IntRType())
            self.add(LoadInt(one_reg, 1))

            assert isinstance(s.index, NameExpr)
            assert isinstance(s.index.node, Var)
            lvalue_reg = self.environment.add_local(s.index.node,
                                                    self.node_type(s.index))

            condition_block = self.goto_new_block()

            # For compatibility with python semantics we recalculate the length
            # at every iteration.
            len_reg = self.alloc_temp(IntRType())
            self.add(PrimitiveOp(len_reg, PrimitiveOp.LIST_LEN, expr_reg))

            branch = Branch(index_reg, len_reg, INVALID_LABEL, INVALID_LABEL,
                            Branch.INT_LT)
            self.add(branch)
            branches = [branch]

            body_block = self.new_block()
            self.set_branches(branches, True, body_block)

            target_list_type = self.types[s.expr]
            assert isinstance(target_list_type, Instance)
            target_type = self.type_to_rtype(target_list_type.args[0])
            value_box = self.alloc_temp(ObjectRType())
            self.add(
                PrimitiveOp(value_box, PrimitiveOp.LIST_GET, expr_reg,
                            index_reg))

            self.unbox_or_cast(value_box, target_type, target=lvalue_reg)

            s.body.accept(self)

            end_block = self.goto_new_block()
            self.add(
                PrimitiveOp(index_reg, PrimitiveOp.INT_ADD, index_reg,
                            one_reg))
            self.add(Goto(condition_block.label))

            next_block = self.new_block()
            self.set_branches(branches, False, next_block)

            self.pop_loop_stack(end_block, next_block)

            return INVALID_REGISTER

        assert False, 'for not supported'
Esempio n. 16
0
 def visit_int_expr(self, expr: IntExpr) -> Register:
     reg = self.alloc_target(IntRType())
     self.add(LoadInt(reg, expr.value))
     return reg
Esempio n. 17
0
 def init(self) -> None:
     builder = self.builder
     # Initialize loop index to 0.
     self.index_target = builder.get_assignment_target(
         self.index)  # type: Union[Register, AssignmentTarget]
     builder.assign(self.index_target, builder.add(LoadInt(0)), self.line)