Esempio n. 1
0
def split_blocks_at_errors(blocks: List[BasicBlock],
                           default_error_handler: BasicBlock,
                           func: str) -> List[BasicBlock]:
    new_blocks = []  # type: List[BasicBlock]

    # First split blocks on ops that may raise.
    for block in blocks:
        ops = block.ops
        block.ops = []
        cur_block = block
        new_blocks.append(cur_block)

        # If the block has an error handler specified, use it. Otherwise
        # fall back to the default.
        error_label = block.error_handler or default_error_handler
        block.error_handler = None

        for op in ops:
            cur_block.ops.append(op)
            if isinstance(op, RegisterOp) and op.error_kind != ERR_NEVER:
                # Split
                new_block = BasicBlock()
                new_blocks.append(new_block)

                if op.error_kind == ERR_MAGIC:
                    # Op returns an error value on error that depends on result RType.
                    variant = Branch.IS_ERROR
                    negated = False
                elif op.error_kind == ERR_FALSE:
                    # Op returns a C false value on error.
                    variant = Branch.BOOL_EXPR
                    negated = True
                else:
                    assert False, 'unknown error kind %d' % op.error_kind

                # Void ops can't generate errors since error is always
                # indicated by a special value stored in a register.
                assert not op.is_void, "void op generating errors?"

                branch = Branch(op,
                                true_label=error_label,
                                false_label=new_block,
                                op=variant,
                                line=op.line)
                branch.negated = negated
                if op.line != NO_TRACEBACK_LINE_NO:
                    branch.traceback_entry = (func, op.line)
                cur_block.ops.append(branch)
                cur_block = new_block

    return new_blocks
Esempio n. 2
0
 def test_branch(self) -> None:
     self.assert_emit(Branch(self.b, BasicBlock(8), BasicBlock(9), Branch.BOOL_EXPR),
                      """if (cpy_r_b) {
                             goto CPyL8;
                         } else
                             goto CPyL9;
                      """)
     b = Branch(self.b, BasicBlock(8), BasicBlock(9), Branch.BOOL_EXPR)
     b.negated = True
     self.assert_emit(b,
                      """if (!cpy_r_b) {
                             goto CPyL8;
                         } else
                             goto CPyL9;
                      """)
Esempio n. 3
0
 def test_branch_eq(self) -> None:
     self.assert_emit(
         Branch(self.n, self.m, Label(8), Label(9), Branch.INT_EQ),
         """if (CPyTagged_IsEq(cpy_r_n, cpy_r_m))
                             goto CPyL8;
                         else
                             goto CPyL9;
                      """)
     b = Branch(self.n, self.m, Label(8), Label(9), Branch.INT_LT)
     b.negated = True
     self.assert_emit(
         b, """if (!CPyTagged_IsLt(cpy_r_n, cpy_r_m))
                             goto CPyL8;
                         else
                             goto CPyL9;
                      """)
