Skip to content

Commit a2d97d3

Browse files
committed
add overflow_checks intrinsic
1 parent d86784c commit a2d97d3

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
@@ -120,6 +120,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi
120120
| sym::contract_checks
121121
| sym::contract_check_requires
122122
| sym::contract_check_ensures
123+
| sym::overflow_checks
123124
| sym::fadd_algebraic
124125
| sym::fsub_algebraic
125126
| sym::fmul_algebraic
@@ -591,7 +592,7 @@ pub(crate) fn check_intrinsic_type(
591592
sym::aggregate_raw_ptr => (3, 0, vec![param(1), param(2)], param(0)),
592593
sym::ptr_metadata => (2, 0, vec![Ty::new_imm_ptr(tcx, param(0))], param(1)),
593594

594-
sym::ub_checks => (0, 0, Vec::new(), tcx.types.bool),
595+
sym::ub_checks | sym::overflow_checks => (0, 0, Vec::new(), tcx.types.bool),
595596

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

compiler/rustc_middle/src/mir/pretty.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1138,6 +1138,9 @@ impl<'tcx> Debug for Rvalue<'tcx> {
11381138
NullOp::RuntimeChecks(RuntimeChecks::ContractChecks) => {
11391139
write!(fmt, "ContractChecks()")
11401140
}
1141+
NullOp::RuntimeChecks(RuntimeChecks::OverflowChecks) => {
1142+
write!(fmt, "OverflowChecks()")
1143+
}
11411144
}
11421145
}
11431146
ThreadLocalRef(did) => ty::tls::with(|tcx| {

compiler/rustc_middle/src/mir/syntax.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1600,6 +1600,9 @@ pub enum RuntimeChecks {
16001600
/// Returns whether we should perform contract-checking at runtime.
16011601
/// See the `contract_checks` intrinsic docs for details.
16021602
ContractChecks,
1603+
/// Returns whether we should perform some overflow-checking at runtime.
1604+
/// See the `overflow_checks` intrinsic docs for details.
1605+
OverflowChecks,
16031606
}
16041607

16051608
impl RuntimeChecks {

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 => {
26+
sym::ub_checks | sym::overflow_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_smir/src/rustc_smir/convert/mir.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,7 @@ impl<'tcx> Stable<'tcx> for mir::NullOp<'tcx> {
295295
RuntimeChecks(kind) => stable_mir::mir::NullOp::RuntimeChecks(match kind {
296296
UbChecks => stable_mir::mir::RuntimeChecks::UbChecks,
297297
ContractChecks => stable_mir::mir::RuntimeChecks::ContractChecks,
298+
OverflowChecks => stable_mir::mir::RuntimeChecks::OverflowChecks,
298299
}),
299300
}
300301
}

compiler/rustc_smir/src/stable_mir/mir/body.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,6 +1049,8 @@ pub enum RuntimeChecks {
10491049
UbChecks,
10501050
/// cfg!(contract_checks), but at codegen time
10511051
ContractChecks,
1052+
/// cfg!(overflow_checks), but at codegen time
1053+
OverflowChecks,
10521054
}
10531055

10541056
impl Operand {

library/core/src/intrinsics/mod.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3346,6 +3346,26 @@ pub const unsafe fn typed_swap_nonoverlapping<T>(x: *mut T, y: *mut T) {
33463346
pub const fn ub_checks() -> bool {
33473347
cfg!(ub_checks)
33483348
}
3349+
/// Returns whether we should perform some overflow-checking at runtime. This eventually evaluates to
3350+
/// `cfg!(overflow_checks)`, but behaves different from `cfg!` when mixing crates built with different
3351+
/// flags: if the crate has UB checks enabled or carries the `#[rustc_preserve_overflow_checks]`
3352+
/// attribute, evaluation is delayed until monomorphization (or until the call gets inlined into
3353+
/// a crate that does not delay evaluation further); otherwise it can happen any time.
3354+
///
3355+
/// The common case here is a user program built with overflow_checks linked against the distributed
3356+
/// sysroot which is built without overflow_checks but with `#[rustc_preserve_overflow_checks]`.
3357+
/// For code that gets monomorphized in the user crate (i.e., generic functions and functions with
3358+
/// `#[inline]`), gating assertions on `overflow_checks()` rather than `cfg!(overflow_checks)` means that
3359+
/// assertions are enabled whenever the *user crate* has UB checks enabled. However if the
3360+
/// user has overflow checks disabled, the checks will still get optimized out.
3361+
#[cfg(not(bootstrap))]
3362+
#[rustc_const_unstable(feature = "const_overflow_checks", issue = "none")]
3363+
#[unstable(feature = "const_overflow_checks", issue = "none")]
3364+
#[inline(always)]
3365+
#[rustc_intrinsic]
3366+
pub const fn overflow_checks() -> bool {
3367+
cfg!(debug_assertions)
3368+
}
33493369

33503370
/// Allocates a block of memory at compile time.
33513371
/// 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(strict_overflow_ops)]
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+
}

tests/codegen/overflow-checks.rs

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+
//@ [DEBUG] compile-flags:
12+
//@ [NOCHECKS] compile-flags: -Coverflow-checks=no
13+
//@ compile-flags: -O -Cdebug-assertions=yes
14+
15+
#![crate_type = "lib"]
16+
17+
extern crate overflow_checks_add;
18+
19+
// CHECK-LABEL: @add(
20+
#[no_mangle]
21+
pub unsafe fn add(a: u8, b: u8) -> u8 {
22+
// CHECK: i8 noundef %a, i8 noundef %b
23+
// DEBUG: @llvm.uadd.with.overflow.i8
24+
// DEBUG: call core::num::overflow_panic::add
25+
// DEBUG: unreachable
26+
// NOCHECKS: %0 = add i8 %b, %a
27+
// NOCHECKS: ret i8 %0
28+
overflow_checks_add::add(a, b)
29+
}

0 commit comments

Comments
 (0)