@@ -192,23 +192,33 @@ macro_rules! iterator {
192
192
}
193
193
194
194
#[ inline]
195
- fn fold<B , F >( mut self , init: B , mut f: F ) -> B
195
+ fn fold<B , F >( self , init: B , mut f: F ) -> B
196
196
where
197
197
F : FnMut ( B , Self :: Item ) -> B ,
198
198
{
199
- // Handling the 0-len case explicitly and then using a do-while style loop
200
- // helps the optimizer. See issue #106288
199
+ // this implementation consists of the following optimizations compared to the
200
+ // default implementation:
201
+ // - do-while loop, as is llvm's preferred loop shape,
202
+ // see https://releases.llvm.org/16.0.0/docs/LoopTerminology.html#more-canonical-loops
203
+ // - bumps an index instead of a pointer since the latter case inhibits
204
+ // some optimizations, see #111603
205
+ // - avoids Option wrapping/matching
201
206
if is_empty!( self ) {
202
207
return init;
203
208
}
204
209
let mut acc = init;
205
- // SAFETY: The 0-len case was handled above so one loop iteration is guaranteed.
206
- unsafe {
207
- loop {
208
- acc = f( acc, next_unchecked!( self ) ) ;
209
- if is_empty!( self ) {
210
- break ;
211
- }
210
+ let mut i = 0 ;
211
+ let len = len!( self ) ;
212
+ loop {
213
+ // SAFETY: the loop iterates `i in 0..len`, which always is in bounds of
214
+ // the slice allocation
215
+ acc = f( acc, unsafe { & $( $mut_ ) ? * self . ptr. add( i) . as_ptr( ) } ) ;
216
+ // SAFETY: `i` can't overflow since it'll only reach usize::MAX if the
217
+ // slice had that length, in which case we'll break out of the loop
218
+ // after the increment
219
+ i = unsafe { i. unchecked_add( 1 ) } ;
220
+ if i == len {
221
+ break ;
212
222
}
213
223
}
214
224
acc
0 commit comments