Esempio n. 4
0
def split_blocks_at_errors(blocks: List[BasicBlock],
                           default_error_handler: BasicBlock,
                           func: str) -> List[BasicBlock]:
    new_blocks = []  # type: List[BasicBlock]
    mapping = {}
    partial_ops = set()
    # First split blocks on ops that may raise.
    for block in blocks:
        ops = block.ops
        i0 = 0
        i = 0
        next_block = BasicBlock()
        while i < len(ops) - 1:
            op = ops[i]
            if isinstance(op, RegisterOp) and op.error_kind != ERR_NEVER:
                # Split
                new_blocks.append(next_block)
                new_block = next_block
                next_block = BasicBlock()
                new_block.ops.extend(ops[i0:i + 1])

                if op.error_kind == ERR_MAGIC:
                    # Op returns an error value on error that depends on result RType.
                    variant = Branch.IS_ERROR
                    negated = False
                elif op.error_kind == ERR_FALSE:
                    # Op returns a C false value on error.
                    variant = Branch.BOOL_EXPR
                    negated = True
                else:
                    assert False, 'unknown error kind %d' % op.error_kind

                # Void ops can't generate errors since error is always
                # indicated by a special value stored in a register.
                assert not op.is_void, "void op generating errors?"

                # If the block has an error handler specified, use it. Otherwise
                # fall back to the default.
                error_label = block.error_handler or default_error_handler
                branch = Branch(op,
                                true_label=error_label,
                                false_label=next_block,
                                op=variant,
                                line=op.line)
                branch.negated = negated
                if op.line != NO_TRACEBACK_LINE_NO:
                    branch.traceback_entry = (func, op.line)
                partial_ops.add(branch)  # Only tweak true label of these
                new_block.ops.append(branch)
                if i0 == 0:
                    mapping[block] = new_block
                i += 1
                i0 = i
            else:
                i += 1
        new_blocks.append(next_block)
        next_block.ops.extend(ops[i0:i + 1])
        if i0 == 0:
            mapping[block] = next_block
    # Adjust all labels to reflect the new blocks.
    for block in new_blocks:
        for op in block.ops:
            if isinstance(op, Goto):
                op.label = mapping[op.label]
            elif isinstance(op, Branch):
                if op not in partial_ops:
                    op.false = mapping[op.false]
                op.true = mapping[op.true]
    return new_blocks
Esempio n. 5
0
 def process_conditional(self, e: Node) -> List[Branch]:
     if isinstance(e, ComparisonExpr):
         # TODO: Verify operand types.
         assert len(e.operators) == 1, 'more than 1 operator not supported'
         op = e.operators[0]
         if op in ['==', '!=', '<', '<=', '>', '>=']:
             # TODO: check operand types
             left = self.accept(e.operands[0])
             right = self.accept(e.operands[1])
             opcode = self.int_relative_ops[op]
             branch = Branch(left, right, INVALID_LABEL, INVALID_LABEL,
                             opcode)
         elif op in ['is', 'is not']:
             # TODO: check if right operand is None
             left = self.accept(e.operands[0])
             branch = Branch(left, INVALID_REGISTER, INVALID_LABEL,
                             INVALID_LABEL, Branch.IS_NONE)
             if op == 'is not':
                 branch.negated = True
         elif op in ['in', 'not in']:
             left = self.accept(e.operands[0])
             ltype = self.node_type(e.operands[0])
             right = self.accept(e.operands[1])
             rtype = self.node_type(e.operands[1])
             target = self.alloc_temp(self.node_type(e))
             self.binary_op(ltype, left, rtype, right, 'in', target=target)
             branch = Branch(target, INVALID_REGISTER, INVALID_LABEL,
                             INVALID_LABEL, Branch.BOOL_EXPR)
             if op == 'not in':
                 branch.negated = True
         else:
             assert False, "unsupported comparison epxression"
         self.add(branch)
         return [branch]
     elif isinstance(e, OpExpr) and e.op in ['and', 'or']:
         if e.op == 'and':
             # Short circuit 'and' in a conditional context.
             lbranches = self.process_conditional(e.left)
             new = self.new_block()
             self.set_branches(lbranches, True, new)
             rbranches = self.process_conditional(e.right)
             return lbranches + rbranches
         else:
             # Short circuit 'or' in a conditional context.
             lbranches = self.process_conditional(e.left)
             new = self.new_block()
             self.set_branches(lbranches, False, new)
             rbranches = self.process_conditional(e.right)
             return lbranches + rbranches
     elif isinstance(e, UnaryExpr) and e.op == 'not':
         branches = self.process_conditional(e.expr)
         for b in branches:
             b.invert()
         return branches
     # Catch-all for arbitrary expressions.
     else:
         reg = self.accept(e)
         branch = Branch(reg, INVALID_REGISTER, INVALID_LABEL,
                         INVALID_LABEL, Branch.BOOL_EXPR)
         self.add(branch)
         return [branch]