Skip to content

Commit 2d3d0b7

Browse files
authored
Rollup merge of rust-lang#61885 - scottmcm:slice-iter-len-opt, r=rkruppe,RalfJung
Help LLVM better optimize slice::Iter(Mut)::len r? @RalfJung I've included a codegen test that fails without this change as a demonstration of usefulness.
2 parents 0800b6e + af0e35e commit 2d3d0b7

File tree

3 files changed

+37
-4
lines changed

3 files changed

+37
-4
lines changed

Diff for: src/libcore/intrinsics.rs

+6
Original file line numberDiff line numberDiff line change
@@ -1607,3 +1607,9 @@ pub fn maxnumf64(x: f64, y: f64) -> f64 {
16071607
// Identical to the `f32` case.
16081608
(if x < y || x != x { y } else { x }) * 1.0
16091609
}
1610+
1611+
/// For bootstrapping, implement unchecked_sub as just wrapping_sub.
1612+
#[cfg(bootstrap)]
1613+
pub unsafe fn unchecked_sub<T>(x: T, y: T) -> T {
1614+
sub_with_overflow(x, y).0
1615+
}

Diff for: src/libcore/slice/mod.rs

+17-4
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
use crate::cmp::Ordering::{self, Less, Equal, Greater};
2626
use crate::cmp;
2727
use crate::fmt;
28-
use crate::intrinsics::assume;
28+
use crate::intrinsics::{assume, exact_div, unchecked_sub};
2929
use crate::isize;
3030
use crate::iter::*;
3131
use crate::ops::{FnMut, Try, self};
@@ -2998,14 +2998,27 @@ macro_rules! is_empty {
29982998
// unexpected way. (Tested by `codegen/slice-position-bounds-check`.)
29992999
macro_rules! len {
30003000
($self: ident) => {{
3001+
#![allow(unused_unsafe)] // we're sometimes used within an unsafe block
3002+
30013003
let start = $self.ptr;
3002-
let diff = ($self.end as usize).wrapping_sub(start as usize);
30033004
let size = size_from_ptr(start);
30043005
if size == 0 {
3006+
// This _cannot_ use `unchecked_sub` because we depend on wrapping
3007+
// to represent the length of long ZST slice iterators.
3008+
let diff = ($self.end as usize).wrapping_sub(start as usize);
30053009
diff
30063010
} else {
3007-
// Using division instead of `offset_from` helps LLVM remove bounds checks
3008-
diff / size
3011+
// We know that `start <= end`, so can do better than `offset_from`,
3012+
// which needs to deal in signed. By setting appropriate flags here
3013+
// we can tell LLVM this, which helps it remove bounds checks.
3014+
// SAFETY: By the type invariant, `start <= end`
3015+
let diff = unsafe { unchecked_sub($self.end as usize, start as usize) };
3016+
// By also telling LLVM that the pointers are apart by an exact
3017+
// multiple of the type size, it can optimize `len() == 0` down to
3018+
// `start == end` instead of `(end - start) < size`.
3019+
// SAFETY: By the type invariant, the pointers are aligned so the
3020+
// distance between them must be a multiple of pointee size
3021+
unsafe { exact_div(diff, size) }
30093022
}
30103023
}}
30113024
}

Diff for: src/test/codegen/slice-iter-len-eq-zero.rs

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// no-system-llvm
2+
// compile-flags: -O
3+
#![crate_type = "lib"]
4+
5+
type Demo = [u8; 3];
6+
7+
// CHECK-LABEL: @slice_iter_len_eq_zero
8+
#[no_mangle]
9+
pub fn slice_iter_len_eq_zero(y: std::slice::Iter<'_, Demo>) -> bool {
10+
// CHECK-NOT: sub
11+
// CHECK: %2 = icmp eq i8* %1, %0
12+
// CHECK: ret i1 %2
13+
y.len() == 0
14+
}

0 commit comments

Comments
 (0)