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:
commit
a48d83d556
@ -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);
|
||||||
|
|
||||||
|
@ -563,15 +563,8 @@ fn codegen_assert_terminator(
|
|||||||
// 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.
|
||||||
|
@ -155,7 +155,7 @@ fn enforce_abi(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
|
|||||||
|
|
||||||
/// 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 @@ fn use_addr_for_alignment_check(_ecx: &InterpCx<$mir, $tcx, Self>) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,12 +138,8 @@ pub(super) fn eval_terminator(
|
|||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
|
@ -1268,6 +1268,13 @@ pub fn is_empty_unreachable(&self) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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 @@ pub fn describe_mutability(&self) -> &str {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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::*;
|
||||||
|
@ -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,
|
||||||
|
@ -822,7 +822,7 @@ fn enforce_abi(ecx: &MiriInterpCx<'mir, 'tcx>) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user