def test_branch(self) -> None: self.assert_emit( Branch(self.b, BasicBlock(8), BasicBlock(9), Branch.BOOL), """if (cpy_r_b) { goto CPyL8; } else goto CPyL9; """) b = Branch(self.b, BasicBlock(8), BasicBlock(9), Branch.BOOL) b.negated = True self.assert_emit( b, """if (!cpy_r_b) { goto CPyL8; } else goto CPyL9; """)
def test_branch_is_error(self) -> None: b = Branch(self.b, BasicBlock(8), BasicBlock(9), Branch.IS_ERROR) self.assert_emit( b, """if (cpy_r_b == 2) { goto CPyL8; } else goto CPyL9; """) b = Branch(self.b, BasicBlock(8), BasicBlock(9), Branch.IS_ERROR) b.negated = True self.assert_emit( b, """if (cpy_r_b != 2) { goto CPyL8; } else goto CPyL9; """)
def compare_tagged(self, lhs: Value, rhs: Value, op: str, line: int) -> Value: """Compare two tagged integers using given op""" op_type, c_func_desc = int_logical_op_mapping[op] result = self.alloc_temp(bool_rprimitive) short_int_block, int_block, out = BasicBlock(), BasicBlock(), BasicBlock() check = self.check_tagged_short_int(lhs, line) branch = Branch(check, short_int_block, int_block, Branch.BOOL_EXPR) branch.negated = False self.add(branch) self.activate_block(short_int_block) eq = self.binary_int_op(bool_rprimitive, lhs, rhs, op_type, line) self.add(Assign(result, eq, line)) self.goto(out) self.activate_block(int_block) call = self.call_c(c_func_desc, [lhs, rhs], line) self.add(Assign(result, call, line)) self.goto_and_activate(out) return result
def test_cast_and_branch_no_merge_2(self) -> None: op = Cast(self.r, dict_rprimitive, 1) next_block = BasicBlock(9) branch = Branch(op, BasicBlock(8), next_block, Branch.IS_ERROR) branch.negated = True branch.traceback_entry = ('foobar', 123) self.assert_emit( op, """\ if (likely(PyDict_Check(cpy_r_r))) cpy_r_r0 = cpy_r_r; else { CPy_TypeError("dict", cpy_r_r); cpy_r_r0 = NULL; } """, next_block=next_block, next_branch=branch, )
def compare_tagged(self, lhs: Value, rhs: Value, op: str, line: int) -> Value: """Compare two tagged integers using given op""" # generate fast binary logic ops on short ints if is_short_int_rprimitive(lhs.type) and is_short_int_rprimitive( rhs.type): return self.comparison_op(lhs, rhs, int_comparison_op_mapping[op][0], line) op_type, c_func_desc, negate_result, swap_op = int_comparison_op_mapping[ op] result = self.alloc_temp(bool_rprimitive) short_int_block, int_block, out = BasicBlock(), BasicBlock( ), BasicBlock() check_lhs = self.check_tagged_short_int(lhs, line) if op in ("==", "!="): check = check_lhs else: # for non-equal logical ops(less than, greater than, etc.), need to check both side check_rhs = self.check_tagged_short_int(rhs, line) check = self.binary_int_op(bool_rprimitive, check_lhs, check_rhs, BinaryIntOp.AND, line) branch = Branch(check, short_int_block, int_block, Branch.BOOL_EXPR) branch.negated = False self.add(branch) self.activate_block(short_int_block) eq = self.comparison_op(lhs, rhs, op_type, line) self.add(Assign(result, eq, line)) self.goto(out) self.activate_block(int_block) if swap_op: args = [rhs, lhs] else: args = [lhs, rhs] call = self.call_c(c_func_desc, args, line) if negate_result: # TODO: introduce UnaryIntOp? call_result = self.unary_op(call, "not", line) else: call_result = call self.add(Assign(result, call_result, line)) self.goto_and_activate(out) return result
def split_blocks_at_errors(blocks: List[BasicBlock], default_error_handler: BasicBlock, func_name: Optional[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: target = op 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 negated = True elif op.error_kind == ERR_ALWAYS: variant = Branch.BOOL negated = True # this is a hack to represent the always fail # semantics, using a temporary bool with value false tmp = LoadInt(0, rtype=bool_rprimitive) cur_block.ops.append(tmp) target = tmp 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. if op.error_kind != ERR_ALWAYS: assert not op.is_void, "void op generating errors?" branch = Branch(target, true_label=error_label, false_label=new_block, op=variant, line=op.line) branch.negated = negated if op.line != NO_TRACEBACK_LINE_NO and func_name is not None: branch.traceback_entry = (func_name, op.line) cur_block.ops.append(branch) cur_block = new_block return new_blocks