Exemplo n.º 1
0
    def decompose_union_helper(self,
                               obj: Value,
                               rtype: RUnion,
                               result_type: RType,
                               process_item: Callable[[Value], Value],
                               line: int) -> Value:
        """Generate isinstance() + specialized operations for union items.

        Say, for Union[A, B] generate ops resembling this (pseudocode):

            if isinstance(obj, A):
                result = <result of process_item(cast(A, obj)>
            else:
                result = <result of process_item(cast(B, obj)>

        Args:
            obj: value with a union type
            rtype: the union type
            result_type: result of the operation
            process_item: callback to generate op for a single union item (arg is coerced
                to union item type)
            line: line number
        """
        # TODO: Optimize cases where a single operation can handle multiple union items
        #     (say a method is implemented in a common base class)
        fast_items = []
        rest_items = []
        for item in rtype.items:
            if isinstance(item, RInstance):
                fast_items.append(item)
            else:
                # For everything but RInstance we fall back to C API
                rest_items.append(item)
        exit_block = BasicBlock()
        result = self.alloc_temp(result_type)
        for i, item in enumerate(fast_items):
            more_types = i < len(fast_items) - 1 or rest_items
            if more_types:
                # We are not at the final item so we need one more branch
                op = self.isinstance_native(obj, item.class_ir, line)
                true_block, false_block = BasicBlock(), BasicBlock()
                self.add_bool_branch(op, true_block, false_block)
                self.activate_block(true_block)
            coerced = self.coerce(obj, item, line)
            temp = process_item(coerced)
            temp2 = self.coerce(temp, result_type, line)
            self.add(Assign(result, temp2))
            self.goto(exit_block)
            if more_types:
                self.activate_block(false_block)
        if rest_items:
            # For everything else we use generic operation. Use force=True to drop the
            # union type.
            coerced = self.coerce(obj, object_rprimitive, line, force=True)
            temp = process_item(coerced)
            temp2 = self.coerce(temp, result_type, line)
            self.add(Assign(result, temp2))
            self.goto(exit_block)
        self.activate_block(exit_block)
        return result
Exemplo n.º 2
0
    def shortcircuit_helper(self, op: str, expr_type: RType,
                            left: Callable[[], Value],
                            right: Callable[[], Value], line: int) -> Value:
        # Having actual Phi nodes would be really nice here!
        target = self.alloc_temp(expr_type)
        # left_body takes the value of the left side, right_body the right
        left_body, right_body, next = BasicBlock(), BasicBlock(), BasicBlock()
        # true_body is taken if the left is true, false_body if it is false.
        # For 'and' the value is the right side if the left is true, and for 'or'
        # it is the right side if the left is false.
        true_body, false_body = ((right_body, left_body) if op == 'and' else
                                 (left_body, right_body))

        left_value = left()
        self.add_bool_branch(left_value, true_body, false_body)

        self.activate_block(left_body)
        left_coerced = self.coerce(left_value, expr_type, line)
        self.add(Assign(target, left_coerced))
        self.goto(next)

        self.activate_block(right_body)
        right_value = right()
        right_coerced = self.coerce(right_value, expr_type, line)
        self.add(Assign(target, right_coerced))
        self.goto(next)

        self.activate_block(next)
        return target
Exemplo n.º 3
0
    def coerce(self,
               src: Value,
               target_type: RType,
               line: int,
               force: bool = False) -> Value:
        """Generate a coercion/cast from one type to other (only if needed).

        For example, int -> object boxes the source int; int -> int emits nothing;
        object -> int unboxes the object. All conversions preserve object value.

        If force is true, always generate an op (even if it is just an assignment) so
        that the result will have exactly target_type as the type.

        Returns the register with the converted value (may be same as src).
        """
        if src.type.is_unboxed and not target_type.is_unboxed:
            return self.box(src)
        if ((src.type.is_unboxed and target_type.is_unboxed)
                and not is_runtime_subtype(src.type, target_type)):
            # To go from one unboxed type to another, we go through a boxed
            # in-between value, for simplicity.
            tmp = self.box(src)
            return self.unbox_or_cast(tmp, target_type, line)
        if ((not src.type.is_unboxed and target_type.is_unboxed)
                or not is_subtype(src.type, target_type)):
            return self.unbox_or_cast(src, target_type, line)
        elif force:
            tmp = self.alloc_temp(target_type)
            self.add(Assign(tmp, src))
            return tmp
        return src
Exemplo n.º 4
0
    def gen_return(self, builder: 'IRBuilder', value: Value,
                   line: int) -> None:
        if self.ret_reg is None:
            self.ret_reg = builder.alloc_temp(builder.ret_types[-1])

        builder.add(Assign(self.ret_reg, value))
        builder.add(Goto(self.target))
Exemplo n.º 5
0
    def visit_conditional_expr(self, expr: ConditionalExpr) -> Value:
        if_body, else_body, next = BasicBlock(), BasicBlock(), BasicBlock()

        self.builder.process_conditional(expr.cond, if_body, else_body)
        expr_type = self.builder.node_type(expr)
        # Having actual Phi nodes would be really nice here!
        target = self.builder.alloc_temp(expr_type)

        self.builder.activate_block(if_body)
        true_value = self.builder.accept(expr.if_expr)
        true_value = self.builder.coerce(true_value, expr_type, expr.line)
        self.builder.add(Assign(target, true_value))
        self.builder.goto(next)

        self.builder.activate_block(else_body)
        false_value = self.builder.accept(expr.else_expr)
        false_value = self.builder.coerce(false_value, expr_type, expr.line)
        self.builder.add(Assign(target, false_value))
        self.builder.goto(next)

        self.builder.activate_block(next)

        return target
