From 9d0041c40d7ef7275dfc89b6086eaf90bad3e111 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Sun, 7 Apr 2024 04:08:25 -0400 Subject: [PATCH] Update documentation for `hint::assert_unchecked` Rearrange the sections and add an example to `core::hint::assert_unchecked`. --- core/src/hint.rs | 93 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 73 insertions(+), 20 deletions(-) diff --git a/core/src/hint.rs b/core/src/hint.rs index 6e2d88c6b8337..b8db4ad8237bd 100644 --- a/core/src/hint.rs +++ b/core/src/hint.rs @@ -111,36 +111,89 @@ pub const unsafe fn unreachable_unchecked() -> ! { /// 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. +/// 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. /// -/// 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 -/// and allow removing it later should the optimizer get smarter and no longer need it. +/// You may know this from other places as +/// [`llvm.assume`](https://llvm.org/docs/LangRef.html#llvm-assume-intrinsic) or, in C, +/// [`__builtin_assume`](https://clang.llvm.org/docs/LanguageExtensions.html#builtin-assume). /// -/// The more complicated the condition the less likely this is to be fruitful. -/// 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. +/// This promotes a correctness requirement to a soundness requirement. Don't do that without +/// very good reason. /// -/// 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's no -/// benefit to `let n = u32::count_ones(x); assert_unchecked(n <= u32::BITS);`. +/// # Usage /// -/// If ever you're tempted to write `assert_unchecked(false)`, then you're -/// actually looking for [`unreachable_unchecked()`]. +/// 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. /// -/// You may know this from other places -/// as [`llvm.assume`](https://llvm.org/docs/LangRef.html#llvm-assume-intrinsic) -/// or [`__builtin_assume`](https://clang.llvm.org/docs/LanguageExtensions.html#builtin-assume). +/// 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. /// -/// This promotes a correctness requirement to a soundness requirement. -/// Don't do that without very good reason. +/// 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's immediate UB to call this with `false`. +/// `cond` must be `true`. It is immediate UB to call this with `false`. +/// +/// # Example +/// +/// ``` +/// #![feature(hint_assert_unchecked)] +/// +/// 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: +/// +/// ```asm +/// 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: +/// +/// ```asm +/// 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 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. #[inline(always)] #[doc(alias = "assume")] #[track_caller]