Skip to content

Commit 708e9bf

Browse files
committed
Implement a specialized version std::iter::Enumerate for TrustedLen
This allows to hint at the compiler via `intrinsics::assume()` that the returned index is smaller than the length of the underlying iterator and allows the compiler to optimize code better based on that. Fixes rust-lang#75935
1 parent 7ce1b3b commit 708e9bf

File tree

1 file changed

+146
-16
lines changed

1 file changed

+146
-16
lines changed

library/core/src/iter/adapters/enumerate.rs

Lines changed: 146 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::intrinsics;
12
use crate::iter::adapters::{zip::try_get_unchecked, SourceIter, TrustedRandomAccess};
23
use crate::iter::{FusedIterator, InPlaceIterable, TrustedLen};
34
use crate::ops::{Add, AddAssign, Try};
@@ -15,10 +16,149 @@ use crate::ops::{Add, AddAssign, Try};
1516
pub struct Enumerate<I> {
1617
iter: I,
1718
count: usize,
19+
len: usize,
1820
}
19-
impl<I> Enumerate<I> {
21+
impl<I: Iterator> Enumerate<I> {
2022
pub(in crate::iter) fn new(iter: I) -> Enumerate<I> {
21-
Enumerate { iter, count: 0 }
23+
EnumerateImpl::new(iter)
24+
}
25+
}
26+
27+
/// Enumerate specialization trait
28+
///
29+
/// This exists to work around https://bugs.llvm.org/show_bug.cgi?id=48965. It can be removed again
30+
/// once this is solved in LLVM and the implementation of the trait functions can be folded again
31+
/// into the corresponding functions on `Enumerate` based on the default implementation.
32+
///
33+
/// The trait is implemented via specialization on any iterator that implements `TrustedRandomAccess`
34+
/// to provide the information about the maximum value this iterator can return to the optimizer.
35+
/// Specifically, for slices this allows the optimizer to know that the returned values are never
36+
/// bigger than the size of the slice.
37+
///
38+
/// The only difference between the default and specialized implementation is the use of
39+
/// `intrinsics::assume()` on the to be returned values, and both implementations must be kept in
40+
/// sync.
41+
#[doc(hidden)]
42+
trait EnumerateImpl<I> {
43+
type Item;
44+
fn new(iter: I) -> Self;
45+
fn next(&mut self) -> Option<Self::Item>;
46+
unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item
47+
where
48+
Self: TrustedRandomAccess;
49+
fn next_back(&mut self) -> Option<Self::Item>
50+
where
51+
I: ExactSizeIterator + DoubleEndedIterator;
52+
}
53+
54+
impl<I> EnumerateImpl<I> for Enumerate<I>
55+
where
56+
I: Iterator,
57+
{
58+
type Item = (usize, I::Item);
59+
60+
default fn new(iter: I) -> Self {
61+
Enumerate {
62+
iter,
63+
count: 0,
64+
len: 0, // unused
65+
}
66+
}
67+
68+
#[inline]
69+
default fn next(&mut self) -> Option<Self::Item> {
70+
let a = self.iter.next()?;
71+
let i = self.count;
72+
// Possible undefined overflow.
73+
AddAssign::add_assign(&mut self.count, 1);
74+
Some((i, a))
75+
}
76+
77+
#[inline]
78+
default unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item
79+
where
80+
Self: TrustedRandomAccess,
81+
{
82+
// SAFETY: the caller must uphold the contract for
83+
// `Iterator::__iterator_get_unchecked`.
84+
let value = unsafe { try_get_unchecked(&mut self.iter, idx) };
85+
(Add::add(self.count, idx), value)
86+
}
87+
88+
#[inline]
89+
default fn next_back(&mut self) -> Option<Self::Item>
90+
where
91+
I: ExactSizeIterator + DoubleEndedIterator,
92+
{
93+
let a = self.iter.next_back()?;
94+
let len = self.iter.len();
95+
// Can safely add, `ExactSizeIterator` promises that the number of
96+
// elements fits into a `usize`.
97+
Some((self.count + len, a))
98+
}
99+
}
100+
101+
// This is the same code as above but using `intrinsics::assume()` to hint at the compiler
102+
// that the returned index is smaller than the length of the underlying iterator.
103+
//
104+
// This could be bound to `TrustedLen + ExactSizeIterator` or `TrustedRandomAccess` to guarantee
105+
// that the number of elements fits into an `usize` and that the returned length is actually the
106+
// real length. `TrustedRandomAccess` was selected because specialization on `ExactSizeIterator` is
107+
// not possible (yet?).
108+
impl<I> EnumerateImpl<I> for Enumerate<I>
109+
where
110+
I: TrustedRandomAccess + Iterator,
111+
{
112+
fn new(iter: I) -> Self {
113+
let len = iter.size();
114+
115+
Enumerate { iter, count: 0, len }
116+
}
117+
118+
#[inline]
119+
fn next(&mut self) -> Option<Self::Item> {
120+
let a = self.iter.next()?;
121+
// SAFETY: There must be fewer than `self.len` items because of `TrustedLen`'s API contract
122+
unsafe {
123+
intrinsics::assume(self.count < self.len);
124+
}
125+
let i = self.count;
126+
// Possible undefined overflow.
127+
AddAssign::add_assign(&mut self.count, 1);
128+
Some((i, a))
129+
}
130+
131+
#[inline]
132+
unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item
133+
where
134+
Self: TrustedRandomAccess,
135+
{
136+
// SAFETY: the caller must uphold the contract for
137+
// `Iterator::__iterator_get_unchecked`.
138+
let value = unsafe { try_get_unchecked(&mut self.iter, idx) };
139+
let idx = Add::add(self.count, idx);
140+
// SAFETY: There must be fewer than `self.len` items because of `TrustedLen`'s API contract
141+
unsafe {
142+
intrinsics::assume(idx < self.len);
143+
}
144+
(idx, value)
145+
}
146+
147+
#[inline]
148+
fn next_back(&mut self) -> Option<Self::Item>
149+
where
150+
I: ExactSizeIterator + DoubleEndedIterator,
151+
{
152+
let a = self.iter.next_back()?;
153+
let len = self.iter.len();
154+
// Can safely add, `ExactSizeIterator` promises that the number of
155+
// elements fits into a `usize`.
156+
let idx = self.count + len;
157+
// SAFETY: There must be fewer than `self.len` items because of `TrustedLen`'s API contract
158+
unsafe {
159+
intrinsics::assume(idx < self.len);
160+
}
161+
Some((idx, a))
22162
}
23163
}
24164

@@ -40,11 +180,7 @@ where
40180
/// Might panic if the index of the element overflows a `usize`.
41181
#[inline]
42182
fn next(&mut self) -> Option<(usize, <I as Iterator>::Item)> {
43-
let a = self.iter.next()?;
44-
let i = self.count;
45-
// Possible undefined overflow.
46-
AddAssign::add_assign(&mut self.count, 1);
47-
Some((i, a))
183+
EnumerateImpl::next(self)
48184
}
49185

50186
#[inline]
@@ -114,10 +250,8 @@ where
114250
where
115251
Self: TrustedRandomAccess,
116252
{
117-
// SAFETY: the caller must uphold the contract for
118-
// `Iterator::__iterator_get_unchecked`.
119-
let value = unsafe { try_get_unchecked(&mut self.iter, idx) };
120-
(Add::add(self.count, idx), value)
253+
// SAFETY: Just forwarding to the actual implementation.
254+
unsafe { EnumerateImpl::__iterator_get_unchecked(self, idx) }
121255
}
122256
}
123257

@@ -128,11 +262,7 @@ where
128262
{
129263
#[inline]
130264
fn next_back(&mut self) -> Option<(usize, <I as Iterator>::Item)> {
131-
let a = self.iter.next_back()?;
132-
let len = self.iter.len();
133-
// Can safely add, `ExactSizeIterator` promises that the number of
134-
// elements fits into a `usize`.
135-
Some((self.count + len, a))
265+
EnumerateImpl::next_back(self)
136266
}
137267

138268
#[inline]

0 commit comments

Comments
 (0)