no_std_compat::hint

Function assert_unchecked

1.81.0 (const: 1.81.0) · Source
pub const unsafe fn assert_unchecked(cond: bool)
Expand description

Makes a soundness promise to the compiler that cond holds.

This may allow the optimizer to simplify things, but it might also make the generated code slower. Either way, calling it will most likely make compilation take longer.

You may know this from other places as llvm.assume or, in C, __builtin_assume.

This promotes a correctness requirement to a soundness requirement. Don’t do that without very good reason.

§Usage

This is a situational tool for micro-optimization, and is allowed to do nothing. Any use should come with a repeatable benchmark to show the value, with the expectation to drop it later should the optimizer get smarter and no longer need it.

The more complicated the condition, the less likely this is to be useful. For example, assert_unchecked(foo.is_sorted()) is a complex enough value that the compiler is unlikely to be able to take advantage of it.

There’s also no need to assert_unchecked basic properties of things. For example, the compiler already knows the range of count_ones, so there is no benefit to let n = u32::count_ones(x); assert_unchecked(n <= u32::BITS);.

assert_unchecked is logically equivalent to if !cond { unreachable_unchecked(); }. If ever you are tempted to write assert_unchecked(false), you should instead use unreachable_unchecked() directly.

§Safety

cond must be true. It is immediate UB to call this with false.

§Example

use core::hint;

/// # Safety
///
/// `p` must be nonnull and valid
pub unsafe fn next_value(p: *const i32) -> i32 {
    // SAFETY: caller invariants guarantee that `p` is not null
    unsafe { hint::assert_unchecked(!p.is_null()) }

    if p.is_null() {
        return -1;
    } else {
        // SAFETY: caller invariants guarantee that `p` is valid
        unsafe { *p + 1 }
    }
}

Without the assert_unchecked, the above function produces the following with optimizations enabled:

next_value:
        test    rdi, rdi
        je      .LBB0_1
        mov     eax, dword ptr [rdi]
        inc     eax
        ret
.LBB0_1:
        mov     eax, -1
        ret

Adding the assertion allows the optimizer to remove the extra check:

next_value:
        mov     eax, dword ptr [rdi]
        inc     eax
        ret

This example is quite unlike anything that would be used in the real world: it is redundant to put an assertion right next to code that checks the same thing, and dereferencing a pointer already has the builtin assumption that it is nonnull. However, it illustrates the kind of changes the optimizer can make even when the behavior is less obviously related.