Skip to content

Commit cc8b95c

Browse files
committed
add overflow_checks intrinsic
1 parent 2e5d395 commit cc8b95c

File tree

12 files changed

+81
-10
lines changed

12 files changed

+81
-10
lines changed

compiler/rustc_const_eval/src/check_consts/check.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -645,10 +645,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
645645

646646
Rvalue::Cast(_, _, _) => {}
647647

648-
Rvalue::NullaryOp(
649-
NullOp::OffsetOf(_) | NullOp::RuntimeChecks(_),
650-
_,
651-
) => {}
648+
Rvalue::NullaryOp(NullOp::OffsetOf(_) | NullOp::RuntimeChecks(_), _) => {}
652649
Rvalue::ShallowInitBox(_, _) => {}
653650

654651
Rvalue::UnaryOp(op, operand) => {

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
@@ -1097,6 +1097,9 @@ impl<'tcx> Debug for Rvalue<'tcx> {
10971097
NullOp::RuntimeChecks(RuntimeChecks::ContractChecks) => {
10981098
write!(fmt, "ContractChecks()")
10991099
}
1100+
NullOp::RuntimeChecks(RuntimeChecks::OverflowChecks) => {
1101+
write!(fmt, "OverflowChecks()")
1102+
}
11001103
}
11011104
}
11021105
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
@@ -1577,13 +1577,17 @@ pub enum RuntimeChecks {
15771577
/// Returns whether we should perform contract-checking at runtime.
15781578
/// See the `contract_checks` intrinsic docs for details.
15791579
ContractChecks,
1580+
/// Returns whether we should perform some overflow-checking at runtime.
1581+
/// See the `overflow_checks` intrinsic docs for details.
1582+
OverflowChecks,
15801583
}
15811584

15821585
impl RuntimeChecks {
15831586
pub fn value(self, sess: &rustc_session::Session) -> bool {
15841587
match self {
15851588
Self::UbChecks => sess.ub_checks(),
15861589
Self::ContractChecks => sess.contract_checks(),
1590+
Self::OverflowChecks => sess.overflow_checks(),
15871591
}
15881592
}
15891593
}

compiler/rustc_mir_dataflow/src/move_paths/builder.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -451,10 +451,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> {
451451
Rvalue::Ref(..)
452452
| Rvalue::RawPtr(..)
453453
| Rvalue::Discriminant(..)
454-
| Rvalue::NullaryOp(
455-
NullOp::OffsetOf(..) | NullOp::RuntimeChecks(_),
456-
_,
457-
) => {}
454+
| Rvalue::NullaryOp(NullOp::OffsetOf(..) | NullOp::RuntimeChecks(_), _) => {}
458455
}
459456
}
460457

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
@@ -1033,6 +1033,8 @@ pub enum RuntimeChecks {
10331033
UbChecks,
10341034
/// cfg!(contract_checks), but at codegen time
10351035
ContractChecks,
1036+
/// cfg!(overflow_checks), but at codegen time
1037+
OverflowChecks,
10361038
}
10371039

10381040
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
@@ -330,6 +330,7 @@ impl<'tcx> Stable<'tcx> for mir::NullOp<'tcx> {
330330
RuntimeChecks(op) => crate::mir::NullOp::RuntimeChecks(match op {
331331
UbChecks => crate::mir::RuntimeChecks::UbChecks,
332332
ContractChecks => crate::mir::RuntimeChecks::ContractChecks,
333+
OverflowChecks => crate::mir::RuntimeChecks::OverflowChecks,
333334
}),
334335
}
335336
}

library/core/src/intrinsics/mod.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2585,6 +2585,24 @@ pub const fn ub_checks() -> bool {
25852585
cfg!(ub_checks)
25862586
}
25872587

2588+
/// Returns whether we should perform some overflow-checking at runtime. This eventually evaluates to
2589+
/// `cfg!(overflow_checks)`, but behaves different from `cfg!` when mixing crates built with different
2590+
/// flags: if the crate has overflow checks enabled or carries the `#[rustc_inherit_overflow_checks]`
2591+
/// attribute, evaluation is delayed until monomorphization (or until the call gets inlined into
2592+
/// a crate that does not delay evaluation further); otherwise it can happen any time.
2593+
///
2594+
/// The common case here is a user program built with overflow_checks linked against the distributed
2595+
/// sysroot which is built without overflow_checks but with `#[rustc_inherit_overflow_checks]`.
2596+
/// For code that gets monomorphized in the user crate (i.e., generic functions and functions with
2597+
/// `#[inline]`), gating assertions on `overflow_checks()` rather than `cfg!(overflow_checks)` means that
2598+
/// assertions are enabled whenever the *user crate* has overflow checks enabled. However if the
2599+
/// user has overflow checks disabled, the checks will still get optimized out.
2600+
#[inline(always)]
2601+
#[rustc_intrinsic]
2602+
pub const fn overflow_checks() -> bool {
2603+
cfg!(debug_assertions)
2604+
}
2605+
25882606
/// Allocates a block of memory at compile time.
25892607
/// At runtime, just returns a null pointer.
25902608
///
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//@ compile-flags: -Cdebug-assertions=yes
2+
3+
#![crate_type = "lib"]
4+
#![feature(core_intrinsics)]
5+
6+
/// Emulates the default behavior of `+` using `intrinsics::overflow_checks()`.
7+
#[inline]
8+
pub fn add(a: u8, b: u8) -> u8 {
9+
if core::intrinsics::overflow_checks() { a.strict_add(b) } else { a.wrapping_add(b) }
10+
}

0 commit comments

Comments
 (0)