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
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; """)
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; """)
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
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]