This repository was archived by the owner on May 28, 2025. It is now read-only.
File tree Expand file tree Collapse file tree 4 files changed +56
-8
lines changed Expand file tree Collapse file tree 4 files changed +56
-8
lines changed Original file line number Diff line number Diff line change
1
+ use core:: ptr:: NonNull ;
1
2
use test:: black_box;
2
3
use test:: Bencher ;
3
4
@@ -162,3 +163,11 @@ fn fill_byte_sized(b: &mut Bencher) {
162
163
black_box ( slice. fill ( black_box ( NewType ( 42 ) ) ) ) ;
163
164
} ) ;
164
165
}
166
+
167
+ // Tests the ability of the compiler to recognize that only the last slice item is needed
168
+ // based on issue #106288
169
+ #[ bench]
170
+ fn fold_to_last ( b : & mut Bencher ) {
171
+ let slice: & [ i32 ] = & [ 0 ; 1024 ] ;
172
+ b. iter ( || black_box ( slice) . iter ( ) . fold ( None , |_, r| Some ( NonNull :: from ( r) ) ) ) ;
173
+ }
Original file line number Diff line number Diff line change @@ -191,6 +191,39 @@ macro_rules! iterator {
191
191
self . next_back( )
192
192
}
193
193
194
+ #[ inline]
195
+ fn fold<B , F >( self , init: B , mut f: F ) -> B
196
+ where
197
+ F : FnMut ( B , Self :: Item ) -> B ,
198
+ {
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
206
+ if is_empty!( self ) {
207
+ return init;
208
+ }
209
+ let mut acc = init;
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 ;
222
+ }
223
+ }
224
+ acc
225
+ }
226
+
194
227
// We override the default implementation, which uses `try_fold`,
195
228
// because this simple implementation generates less LLVM IR and is
196
229
// faster to compile.
Original file line number Diff line number Diff line change
1
+ // ignore-debug: the debug assertions get in the way
2
+ // compile-flags: -O
3
+ // min-llvm-version: 16
4
+ #![ crate_type = "lib" ]
5
+
6
+ // CHECK-LABEL: @slice_fold_to_last
7
+ #[ no_mangle]
8
+ pub fn slice_fold_to_last ( slice : & [ i32 ] ) -> Option < & i32 > {
9
+ // CHECK-NOT: loop
10
+ // CHECK-NOT: br
11
+ // CHECK-NOT: call
12
+ // CHECK: ret
13
+ slice. iter ( ) . fold ( None , |_, i| Some ( i) )
14
+ }
Original file line number Diff line number Diff line change @@ -37,14 +37,6 @@ pub fn issue71861(vec: Vec<u32>) -> Box<[u32]> {
37
37
// CHECK-LABEL: @issue75636
38
38
#[ no_mangle]
39
39
pub fn issue75636 < ' a > ( iter : & [ & ' a str ] ) -> Box < [ & ' a str ] > {
40
- // CHECK-NOT: panic
41
-
42
- // Call to panic_cannot_unwind in case of double-panic is expected,
43
- // on LLVM 16 and older, but other panics are not.
44
- // old: filter
45
- // old-NEXT: ; call core::panicking::panic_cannot_unwind
46
- // old-NEXT: panic_cannot_unwind
47
-
48
40
// CHECK-NOT: panic
49
41
iter. iter ( ) . copied ( ) . collect ( )
50
42
}
You can’t perform that action at this time.
0 commit comments