Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 9d0041c

Browse files
committedJun 19, 2024
Update documentation for hint::assert_unchecked
Rearrange the sections and add an example to `core::hint::assert_unchecked`.
1 parent dd7c901 commit 9d0041c

File tree

1 file changed

+73
-20
lines changed

1 file changed

+73
-20
lines changed
 

‎core/src/hint.rs

+73-20
Original file line numberDiff line numberDiff line change
@@ -111,36 +111,89 @@ pub const unsafe fn unreachable_unchecked() -> ! {
111111

112112
/// Makes a *soundness* promise to the compiler that `cond` holds.
113113
///
114-
/// This may allow the optimizer to simplify things,
115-
/// but it might also make the generated code slower.
116-
/// Either way, calling it will most likely make compilation take longer.
114+
/// This may allow the optimizer to simplify things, but it might also make the generated code
115+
/// slower. Either way, calling it will most likely make compilation take longer.
117116
///
118-
/// This is a situational tool for micro-optimization, and is allowed to do nothing.
119-
/// Any use should come with a repeatable benchmark to show the value
120-
/// and allow removing it later should the optimizer get smarter and no longer need it.
117+
/// You may know this from other places as
118+
/// [`llvm.assume`](https://llvm.org/docs/LangRef.html#llvm-assume-intrinsic) or, in C,
119+
/// [`__builtin_assume`](https://clang.llvm.org/docs/LanguageExtensions.html#builtin-assume).
121120
///
122-
/// The more complicated the condition the less likely this is to be fruitful.
123-
/// For example, `assert_unchecked(foo.is_sorted())` is a complex enough value
124-
/// that the compiler is unlikely to be able to take advantage of it.
121+
/// This promotes a correctness requirement to a soundness requirement. Don't do that without
122+
/// very good reason.
125123
///
126-
/// There's also no need to `assert_unchecked` basic properties of things. For
127-
/// example, the compiler already knows the range of `count_ones`, so there's no
128-
/// benefit to `let n = u32::count_ones(x); assert_unchecked(n <= u32::BITS);`.
124+
/// # Usage
129125
///
130-
/// If ever you're tempted to write `assert_unchecked(false)`, then you're
131-
/// actually looking for [`unreachable_unchecked()`].
126+
/// This is a situational tool for micro-optimization, and is allowed to do nothing. Any use
127+
/// should come with a repeatable benchmark to show the value, with the expectation to drop it
128+
/// later should the optimizer get smarter and no longer need it.
132129
///
133-
/// You may know this from other places
134-
/// as [`llvm.assume`](https://llvm.org/docs/LangRef.html#llvm-assume-intrinsic)
135-
/// or [`__builtin_assume`](https://clang.llvm.org/docs/LanguageExtensions.html#builtin-assume).
130+
/// The more complicated the condition, the less likely this is to be useful. For example,
131+
/// `assert_unchecked(foo.is_sorted())` is a complex enough value that the compiler is unlikely
132+
/// to be able to take advantage of it.
136133
///
137-
/// This promotes a correctness requirement to a soundness requirement.
138-
/// Don't do that without very good reason.
134+
/// There's also no need to `assert_unchecked` basic properties of things. For example, the
135+
/// compiler already knows the range of `count_ones`, so there is no benefit to
136+
/// `let n = u32::count_ones(x); assert_unchecked(n <= u32::BITS);`.
137+
///
138+
/// `assert_unchecked` is logically equivalent to `if !cond { unreachable_unchecked(); }`. If
139+
/// ever you are tempted to write `assert_unchecked(false)`, you should instead use
140+
/// [`unreachable_unchecked()`] directly.
139141
///
140142
/// # Safety
141143
///
142-
/// `cond` must be `true`. It's immediate UB to call this with `false`.
144+
/// `cond` must be `true`. It is immediate UB to call this with `false`.
145+
///
146+
/// # Example
147+
///
148+
/// ```
149+
/// #![feature(hint_assert_unchecked)]
150+
///
151+
/// use core::hint;
152+
///
153+
/// /// # Safety
154+
/// ///
155+
/// /// `p` must be nonnull and valid
156+
/// pub unsafe fn next_value(p: *const i32) -> i32 {
157+
/// // SAFETY: caller invariants guarantee that `p` is not null
158+
/// unsafe { hint::assert_unchecked(!p.is_null()) }
159+
///
160+
/// if p.is_null() {
161+
/// return -1;
162+
/// } else {
163+
/// // SAFETY: caller invariants guarantee that `p` is valid
164+
/// unsafe { *p + 1 }
165+
/// }
166+
/// }
167+
/// ```
168+
///
169+
/// Without the `assert_unchecked`, the above function produces the following with optimizations
170+
/// enabled:
171+
///
172+
/// ```asm
173+
/// next_value:
174+
/// test rdi, rdi
175+
/// je .LBB0_1
176+
/// mov eax, dword ptr [rdi]
177+
/// inc eax
178+
/// ret
179+
/// .LBB0_1:
180+
/// mov eax, -1
181+
/// ret
182+
/// ```
183+
///
184+
/// Adding the assertion allows the optimizer to remove the extra check:
185+
///
186+
/// ```asm
187+
/// next_value:
188+
/// mov eax, dword ptr [rdi]
189+
/// inc eax
190+
/// ret
191+
/// ```
143192
///
193+
/// This example is quite unlike anything that would be used in the real world: it is redundant
194+
/// to put an an assertion right next to code that checks the same thing, and dereferencing a
195+
/// pointer already has the builtin assumption that it is nonnull. However, it illustrates the
196+
/// kind of changes the optimizer can make even when the behavior is less obviously related.
144197
#[inline(always)]
145198
#[doc(alias = "assume")]
146199
#[track_caller]

0 commit comments

Comments
 (0)
Failed to load comments.