Exemplo n.º 6
0
def try_finally_entry_blocks(builder: IRBuilder,
                             err_handler: BasicBlock,
                             return_entry: BasicBlock,
                             main_entry: BasicBlock,
                             finally_block: BasicBlock,
                             ret_reg: Optional[Register]) -> Value:
    old_exc = builder.alloc_temp(exc_rtuple)

    # Entry block for non-exceptional flow
    builder.activate_block(main_entry)
    if ret_reg:
        builder.add(
            Assign(
                ret_reg,
                builder.add(LoadErrorValue(builder.ret_types[-1]))
            )
        )
    builder.goto(return_entry)

    builder.activate_block(return_entry)
    builder.add(Assign(old_exc, builder.add(LoadErrorValue(exc_rtuple))))
    builder.goto(finally_block)

    # Entry block for errors
    builder.activate_block(err_handler)
    if ret_reg:
        builder.add(
            Assign(
                ret_reg,
                builder.add(LoadErrorValue(builder.ret_types[-1]))
            )
        )
    builder.add(Assign(old_exc, builder.primitive_op(error_catch_op, [], -1)))
    builder.goto(finally_block)

    return old_exc
Exemplo n.º 7
0
 def box(self,
         src: Register,
         typ: RType,
         target: Optional[Register] = None) -> Register:
     if typ.supports_unbox:
         if target is None:
             target = self.alloc_temp(ObjectRType())
         self.add(Box(target, src, typ))
         return target
     else:
         # Already boxed
         if target is not None:
             self.add(Assign(target, src))
             return target
         else:
             return src
Exemplo n.º 8
0
def transform_block(block: BasicBlock, pre_live: AnalysisDict[Register],
                    post_live: AnalysisDict[Register],
                    pre_borrow: AnalysisDict[Register],
                    env: Environment) -> None:
    old_ops = block.ops
    ops = []  # type: List[Op]
    for i, op in enumerate(old_ops):
        key = (block.label, i)
        if isinstance(op, (Assign, Cast, Box)):
            # These operations just copy/steal a reference and don't create new
            # references.
            if op.src in post_live[key] or op.src in pre_borrow[key]:
                ops.append(IncRef(op.src, env.types[op.src]))
                if (op.dest not in pre_borrow[key]
                        and op.dest in pre_live[key]):
                    ops.append(DecRef(op.dest, env.types[op.dest]))
            ops.append(op)
            if op.dest not in post_live[key]:
                ops.append(DecRef(op.dest, env.types[op.dest]))
        elif isinstance(op, RegisterOp):
            # These operations construct a new reference.
            tmp_reg = None  # type: Optional[Register]
            if (op.dest not in pre_borrow[key] and op.dest in pre_live[key]):
                if op.dest not in op.sources():
                    ops.append(DecRef(op.dest, env.types[op.dest]))
                else:
                    tmp_reg = env.add_temp(env.types[op.dest])
                    ops.append(Assign(tmp_reg, op.dest))
            ops.append(op)
            for src in op.unique_sources():
                # Decrement source that won't be live afterwards.
                if src not in post_live[key] and src not in pre_borrow[key]:
                    if src != op.dest:
                        ops.append(DecRef(src, env.types[src]))
            if op.dest is not None and op.dest not in post_live[key]:
                ops.append(DecRef(op.dest, env.types[op.dest]))
            if tmp_reg is not None:
                ops.append(DecRef(tmp_reg, env.types[tmp_reg]))
        elif isinstance(op, Return) and op.reg in pre_borrow[key]:
            # The return op returns a new reference.
            ops.append(IncRef(op.reg, env.types[op.reg]))
            ops.append(op)
        else:
            ops.append(op)
    block.ops = ops
Exemplo n.º 9
0
 def get_using_binder(self, reg: Register, var: Var,
                      expr: Expression) -> Register:
     var_type = self.type_to_rtype(var.type)
     target_type = self.node_type(expr)
     if var_type != target_type:
         # Cast/unbox to the narrower given by the binder.
         if self.targets[-1] < 0:
             target = self.alloc_temp(target_type)
         else:
             target = self.targets[-1]
         return self.unbox_or_cast(reg, target_type, target)
     else:
         # Regular register access -- binder is not active.
         if self.targets[-1] < 0:
             return reg
         else:
             target = self.targets[-1]
             self.add(Assign(target, reg))
             return target
Exemplo n.º 10
0
def transform_del_item(builder: IRBuilder, target: AssignmentTarget, line: int) -> None:
    if isinstance(target, AssignmentTargetIndex):
        builder.gen_method_call(
            target.base,
            '__delitem__',
            [target.index],
            result_type=None,
            line=line
        )
    elif isinstance(target, AssignmentTargetAttr):
        key = builder.load_static_unicode(target.attr)
        builder.add(PrimitiveOp([target.obj, key], py_delattr_op, line))
    elif isinstance(target, AssignmentTargetRegister):
        # Delete a local by assigning an error value to it, which will
        # prompt the insertion of uninit checks.
        builder.add(Assign(target.register,
                           builder.add(LoadErrorValue(target.type, undefines=True))))
    elif isinstance(target, AssignmentTargetTuple):
        for subtarget in target.items:
            transform_del_item(builder, subtarget, line)
Exemplo n.º 11
0
 def test_assign_int(self) -> None:
     self.assert_emit(Assign(self.m, self.n), "cpy_r_m = cpy_r_n;")
Exemplo n.º 12
0
 def visit_assign(self, op: Assign) -> GenAndKill:
     return set(op.sources()), {op.dest}