Skip to content

Commit e6f12c8

Browse files
committed
Auto merge of #136272 - Zalathar:rollup-6s577l5, r=Zalathar
Rollup of 5 pull requests Successful merges: - #135847 (optimize slice::ptr_rotate for small rotates) - #136215 (btree/node.rs: remove incorrect comment from pop_internal_level docs) - #136252 (spastorino back from vacations) - #136254 (Rustc dev guide subtree update) - #136259 (Cleanup docs for Allocator) r? `@ghost` `@rustbot` modify labels: rollup
2 parents 5e55679 + 4059a79 commit e6f12c8

File tree

15 files changed

+192
-123
lines changed

15 files changed

+192
-123
lines changed

library/alloc/src/collections/btree/node.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -600,8 +600,8 @@ impl<K, V> NodeRef<marker::Owned, K, V, marker::LeafOrInternal> {
600600
/// no cleanup is done on any of the keys, values and other children.
601601
/// This decreases the height by 1 and is the opposite of `push_internal_level`.
602602
///
603-
/// Requires exclusive access to the `NodeRef` object but not to the root node;
604-
/// it will not invalidate other handles or references to the root node.
603+
/// Does not invalidate any handles or references pointing into the subtree
604+
/// rooted at the first child of `self`.
605605
///
606606
/// Panics if there is no internal level, i.e., if the root node is a leaf.
607607
pub(super) fn pop_internal_level<A: Allocator + Clone>(&mut self, alloc: A) {

library/core/src/alloc/mod.rs

Lines changed: 27 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -49,26 +49,26 @@ impl fmt::Display for AllocError {
4949
/// An implementation of `Allocator` can allocate, grow, shrink, and deallocate arbitrary blocks of
5050
/// data described via [`Layout`][].
5151
///
52-
/// `Allocator` is designed to be implemented on ZSTs, references, or smart pointers because having
53-
/// an allocator like `MyAlloc([u8; N])` cannot be moved, without updating the pointers to the
52+
/// `Allocator` is designed to be implemented on ZSTs, references, or smart pointers.
53+
/// An allocator for `MyAlloc([u8; N])` cannot be moved, without updating the pointers to the
5454
/// allocated memory.
5555
///
56-
/// Unlike [`GlobalAlloc`][], zero-sized allocations are allowed in `Allocator`. If an underlying
57-
/// allocator does not support this (like jemalloc) or return a null pointer (such as
58-
/// `libc::malloc`), this must be caught by the implementation.
56+
/// In contrast to [`GlobalAlloc`][], `Allocator` allows zero-sized allocations. If an underlying
57+
/// allocator does not support this (like jemalloc) or responds by returning a null pointer
58+
/// (such as `libc::malloc`), this must be caught by the implementation.
5959
///
6060
/// ### Currently allocated memory
6161
///
62-
/// Some of the methods require that a memory block be *currently allocated* via an allocator. This
63-
/// means that:
62+
/// Some of the methods require that a memory block is *currently allocated* by an allocator.
63+
/// This means that:
64+
/// * the starting address for that memory block was previously
65+
/// returned by [`allocate`], [`grow`], or [`shrink`], and
66+
/// * the memory block has not subsequently been deallocated.
6467
///
65-
/// * the starting address for that memory block was previously returned by [`allocate`], [`grow`], or
66-
/// [`shrink`], and
67-
///
68-
/// * the memory block has not been subsequently deallocated, where blocks are either deallocated
69-
/// directly by being passed to [`deallocate`] or were changed by being passed to [`grow`] or
70-
/// [`shrink`] that returns `Ok`. If `grow` or `shrink` have returned `Err`, the passed pointer
71-
/// remains valid.
68+
/// A memory block is deallocated by a call to [`deallocate`],
69+
/// or by a call to [`grow`] or [`shrink`] that returns `Ok`.
70+
/// A call to `grow` or `shrink` that returns `Err`,
71+
/// does not deallocate the memory block passed to it.
7272
///
7373
/// [`allocate`]: Allocator::allocate
7474
/// [`grow`]: Allocator::grow
@@ -77,32 +77,28 @@ impl fmt::Display for AllocError {
7777
///
7878
/// ### Memory fitting
7979
///
80-
/// Some of the methods require that a layout *fit* a memory block. What it means for a layout to
81-
/// "fit" a memory block means (or equivalently, for a memory block to "fit" a layout) is that the
80+
/// Some of the methods require that a `layout` *fit* a memory block or vice versa. This means that the
8281
/// following conditions must hold:
83-
///
84-
/// * The block must be allocated with the same alignment as [`layout.align()`], and
85-
///
86-
/// * The provided [`layout.size()`] must fall in the range `min ..= max`, where:
87-
/// - `min` is the size of the layout most recently used to allocate the block, and
88-
/// - `max` is the latest actual size returned from [`allocate`], [`grow`], or [`shrink`].
82+
/// * the memory block must be *currently allocated* with alignment of [`layout.align()`], and
83+
/// * [`layout.size()`] must fall in the range `min ..= max`, where:
84+
/// - `min` is the size of the layout used to allocate the block, and
85+
/// - `max` is the actual size returned from [`allocate`], [`grow`], or [`shrink`].
8986
///
9087
/// [`layout.align()`]: Layout::align
9188
/// [`layout.size()`]: Layout::size
9289
///
9390
/// # Safety
9491
///
95-
/// * Memory blocks returned from an allocator that are [*currently allocated*] must point to
96-
/// valid memory and retain their validity while they are [*currently allocated*] and the shorter
97-
/// of:
98-
/// - the borrow-checker lifetime of the allocator type itself.
99-
/// - as long as at least one of the instance and all of its clones has not been dropped.
92+
/// Memory blocks that are [*currently allocated*] by an allocator,
93+
/// must point to valid memory, and retain their validity while until either:
94+
/// - the memory block is deallocated, or
95+
/// - the allocator is dropped.
10096
///
101-
/// * copying, cloning, or moving the allocator must not invalidate memory blocks returned from this
102-
/// allocator. A copied or cloned allocator must behave like the same allocator, and
97+
/// Copying, cloning, or moving the allocator must not invalidate memory blocks returned from it
98+
/// A copied or cloned allocator must behave like the original allocator.
10399
///
104-
/// * any pointer to a memory block which is [*currently allocated*] may be passed to any other
105-
/// method of the allocator.
100+
/// A memory block which is [*currently allocated*] may be passed to
101+
/// any method of the allocator that accepts such an argument.
106102
///
107103
/// [*currently allocated*]: #currently-allocated-memory
108104
#[unstable(feature = "allocator_api", issue = "32838")]

library/core/src/slice/rotate.rs

Lines changed: 109 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,91 @@
11
use crate::mem::{self, MaybeUninit, SizedTypeProperties};
22
use crate::{cmp, ptr};
33

4+
type BufType = [usize; 32];
5+
46
/// Rotates the range `[mid-left, mid+right)` such that the element at `mid` becomes the first
57
/// element. Equivalently, rotates the range `left` elements to the left or `right` elements to the
68
/// right.
79
///
810
/// # Safety
911
///
1012
/// The specified range must be valid for reading and writing.
13+
#[inline]
14+
pub(super) unsafe fn ptr_rotate<T>(left: usize, mid: *mut T, right: usize) {
15+
if T::IS_ZST {
16+
return;
17+
}
18+
// abort early if the rotate is a no-op
19+
if (left == 0) || (right == 0) {
20+
return;
21+
}
22+
// `T` is not a zero-sized type, so it's okay to divide by its size.
23+
if !cfg!(feature = "optimize_for_size")
24+
&& cmp::min(left, right) <= mem::size_of::<BufType>() / mem::size_of::<T>()
25+
{
26+
// SAFETY: guaranteed by the caller
27+
unsafe { ptr_rotate_memmove(left, mid, right) };
28+
} else if !cfg!(feature = "optimize_for_size")
29+
&& ((left + right < 24) || (mem::size_of::<T>() > mem::size_of::<[usize; 4]>()))
30+
{
31+
// SAFETY: guaranteed by the caller
32+
unsafe { ptr_rotate_gcd(left, mid, right) }
33+
} else {
34+
// SAFETY: guaranteed by the caller
35+
unsafe { ptr_rotate_swap(left, mid, right) }
36+
}
37+
}
38+
39+
/// Algorithm 1 is used if `min(left, right)` is small enough to fit onto a stack buffer. The
40+
/// `min(left, right)` elements are copied onto the buffer, `memmove` is applied to the others, and
41+
/// the ones on the buffer are moved back into the hole on the opposite side of where they
42+
/// originated.
1143
///
12-
/// # Algorithm
44+
/// # Safety
1345
///
14-
/// Algorithm 1 is used for small values of `left + right` or for large `T`. The elements are moved
15-
/// into their final positions one at a time starting at `mid - left` and advancing by `right` steps
16-
/// modulo `left + right`, such that only one temporary is needed. Eventually, we arrive back at
17-
/// `mid - left`. However, if `gcd(left + right, right)` is not 1, the above steps skipped over
18-
/// elements. For example:
46+
/// The specified range must be valid for reading and writing.
47+
#[inline]
48+
unsafe fn ptr_rotate_memmove<T>(left: usize, mid: *mut T, right: usize) {
49+
// The `[T; 0]` here is to ensure this is appropriately aligned for T
50+
let mut rawarray = MaybeUninit::<(BufType, [T; 0])>::uninit();
51+
let buf = rawarray.as_mut_ptr() as *mut T;
52+
// SAFETY: `mid-left <= mid-left+right < mid+right`
53+
let dim = unsafe { mid.sub(left).add(right) };
54+
if left <= right {
55+
// SAFETY:
56+
//
57+
// 1) The `if` condition about the sizes ensures `[mid-left; left]` will fit in
58+
// `buf` without overflow and `buf` was created just above and so cannot be
59+
// overlapped with any value of `[mid-left; left]`
60+
// 2) [mid-left, mid+right) are all valid for reading and writing and we don't care
61+
// about overlaps here.
62+
// 3) The `if` condition about `left <= right` ensures writing `left` elements to
63+
// `dim = mid-left+right` is valid because:
64+
// - `buf` is valid and `left` elements were written in it in 1)
65+
// - `dim+left = mid-left+right+left = mid+right` and we write `[dim, dim+left)`
66+
unsafe {
67+
// 1)
68+
ptr::copy_nonoverlapping(mid.sub(left), buf, left);
69+
// 2)
70+
ptr::copy(mid, mid.sub(left), right);
71+
// 3)
72+
ptr::copy_nonoverlapping(buf, dim, left);
73+
}
74+
} else {
75+
// SAFETY: same reasoning as above but with `left` and `right` reversed
76+
unsafe {
77+
ptr::copy_nonoverlapping(mid, buf, right);
78+
ptr::copy(mid.sub(left), dim, left);
79+
ptr::copy_nonoverlapping(buf, mid.sub(left), right);
80+
}
81+
}
82+
}
83+
84+
/// Algorithm 2 is used for small values of `left + right` or for large `T`. The elements
85+
/// are moved into their final positions one at a time starting at `mid - left` and advancing by
86+
/// `right` steps modulo `left + right`, such that only one temporary is needed. Eventually, we
87+
/// arrive back at `mid - left`. However, if `gcd(left + right, right)` is not 1, the above steps
88+
/// skipped over elements. For example:
1989
/// ```text
2090
/// left = 10, right = 6
2191
/// the `^` indicates an element in its final place
@@ -39,41 +109,16 @@ use crate::{cmp, ptr};
39109
/// `gcd(left + right, right)` value). The end result is that all elements are finalized once and
40110
/// only once.
41111
///
42-
/// Algorithm 2 is used if `left + right` is large but `min(left, right)` is small enough to
43-
/// fit onto a stack buffer. The `min(left, right)` elements are copied onto the buffer, `memmove`
44-
/// is applied to the others, and the ones on the buffer are moved back into the hole on the
45-
/// opposite side of where they originated.
46-
///
47-
/// Algorithms that can be vectorized outperform the above once `left + right` becomes large enough.
48-
/// Algorithm 1 can be vectorized by chunking and performing many rounds at once, but there are too
112+
/// Algorithm 2 can be vectorized by chunking and performing many rounds at once, but there are too
49113
/// few rounds on average until `left + right` is enormous, and the worst case of a single
50-
/// round is always there. Instead, algorithm 3 utilizes repeated swapping of
51-
/// `min(left, right)` elements until a smaller rotate problem is left.
114+
/// round is always there.
52115
///
53-
/// ```text
54-
/// left = 11, right = 4
55-
/// [4 5 6 7 8 9 10 11 12 13 14 . 0 1 2 3]
56-
/// ^ ^ ^ ^ ^ ^ ^ ^ swapping the right most elements with elements to the left
57-
/// [4 5 6 7 8 9 10 . 0 1 2 3] 11 12 13 14
58-
/// ^ ^ ^ ^ ^ ^ ^ ^ swapping these
59-
/// [4 5 6 . 0 1 2 3] 7 8 9 10 11 12 13 14
60-
/// we cannot swap any more, but a smaller rotation problem is left to solve
61-
/// ```
62-
/// when `left < right` the swapping happens from the left instead.
63-
pub(super) unsafe fn ptr_rotate<T>(mut left: usize, mut mid: *mut T, mut right: usize) {
64-
type BufType = [usize; 32];
65-
if T::IS_ZST {
66-
return;
67-
}
68-
loop {
69-
// N.B. the below algorithms can fail if these cases are not checked
70-
if (right == 0) || (left == 0) {
71-
return;
72-
}
73-
if !cfg!(feature = "optimize_for_size")
74-
&& ((left + right < 24) || (mem::size_of::<T>() > mem::size_of::<[usize; 4]>()))
75-
{
76-
// Algorithm 1
116+
/// # Safety
117+
///
118+
/// The specified range must be valid for reading and writing.
119+
#[inline]
120+
unsafe fn ptr_rotate_gcd<T>(left: usize, mid: *mut T, right: usize) {
121+
// Algorithm 2
77122
// Microbenchmarks indicate that the average performance for random shifts is better all
78123
// the way until about `left + right == 32`, but the worst case performance breaks even
79124
// around 16. 24 was chosen as middle ground. If the size of `T` is larger than 4
@@ -157,47 +202,29 @@ pub(super) unsafe fn ptr_rotate<T>(mut left: usize, mut mid: *mut T, mut right:
157202
}
158203
}
159204
}
160-
return;
161-
// `T` is not a zero-sized type, so it's okay to divide by its size.
162-
} else if !cfg!(feature = "optimize_for_size")
163-
&& cmp::min(left, right) <= mem::size_of::<BufType>() / mem::size_of::<T>()
164-
{
165-
// Algorithm 2
166-
// The `[T; 0]` here is to ensure this is appropriately aligned for T
167-
let mut rawarray = MaybeUninit::<(BufType, [T; 0])>::uninit();
168-
let buf = rawarray.as_mut_ptr() as *mut T;
169-
// SAFETY: `mid-left <= mid-left+right < mid+right`
170-
let dim = unsafe { mid.sub(left).add(right) };
171-
if left <= right {
172-
// SAFETY:
173-
//
174-
// 1) The `else if` condition about the sizes ensures `[mid-left; left]` will fit in
175-
// `buf` without overflow and `buf` was created just above and so cannot be
176-
// overlapped with any value of `[mid-left; left]`
177-
// 2) [mid-left, mid+right) are all valid for reading and writing and we don't care
178-
// about overlaps here.
179-
// 3) The `if` condition about `left <= right` ensures writing `left` elements to
180-
// `dim = mid-left+right` is valid because:
181-
// - `buf` is valid and `left` elements were written in it in 1)
182-
// - `dim+left = mid-left+right+left = mid+right` and we write `[dim, dim+left)`
183-
unsafe {
184-
// 1)
185-
ptr::copy_nonoverlapping(mid.sub(left), buf, left);
186-
// 2)
187-
ptr::copy(mid, mid.sub(left), right);
188-
// 3)
189-
ptr::copy_nonoverlapping(buf, dim, left);
190205
}
191-
} else {
192-
// SAFETY: same reasoning as above but with `left` and `right` reversed
193-
unsafe {
194-
ptr::copy_nonoverlapping(mid, buf, right);
195-
ptr::copy(mid.sub(left), dim, left);
196-
ptr::copy_nonoverlapping(buf, mid.sub(left), right);
197-
}
198-
}
199-
return;
200-
} else if left >= right {
206+
207+
/// Algorithm 3 utilizes repeated swapping of `min(left, right)` elements.
208+
///
209+
/// ///
210+
/// ```text
211+
/// left = 11, right = 4
212+
/// [4 5 6 7 8 9 10 11 12 13 14 . 0 1 2 3]
213+
/// ^ ^ ^ ^ ^ ^ ^ ^ swapping the right most elements with elements to the left
214+
/// [4 5 6 7 8 9 10 . 0 1 2 3] 11 12 13 14
215+
/// ^ ^ ^ ^ ^ ^ ^ ^ swapping these
216+
/// [4 5 6 . 0 1 2 3] 7 8 9 10 11 12 13 14
217+
/// we cannot swap any more, but a smaller rotation problem is left to solve
218+
/// ```
219+
/// when `left < right` the swapping happens from the left instead.
220+
///
221+
/// # Safety
222+
///
223+
/// The specified range must be valid for reading and writing.
224+
#[inline]
225+
unsafe fn ptr_rotate_swap<T>(mut left: usize, mut mid: *mut T, mut right: usize) {
226+
loop {
227+
if left >= right {
201228
// Algorithm 3
202229
// There is an alternate way of swapping that involves finding where the last swap
203230
// of this algorithm would be, and swapping using that last chunk instead of swapping
@@ -233,5 +260,8 @@ pub(super) unsafe fn ptr_rotate<T>(mut left: usize, mut mid: *mut T, mut right:
233260
}
234261
}
235262
}
263+
if (right == 0) || (left == 0) {
264+
return;
265+
}
236266
}
237267
}

src/doc/rustc-dev-guide/.github/workflows/ci.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@ jobs:
4141
uses: actions/cache/restore@v4
4242
with:
4343
path: book/linkcheck/cache.json
44-
key: linkcheck--${{ env.MDBOOK_LINKCHECK2_VERSION }}
44+
key: linkcheck--${{ env.MDBOOK_LINKCHECK2_VERSION }}--${{ github.run_id }}
45+
restore-keys: |
46+
linkcheck--${{ env.MDBOOK_LINKCHECK2_VERSION }}--
4547
4648
- name: Install latest nightly Rust toolchain
4749
if: steps.mdbook-cache.outputs.cache-hit != 'true'
@@ -66,7 +68,7 @@ jobs:
6668
uses: actions/cache/save@v4
6769
with:
6870
path: book/linkcheck/cache.json
69-
key: linkcheck--${{ env.MDBOOK_LINKCHECK2_VERSION }}
71+
key: linkcheck--${{ env.MDBOOK_LINKCHECK2_VERSION }}--${{ github.run_id }}
7072

7173
- name: Deploy to gh-pages
7274
if: github.event_name == 'push'

src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,10 @@ jobs:
5050
RESULT=`gh pr list --author github-actions[bot] --state open -q 'map(select(.title=="Rustc pull update")) | length' --json title`
5151
if [[ "$RESULT" -eq 0 ]]; then
5252
echo "Creating new pull request"
53-
PR_URL=gh pr create -B master --title 'Rustc pull update' --body 'Latest update from rustc.'
53+
PR_URL=`gh pr create -B master --title 'Rustc pull update' --body 'Latest update from rustc.'`
5454
echo "pr_url=$PR_URL" >> $GITHUB_OUTPUT
5555
else
56-
PR_URL=gh pr list --author github-actions[bot] --state open -q 'map(select(.title=="Rustc pull update")) | .[0].url' --json url,title
56+
PR_URL=`gh pr list --author github-actions[bot] --state open -q 'map(select(.title=="Rustc pull update")) | .[0].url' --json url,title`
5757
echo "pr_url=$PR_URL" >> $GITHUB_OUTPUT
5858
fi
5959
env:

src/doc/rustc-dev-guide/rust-version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
ecda83b30f0f68cf5692855dddc0bc38ee8863fc
1+
66d6064f9eb888018775e08f84747ee6f39ba28e

src/doc/rustc-dev-guide/src/about-this-guide.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@ You might also find the following sites useful:
7272
- The [Rust reference][rr], even though it doesn't specifically talk about
7373
Rust's internals, is a great resource nonetheless
7474
- Although out of date, [Tom Lee's great blog article][tlgba] is very helpful
75-
- [rustaceans.org][ro] is helpful, but mostly dedicated to IRC
7675
- The [Rust Compiler Testing Docs][rctd]
7776
- For [@bors], [this cheat sheet][cheatsheet] is helpful
7877
- Google is always helpful when programming.

src/doc/rustc-dev-guide/src/backend/libs-and-metadata.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ format is specific to `rustc`, and may change over time. This file contains:
4242
### dylib
4343

4444
A `dylib` is a platform-specific shared library. It includes the `rustc`
45-
[metadata] in a special link section called `.rustc` in a compressed format.
45+
[metadata] in a special link section called `.rustc`.
4646

4747
### rmeta
4848

src/doc/rustc-dev-guide/src/diagnostics.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -602,7 +602,7 @@ as the linter walks the AST. You can then choose to emit lints in a
602602
very similar way to compile errors.
603603

604604
You also declare the metadata of a particular lint via the `declare_lint!`
605-
macro. This includes the name, the default level, a short description, and some
605+
macro. [This macro](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint_defs/macro.declare_lint.html) includes the name, the default level, a short description, and some
606606
more details.
607607

608608
Note that the lint and the lint pass must be registered with the compiler.

src/doc/rustc-dev-guide/src/early_late_parameters.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ If the lifetime parameter on `foo` was late bound this would be able to compile
128128
```rust
129129
# fn foo<'a: 'a>(b: &'a String) -> &'a String { b }
130130
# fn bar<'a>(b: &'a String) -> &'a String { b }
131-
131+
#
132132
// Early bound parameters are instantiated here, however as `'a` is
133133
// late bound it is not provided here.
134134
let b = bar;

0 commit comments

Comments
 (0)