Skip to content

Commit 8939b87

Browse files
committed
add overflow_checks intrinsic
1 parent 78016a2 commit 8939b87

File tree

9 files changed

+76
-2
lines changed

9 files changed

+76
-2
lines changed

compiler/rustc_hir_analysis/src/check/intrinsic.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi
126126
| sym::contract_checks
127127
| sym::contract_check_requires
128128
| sym::contract_check_ensures
129+
| sym::overflow_checks
129130
| sym::fadd_algebraic
130131
| sym::fsub_algebraic
131132
| sym::fmul_algebraic
@@ -567,7 +568,7 @@ pub(crate) fn check_intrinsic_type(
567568
sym::aggregate_raw_ptr => (3, 0, vec![param(1), param(2)], param(0)),
568569
sym::ptr_metadata => (2, 0, vec![Ty::new_imm_ptr(tcx, param(0))], param(1)),
569570

570-
sym::ub_checks => (0, 0, Vec::new(), tcx.types.bool),
571+
sym::ub_checks | sym::overflow_checks => (0, 0, Vec::new(), tcx.types.bool),
571572

572573
sym::box_new => (1, 0, vec![param(0)], Ty::new_box(tcx, param(0))),
573574

compiler/rustc_middle/src/mir/pretty.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1077,6 +1077,9 @@ impl<'tcx> Debug for Rvalue<'tcx> {
10771077
NullOp::RuntimeChecks(RuntimeChecks::ContractChecks) => {
10781078
write!(fmt, "ContractChecks()")
10791079
}
1080+
NullOp::RuntimeChecks(RuntimeChecks::OverflowChecks) => {
1081+
write!(fmt, "OverflowChecks()")
1082+
}
10801083
}
10811084
}
10821085
ThreadLocalRef(did) => ty::tls::with(|tcx| {

compiler/rustc_middle/src/mir/syntax.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1587,13 +1587,17 @@ pub enum RuntimeChecks {
15871587
/// Returns whether we should perform contract-checking at runtime.
15881588
/// See the `contract_checks` intrinsic docs for details.
15891589
ContractChecks,
1590+
/// Returns whether we should perform some overflow-checking at runtime.
1591+
/// See the `overflow_checks` intrinsic docs for details.
1592+
OverflowChecks,
15901593
}
15911594

15921595
impl RuntimeChecks {
15931596
pub fn value(self, sess: &rustc_session::Session) -> bool {
15941597
match self {
15951598
Self::UbChecks => sess.ub_checks(),
15961599
Self::ContractChecks => sess.contract_checks(),
1600+
Self::OverflowChecks => sess.overflow_checks(),
15971601
}
15981602
}
15991603
}

compiler/rustc_mir_transform/src/lower_intrinsics.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,11 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics {
2323
sym::unreachable => {
2424
terminator.kind = TerminatorKind::Unreachable;
2525
}
26-
sym::ub_checks | sym::contract_checks => {
26+
sym::ub_checks | sym::overflow_checks | sym::contract_checks => {
2727
let op = match intrinsic.name {
2828
sym::ub_checks => RuntimeChecks::UbChecks,
2929
sym::contract_checks => RuntimeChecks::ContractChecks,
30+
sym::overflow_checks => RuntimeChecks::OverflowChecks,
3031
_ => unreachable!(),
3132
};
3233
let target = target.unwrap();

compiler/rustc_public/src/mir/body.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,6 +1047,8 @@ pub enum RuntimeChecks {
10471047
UbChecks,
10481048
/// cfg!(contract_checks), but at codegen time
10491049
ContractChecks,
1050+
/// cfg!(overflow_checks), but at codegen time
1051+
OverflowChecks,
10501052
}
10511053

10521054
impl Operand {

compiler/rustc_public/src/unstable/convert/stable/mir.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,7 @@ impl<'tcx> Stable<'tcx> for mir::NullOp<'tcx> {
336336
RuntimeChecks(op) => crate::mir::NullOp::RuntimeChecks(match op {
337337
UbChecks => crate::mir::RuntimeChecks::UbChecks,
338338
ContractChecks => crate::mir::RuntimeChecks::ContractChecks,
339+
OverflowChecks => crate::mir::RuntimeChecks::OverflowChecks,
339340
}),
340341
}
341342
}

library/core/src/intrinsics/mod.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2584,6 +2584,25 @@ pub const unsafe fn typed_swap_nonoverlapping<T>(x: *mut T, y: *mut T) {
25842584
pub const fn ub_checks() -> bool {
25852585
cfg!(ub_checks)
25862586
}
2587+
/// Returns whether we should perform some overflow-checking at runtime. This eventually evaluates to
2588+
/// `cfg!(overflow_checks)`, but behaves different from `cfg!` when mixing crates built with different
2589+
/// flags: if the crate has UB checks enabled or carries the `#[rustc_preserve_overflow_checks]`
2590+
/// attribute, evaluation is delayed until monomorphization (or until the call gets inlined into
2591+
/// a crate that does not delay evaluation further); otherwise it can happen any time.
2592+
///
2593+
/// The common case here is a user program built with overflow_checks linked against the distributed
2594+
/// sysroot which is built without overflow_checks but with `#[rustc_preserve_overflow_checks]`.
2595+
/// For code that gets monomorphized in the user crate (i.e., generic functions and functions with
2596+
/// `#[inline]`), gating assertions on `overflow_checks()` rather than `cfg!(overflow_checks)` means that
2597+
/// assertions are enabled whenever the *user crate* has UB checks enabled. However if the
2598+
/// user has overflow checks disabled, the checks will still get optimized out.
2599+
#[rustc_const_unstable(feature = "const_overflow_checks", issue = "none")]
2600+
#[unstable(feature = "const_overflow_checks", issue = "none")]
2601+
#[inline(always)]
2602+
#[rustc_intrinsic]
2603+
pub const fn overflow_checks() -> bool {
2604+
cfg!(debug_assertions)
2605+
}
25872606

25882607
/// Allocates a block of memory at compile time.
25892608
/// At runtime, just returns a null pointer.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//@ compile-flags: -Cdebug-assertions=yes
2+
3+
#![crate_type = "lib"]
4+
#![feature(const_overflow_checks)]
5+
#![feature(rustc_attrs)]
6+
#![feature(core_intrinsics)]
7+
8+
/// Emulates the default behavior of `+` using
9+
/// `intrinsics::overflow_checks()`.
10+
#[inline]
11+
#[rustc_inherit_overflow_checks]
12+
pub fn add(a: u8, b: u8) -> u8 {
13+
if core::intrinsics::overflow_checks() { a.strict_add(b) } else { a.wrapping_add(b) }
14+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// With -Coverflow-checks=yes (enabled by default by -Cdebug-assertions=yes) we will produce a
2+
// runtime check that panics when an operation would result in integer overflow.
3+
//
4+
// This test ensures that such a runtime check is *not* emitted when debug-assertions are enabled,
5+
// but overflow-checks are explicitly disabled. It also ensures that even if a dependency is
6+
// compiled with overflow checks, `intrinsics::overflow_checks()` will be treated with the
7+
// overflow-checks setting of the current crate (when `#[rustc_inherit_overflow_checks]`) is used.
8+
9+
//@ aux-build:overflow_checks_add.rs
10+
//@ revisions: DEBUG NOCHECKS
11+
//@ compile-flags: -O -Cdebug-assertions=yes
12+
//@ [NOCHECKS] compile-flags: -Coverflow-checks=no
13+
14+
#![crate_type = "lib"]
15+
16+
extern crate overflow_checks_add;
17+
18+
// CHECK-LABEL: @add(
19+
#[no_mangle]
20+
pub unsafe fn add(a: u8, b: u8) -> u8 {
21+
// CHECK: i8 noundef %a, i8 noundef %b
22+
// CHECK: add i8 %b, %a
23+
// DEBUG: icmp ult i8 [[zero:[^,]+]], %a
24+
// DEBUG: call core::num::overflow_panic::add
25+
// DEBUG: unreachable
26+
// NOCHECKS-NOT: unreachable
27+
// NOCHECKS: ret i8 %0
28+
overflow_checks_add::add(a, b)
29+
}

0 commit comments

Comments
 (0)