add overflow_checks intrinsic
This commit is contained in:
parent
2e5d395f2b
commit
cc8b95cc54
12 changed files with 81 additions and 10 deletions
|
|
@ -645,10 +645,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
|||
|
||||
Rvalue::Cast(_, _, _) => {}
|
||||
|
||||
Rvalue::NullaryOp(
|
||||
NullOp::OffsetOf(_) | NullOp::RuntimeChecks(_),
|
||||
_,
|
||||
) => {}
|
||||
Rvalue::NullaryOp(NullOp::OffsetOf(_) | NullOp::RuntimeChecks(_), _) => {}
|
||||
Rvalue::ShallowInitBox(_, _) => {}
|
||||
|
||||
Rvalue::UnaryOp(op, operand) => {
|
||||
|
|
|
|||
|
|
@ -163,6 +163,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi
|
|||
| sym::minnumf128
|
||||
| sym::mul_with_overflow
|
||||
| sym::needs_drop
|
||||
| sym::overflow_checks
|
||||
| sym::powf16
|
||||
| sym::powf32
|
||||
| sym::powf64
|
||||
|
|
@ -643,7 +644,7 @@ pub(crate) fn check_intrinsic_type(
|
|||
sym::aggregate_raw_ptr => (3, 0, vec![param(1), param(2)], param(0)),
|
||||
sym::ptr_metadata => (2, 0, vec![Ty::new_imm_ptr(tcx, param(0))], param(1)),
|
||||
|
||||
sym::ub_checks => (0, 0, Vec::new(), tcx.types.bool),
|
||||
sym::ub_checks | sym::overflow_checks => (0, 0, Vec::new(), tcx.types.bool),
|
||||
|
||||
sym::box_new => (1, 0, vec![param(0)], Ty::new_box(tcx, param(0))),
|
||||
|
||||
|
|
|
|||
|
|
@ -1097,6 +1097,9 @@ impl<'tcx> Debug for Rvalue<'tcx> {
|
|||
NullOp::RuntimeChecks(RuntimeChecks::ContractChecks) => {
|
||||
write!(fmt, "ContractChecks()")
|
||||
}
|
||||
NullOp::RuntimeChecks(RuntimeChecks::OverflowChecks) => {
|
||||
write!(fmt, "OverflowChecks()")
|
||||
}
|
||||
}
|
||||
}
|
||||
ThreadLocalRef(did) => ty::tls::with(|tcx| {
|
||||
|
|
|
|||
|
|
@ -1577,6 +1577,9 @@ pub enum RuntimeChecks {
|
|||
/// Returns whether we should perform contract-checking at runtime.
|
||||
/// See the `contract_checks` intrinsic docs for details.
|
||||
ContractChecks,
|
||||
/// Returns whether we should perform some overflow-checking at runtime.
|
||||
/// See the `overflow_checks` intrinsic docs for details.
|
||||
OverflowChecks,
|
||||
}
|
||||
|
||||
impl RuntimeChecks {
|
||||
|
|
@ -1584,6 +1587,7 @@ impl RuntimeChecks {
|
|||
match self {
|
||||
Self::UbChecks => sess.ub_checks(),
|
||||
Self::ContractChecks => sess.contract_checks(),
|
||||
Self::OverflowChecks => sess.overflow_checks(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -451,10 +451,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> {
|
|||
Rvalue::Ref(..)
|
||||
| Rvalue::RawPtr(..)
|
||||
| Rvalue::Discriminant(..)
|
||||
| Rvalue::NullaryOp(
|
||||
NullOp::OffsetOf(..) | NullOp::RuntimeChecks(_),
|
||||
_,
|
||||
) => {}
|
||||
| Rvalue::NullaryOp(NullOp::OffsetOf(..) | NullOp::RuntimeChecks(_), _) => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,10 +23,11 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics {
|
|||
sym::unreachable => {
|
||||
terminator.kind = TerminatorKind::Unreachable;
|
||||
}
|
||||
sym::ub_checks | sym::contract_checks => {
|
||||
sym::ub_checks | sym::overflow_checks | sym::contract_checks => {
|
||||
let op = match intrinsic.name {
|
||||
sym::ub_checks => RuntimeChecks::UbChecks,
|
||||
sym::contract_checks => RuntimeChecks::ContractChecks,
|
||||
sym::overflow_checks => RuntimeChecks::OverflowChecks,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let target = target.unwrap();
|
||||
|
|
|
|||
|
|
@ -1033,6 +1033,8 @@ pub enum RuntimeChecks {
|
|||
UbChecks,
|
||||
/// cfg!(contract_checks), but at codegen time
|
||||
ContractChecks,
|
||||
/// cfg!(overflow_checks), but at codegen time
|
||||
OverflowChecks,
|
||||
}
|
||||
|
||||
impl Operand {
|
||||
|
|
|
|||
|
|
@ -330,6 +330,7 @@ impl<'tcx> Stable<'tcx> for mir::NullOp<'tcx> {
|
|||
RuntimeChecks(op) => crate::mir::NullOp::RuntimeChecks(match op {
|
||||
UbChecks => crate::mir::RuntimeChecks::UbChecks,
|
||||
ContractChecks => crate::mir::RuntimeChecks::ContractChecks,
|
||||
OverflowChecks => crate::mir::RuntimeChecks::OverflowChecks,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2585,6 +2585,24 @@ pub const fn ub_checks() -> bool {
|
|||
cfg!(ub_checks)
|
||||
}
|
||||
|
||||
/// Returns whether we should perform some overflow-checking at runtime. This eventually evaluates to
|
||||
/// `cfg!(overflow_checks)`, but behaves different from `cfg!` when mixing crates built with different
|
||||
/// flags: if the crate has overflow checks enabled or carries the `#[rustc_inherit_overflow_checks]`
|
||||
/// attribute, evaluation is delayed until monomorphization (or until the call gets inlined into
|
||||
/// a crate that does not delay evaluation further); otherwise it can happen any time.
|
||||
///
|
||||
/// The common case here is a user program built with overflow_checks linked against the distributed
|
||||
/// sysroot which is built without overflow_checks but with `#[rustc_inherit_overflow_checks]`.
|
||||
/// For code that gets monomorphized in the user crate (i.e., generic functions and functions with
|
||||
/// `#[inline]`), gating assertions on `overflow_checks()` rather than `cfg!(overflow_checks)` means that
|
||||
/// assertions are enabled whenever the *user crate* has overflow checks enabled. However if the
|
||||
/// user has overflow checks disabled, the checks will still get optimized out.
|
||||
#[inline(always)]
|
||||
#[rustc_intrinsic]
|
||||
pub const fn overflow_checks() -> bool {
|
||||
cfg!(debug_assertions)
|
||||
}
|
||||
|
||||
/// Allocates a block of memory at compile time.
|
||||
/// At runtime, just returns a null pointer.
|
||||
///
|
||||
|
|
|
|||
10
tests/codegen-llvm/auxiliary/overflow_checks_add.rs
Normal file
10
tests/codegen-llvm/auxiliary/overflow_checks_add.rs
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
//@ compile-flags: -Cdebug-assertions=yes
|
||||
|
||||
#![crate_type = "lib"]
|
||||
#![feature(core_intrinsics)]
|
||||
|
||||
/// Emulates the default behavior of `+` using `intrinsics::overflow_checks()`.
|
||||
#[inline]
|
||||
pub fn add(a: u8, b: u8) -> u8 {
|
||||
if core::intrinsics::overflow_checks() { a.strict_add(b) } else { a.wrapping_add(b) }
|
||||
}
|
||||
29
tests/codegen-llvm/overflow-checks.rs
Normal file
29
tests/codegen-llvm/overflow-checks.rs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
// With -Coverflow-checks=yes (enabled by default by -Cdebug-assertions=yes) we will produce a
|
||||
// runtime check that panics when an operation would result in integer overflow.
|
||||
//
|
||||
// This test ensures that such a runtime check is *not* emitted when debug-assertions are enabled,
|
||||
// but overflow-checks are explicitly disabled. It also ensures that even if a dependency is
|
||||
// compiled with overflow checks, `intrinsics::overflow_checks()` will be treated with the
|
||||
// overflow-checks setting of the current crate (when `#[rustc_inherit_overflow_checks]`) is used.
|
||||
|
||||
//@ aux-build:overflow_checks_add.rs
|
||||
//@ revisions: DEBUG NOCHECKS
|
||||
//@ compile-flags: -O -Cdebug-assertions=yes
|
||||
//@ [NOCHECKS] compile-flags: -Coverflow-checks=no
|
||||
|
||||
#![crate_type = "lib"]
|
||||
|
||||
extern crate overflow_checks_add;
|
||||
|
||||
// CHECK-LABEL: @add(
|
||||
#[no_mangle]
|
||||
pub unsafe fn add(a: u8, b: u8) -> u8 {
|
||||
// CHECK: i8 noundef %a, i8 noundef %b
|
||||
// CHECK: add i8 %b, %a
|
||||
// DEBUG: icmp ult i8 [[zero:[^,]+]], %a
|
||||
// DEBUG: call core::num::overflow_panic::add
|
||||
// DEBUG: unreachable
|
||||
// NOCHECKS-NOT: unreachable
|
||||
// NOCHECKS: ret i8 %0
|
||||
overflow_checks_add::add(a, b)
|
||||
}
|
||||
8
tests/ui/consts/const-eval/overflow_checks.rs
Normal file
8
tests/ui/consts/const-eval/overflow_checks.rs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
//@ build-pass
|
||||
//@ compile-flags: -O -C overflow-checks=no
|
||||
|
||||
#![crate_type = "lib"]
|
||||
#![feature(core_intrinsics)]
|
||||
|
||||
// Always returns true during CTFE, even if overflow checks are disabled.
|
||||
const _: () = assert!(core::intrinsics::overflow_checks());
|
||||
Loading…
Add table
Add a link
Reference in a new issue