Rollup merge of #109234 - tmiasko:overflow-checks, r=cjgillot

Tweak implementation of overflow checking assertions

Extract and reuse logic controlling behaviour of overflow checking assertions instead of duplicating it three times.

r? `@cjgillot`
This commit is contained in:
Matthias Krüger 2023-03-18 12:04:23 +01:00 committed by GitHub
commit a48d83d556
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 19 additions and 41 deletions

View File

@ -346,17 +346,10 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
crate::abi::codegen_return(fx); crate::abi::codegen_return(fx);
} }
TerminatorKind::Assert { cond, expected, msg, target, cleanup: _ } => { TerminatorKind::Assert { cond, expected, msg, target, cleanup: _ } => {
if !fx.tcx.sess.overflow_checks() { if !fx.tcx.sess.overflow_checks() && msg.is_optional_overflow_check() {
let overflow_not_to_check = match msg { let target = fx.get_block(*target);
AssertKind::OverflowNeg(..) => true, fx.bcx.ins().jump(target, &[]);
AssertKind::Overflow(op, ..) => op.is_checkable(), continue;
_ => false,
};
if overflow_not_to_check {
let target = fx.get_block(*target);
fx.bcx.ins().jump(target, &[]);
continue;
}
} }
let cond = codegen_operand(fx, cond).load_scalar(fx); let cond = codegen_operand(fx, cond).load_scalar(fx);

View File

@ -563,15 +563,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
// with #[rustc_inherit_overflow_checks] and inlined from // with #[rustc_inherit_overflow_checks] and inlined from
// another crate (mostly core::num generic/#[inline] fns), // another crate (mostly core::num generic/#[inline] fns),
// while the current crate doesn't use overflow checks. // while the current crate doesn't use overflow checks.
if !bx.cx().check_overflow() { if !bx.cx().check_overflow() && msg.is_optional_overflow_check() {
let overflow_not_to_check = match msg { const_cond = Some(expected);
AssertKind::OverflowNeg(..) => true,
AssertKind::Overflow(op, ..) => op.is_checkable(),
_ => false,
};
if overflow_not_to_check {
const_cond = Some(expected);
}
} }
// Don't codegen the panic block if success if known. // Don't codegen the panic block if success if known.

View File

@ -155,7 +155,7 @@ pub trait Machine<'mir, 'tcx>: Sized {
/// Whether Assert(OverflowNeg) and Assert(Overflow) MIR terminators should actually /// Whether Assert(OverflowNeg) and Assert(Overflow) MIR terminators should actually
/// check for overflow. /// check for overflow.
fn ignore_checkable_overflow_assertions(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool; fn ignore_optional_overflow_checks(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;
/// Entry point for obtaining the MIR of anything that should get evaluated. /// Entry point for obtaining the MIR of anything that should get evaluated.
/// So not just functions and shims, but also const/static initializers, anonymous /// So not just functions and shims, but also const/static initializers, anonymous
@ -474,7 +474,7 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) {
} }
#[inline(always)] #[inline(always)]
fn ignore_checkable_overflow_assertions(_ecx: &InterpCx<$mir, $tcx, Self>) -> bool { fn ignore_optional_overflow_checks(_ecx: &InterpCx<$mir, $tcx, Self>) -> bool {
false false
} }

View File

@ -138,12 +138,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
} }
Assert { ref cond, expected, ref msg, target, cleanup } => { Assert { ref cond, expected, ref msg, target, cleanup } => {
let ignored = M::ignore_checkable_overflow_assertions(self) let ignored =
&& match msg { M::ignore_optional_overflow_checks(self) && msg.is_optional_overflow_check();
mir::AssertKind::OverflowNeg(..) => true,
mir::AssertKind::Overflow(op, ..) => op.is_checkable(),
_ => false,
};
let cond_val = self.read_scalar(&self.eval_operand(cond, None)?)?.to_bool()?; let cond_val = self.read_scalar(&self.eval_operand(cond, None)?)?.to_bool()?;
if ignored || expected == cond_val { if ignored || expected == cond_val {
self.go_to_block(target); self.go_to_block(target);

View File

@ -1268,6 +1268,13 @@ impl<'tcx> BasicBlockData<'tcx> {
} }
impl<O> AssertKind<O> { impl<O> AssertKind<O> {
/// Returns true if this an overflow checking assertion controlled by -C overflow-checks.
pub fn is_optional_overflow_check(&self) -> bool {
use AssertKind::*;
use BinOp::*;
matches!(self, OverflowNeg(..) | Overflow(Add | Sub | Mul | Shl | Shr, ..))
}
/// Getting a description does not require `O` to be printable, and does not /// Getting a description does not require `O` to be printable, and does not
/// require allocation. /// require allocation.
/// The caller is expected to handle `BoundsCheck` separately. /// The caller is expected to handle `BoundsCheck` separately.
@ -1992,16 +1999,6 @@ impl BorrowKind {
} }
} }
impl BinOp {
/// The checkable operators are those whose overflow checking behavior is controlled by
/// -Coverflow-checks option. The remaining operators have either no overflow conditions (e.g.,
/// BitAnd, BitOr, BitXor) or are always checked for overflow (e.g., Div, Rem).
pub fn is_checkable(self) -> bool {
use self::BinOp::*;
matches!(self, Add | Sub | Mul | Shl | Shr)
}
}
impl<'tcx> Debug for Rvalue<'tcx> { impl<'tcx> Debug for Rvalue<'tcx> {
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
use self::Rvalue::*; use self::Rvalue::*;

View File

@ -646,8 +646,7 @@ pub enum TerminatorKind<'tcx> {
/// When overflow checking is disabled and this is run-time MIR (as opposed to compile-time MIR /// When overflow checking is disabled and this is run-time MIR (as opposed to compile-time MIR
/// that is used for CTFE), the following variants of this terminator behave as `goto target`: /// that is used for CTFE), the following variants of this terminator behave as `goto target`:
/// - `OverflowNeg(..)`, /// - `OverflowNeg(..)`,
/// - `Overflow(op, ..)` if op is a "checkable" operation (add, sub, mul, shl, shr, but NOT /// - `Overflow(op, ..)` if op is add, sub, mul, shl, shr, but NOT div or rem.
/// div or rem).
Assert { Assert {
cond: Operand<'tcx>, cond: Operand<'tcx>,
expected: bool, expected: bool,

View File

@ -822,7 +822,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
} }
#[inline(always)] #[inline(always)]
fn ignore_checkable_overflow_assertions(ecx: &MiriInterpCx<'mir, 'tcx>) -> bool { fn ignore_optional_overflow_checks(ecx: &MiriInterpCx<'mir, 'tcx>) -> bool {
!ecx.tcx.sess.overflow_checks() !ecx.tcx.sess.overflow_checks()
} }