Skip to content

Commit e79bbaa

Browse files
committed
add overflow_checks intrinsic
1 parent 5518779 commit e79bbaa

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
@@ -163,6 +163,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi
163163
| sym::minnumf128
164164
| sym::mul_with_overflow
165165
| sym::needs_drop
166+
| sym::overflow_checks
166167
| sym::powf16
167168
| sym::powf32
168169
| sym::powf64
@@ -643,7 +644,7 @@ pub(crate) fn check_intrinsic_type(
643644
sym::aggregate_raw_ptr => (3, 0, vec![param(1), param(2)], param(0)),
644645
sym::ptr_metadata => (2, 0, vec![Ty::new_imm_ptr(tcx, param(0))], param(1)),
645646

646-
sym::ub_checks => (0, 0, Vec::new(), tcx.types.bool),
647+
sym::ub_checks | sym::overflow_checks => (0, 0, Vec::new(), tcx.types.bool),
647648

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

compiler/rustc_middle/src/mir/pretty.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1100,6 +1100,9 @@ impl<'tcx> Debug for Rvalue<'tcx> {
11001100
NullOp::RuntimeChecks(RuntimeChecks::ContractChecks) => {
11011101
write!(fmt, "ContractChecks()")
11021102
}
1103+
NullOp::RuntimeChecks(RuntimeChecks::OverflowChecks) => {
1104+
write!(fmt, "OverflowChecks()")
1105+
}
11031106
}
11041107
}
11051108
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
@@ -1579,13 +1579,17 @@ pub enum RuntimeChecks {
15791579
/// Returns whether we should perform contract-checking at runtime.
15801580
/// See the `contract_checks` intrinsic docs for details.
15811581
ContractChecks,
1582+
/// Returns whether we should perform some overflow-checking at runtime.
1583+
/// See the `overflow_checks` intrinsic docs for details.
1584+
OverflowChecks,
15821585
}
15831586

15841587
impl RuntimeChecks {
15851588
pub fn value(self, sess: &rustc_session::Session) -> bool {
15861589
match self {
15871590
Self::UbChecks => sess.ub_checks(),
15881591
Self::ContractChecks => sess.contract_checks(),
1592+
Self::OverflowChecks => sess.overflow_checks(),
15891593
}
15901594
}
15911595
}

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
@@ -1040,6 +1040,8 @@ pub enum RuntimeChecks {
10401040
UbChecks,
10411041
/// cfg!(contract_checks), but at codegen time
10421042
ContractChecks,
1043+
/// cfg!(overflow_checks), but at codegen time
1044+
OverflowChecks,
10431045
}
10441046

10451047
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
@@ -335,6 +335,7 @@ impl<'tcx> Stable<'tcx> for mir::NullOp<'tcx> {
335335
RuntimeChecks(op) => crate::mir::NullOp::RuntimeChecks(match op {
336336
UbChecks => crate::mir::RuntimeChecks::UbChecks,
337337
ContractChecks => crate::mir::RuntimeChecks::ContractChecks,
338+
OverflowChecks => crate::mir::RuntimeChecks::OverflowChecks,
338339
}),
339340
}
340341
}

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)