def binary_op(self, lreg: Value, rreg: Value, expr_op: str, line: int) -> Value: # special case tuple comparison here so that nested tuples can be supported if (isinstance(lreg.type, RTuple) and isinstance(rreg.type, RTuple) and expr_op in ('==', '!=')): return self.compare_tuples(lreg, rreg, expr_op, line) # Special case == and != when we can resolve the method call statically. value = None if expr_op in ('==', '!='): value = self.translate_eq_cmp(lreg, rreg, expr_op, line) if value is not None: return value # Special case 'is' and 'is not' if expr_op in ('is', 'is not'): return self.translate_is_op(lreg, rreg, expr_op, line) if (is_str_rprimitive(lreg.type) and is_str_rprimitive(rreg.type) and expr_op in ('==', '!=')): return self.compare_strings(lreg, rreg, expr_op, line) if is_tagged(lreg.type) and is_tagged( rreg.type) and expr_op in int_comparison_op_mapping: return self.compare_tagged(lreg, rreg, expr_op, line) call_c_ops_candidates = c_binary_ops.get(expr_op, []) target = self.matching_call_c(call_c_ops_candidates, [lreg, rreg], line) assert target, 'Unsupported binary operation: %s' % expr_op return target
def maybe_process_conditional_comparison(self: IRBuilder, e: Expression, true: BasicBlock, false: BasicBlock) -> bool: """Transform simple tagged integer comparisons in a conditional context. Return True if the operation is supported (and was transformed). Otherwise, do nothing and return False. Args: e: Arbitrary expression true: Branch target if comparison is true false: Branch target if comparison is false """ if not isinstance(e, ComparisonExpr) or len(e.operands) != 2: return False ltype = self.node_type(e.operands[0]) rtype = self.node_type(e.operands[1]) if not is_tagged(ltype) or not is_tagged(rtype): return False op = e.operators[0] if op not in ('==', '!=', '<', '<=', '>', '>='): return False left_expr = e.operands[0] right_expr = e.operands[1] borrow_left = is_borrow_friendly_expr(self, right_expr) left = self.accept(left_expr, can_borrow=borrow_left) right = self.accept(right_expr, can_borrow=True) # "left op right" for two tagged integers self.builder.compare_tagged_condition(left, right, op, true, false, e.line) return True
def transform_operator_assignment_stmt(builder: IRBuilder, stmt: OperatorAssignmentStmt) -> None: """Operator assignment statement such as x += 1""" builder.disallow_class_assignments([stmt.lvalue], stmt.line) if (is_tagged(builder.node_type(stmt.lvalue)) and is_tagged(builder.node_type(stmt.rvalue)) and stmt.op in int_borrow_friendly_op): can_borrow = (is_borrow_friendly_expr(builder, stmt.rvalue) and is_borrow_friendly_expr(builder, stmt.lvalue)) else: can_borrow = False target = builder.get_assignment_target(stmt.lvalue) target_value = builder.read(target, stmt.line, can_borrow=can_borrow) rreg = builder.accept(stmt.rvalue, can_borrow=can_borrow) # the Python parser strips the '=' from operator assignment statements, so re-add it op = stmt.op + '=' res = builder.binary_op(target_value, rreg, op, stmt.line) # usually operator assignments are done in-place # but when target doesn't support that we need to manually assign builder.assign(target, res, res.line)
def binary_op(self, lreg: Value, rreg: Value, expr_op: str, line: int) -> Value: # Special case == and != when we can resolve the method call statically. value = None if expr_op in ('==', '!='): value = self.translate_eq_cmp(lreg, rreg, expr_op, line) if value is not None: return value if is_tagged(lreg.type) and is_tagged( rreg.type) and expr_op in int_logical_op_mapping: return self.compare_tagged(lreg, rreg, expr_op, line) call_c_ops_candidates = c_binary_ops.get(expr_op, []) target = self.matching_call_c(call_c_ops_candidates, [lreg, rreg], line) if target: return target ops = binary_ops.get(expr_op, []) target = self.matching_primitive_op(ops, [lreg, rreg], line) assert target, 'Unsupported binary operation: %s' % expr_op return target
def emit_signed_int_cast(self, type: RType) -> str: if is_tagged(type): return '(Py_ssize_t)' else: return ''