about summary refs log tree commit diff
diff options
context:
space:
mode:
authorThe 8472 <git@infinite-source.de>2023-06-12 14:06:24 +0200
committerThe 8472 <git@infinite-source.de>2023-06-14 22:22:41 +0200
commitd90508f76130a8a9aaa68521ac1b0e35ea9a236e (patch)
treedd0db39597d6d6be0ca556c02c9ffd6a849bc995
parentba5b2f0b4b71662663a224f685b2b808d9c8f99a (diff)
downloadrust-d90508f76130a8a9aaa68521ac1b0e35ea9a236e.tar.gz
rust-d90508f76130a8a9aaa68521ac1b0e35ea9a236e.zip
use indexed loop instead of ptr bumping
this seems to produce less IR
-rw-r--r--library/core/src/slice/iter/macros.rs30
-rw-r--r--tests/codegen/vec-shrink-panik.rs8
2 files changed, 20 insertions, 18 deletions
diff --git a/library/core/src/slice/iter/macros.rs b/library/core/src/slice/iter/macros.rs
index dd809ca459d..96a145e22ed 100644
--- a/library/core/src/slice/iter/macros.rs
+++ b/library/core/src/slice/iter/macros.rs
@@ -192,23 +192,33 @@ macro_rules! iterator {
             }
 
             #[inline]
-            fn fold<B, F>(mut self, init: B, mut f: F) -> B
+            fn fold<B, F>(self, init: B, mut f: F) -> B
                 where
                     F: FnMut(B, Self::Item) -> B,
             {
-                // Handling the 0-len case explicitly and then using a do-while style loop
-                // helps the optimizer. See issue #106288
+                // this implementation consists of the following optimizations compared to the
+                // default implementation:
+                // - do-while loop, as is llvm's preferred loop shape,
+                //   see https://releases.llvm.org/16.0.0/docs/LoopTerminology.html#more-canonical-loops
+                // - bumps an index instead of a pointer since the latter case inhibits
+                //   some optimizations, see #111603
+                // - avoids Option wrapping/matching
                 if is_empty!(self) {
                     return init;
                 }
                 let mut acc = init;
-                // SAFETY: The 0-len case was handled above so one loop iteration is guaranteed.
-                unsafe {
-                    loop {
-                        acc = f(acc, next_unchecked!(self));
-                        if is_empty!(self) {
-                            break;
-                        }
+                let mut i = 0;
+                let len = len!(self);
+                loop {
+                    // SAFETY: the loop iterates `i in 0..len`, which always is in bounds of
+                    // the slice allocation
+                    acc = f(acc, unsafe { & $( $mut_ )? *self.ptr.add(i).as_ptr() });
+                    // SAFETY: `i` can't overflow since it'll only reach usize::MAX if the
+                    // slice had that length, in which case we'll break out of the loop
+                    // after the increment
+                    i = unsafe { i.unchecked_add(1) };
+                    if i == len {
+                        break;
                     }
                 }
                 acc
diff --git a/tests/codegen/vec-shrink-panik.rs b/tests/codegen/vec-shrink-panik.rs
index 606d68ff3ab..14fef4e2cd5 100644
--- a/tests/codegen/vec-shrink-panik.rs
+++ b/tests/codegen/vec-shrink-panik.rs
@@ -38,14 +38,6 @@ pub fn issue71861(vec: Vec<u32>) -> Box<[u32]> {
 #[no_mangle]
 pub fn issue75636<'a>(iter: &[&'a str]) -> Box<[&'a str]> {
     // CHECK-NOT: panic
-
-    // Call to panic_cannot_unwind in case of double-panic is expected,
-    // on LLVM 16 and older, but other panics are not.
-    // old: filter
-    // old-NEXT: ; call core::panicking::panic_cannot_unwind
-    // old-NEXT: panic_cannot_unwind
-
-    // CHECK-NOT: panic
     iter.iter().copied().collect()
 }