Skip to content

Commit

Permalink
Help LLVM better optimize slice::Iter(Mut)::len
Browse files Browse the repository at this point in the history
  • Loading branch information
scottmcm committed Jun 16, 2019
1 parent 9f8cd9d commit af0e35e
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 4 deletions.
6 changes: 6 additions & 0 deletions src/libcore/intrinsics.rs
Expand Up @@ -1607,3 +1607,9 @@ pub fn maxnumf64(x: f64, y: f64) -> f64 {
// Identical to the `f32` case.
(if x < y || x != x { y } else { x }) * 1.0
}

/// For bootstrapping, implement unchecked_sub as just wrapping_sub.
#[cfg(bootstrap)]
pub unsafe fn unchecked_sub<T>(x: T, y: T) -> T {
sub_with_overflow(x, y).0
}
21 changes: 17 additions & 4 deletions src/libcore/slice/mod.rs
Expand Up @@ -25,7 +25,7 @@
use crate::cmp::Ordering::{self, Less, Equal, Greater};
use crate::cmp;
use crate::fmt;
use crate::intrinsics::assume;
use crate::intrinsics::{assume, exact_div, unchecked_sub};
use crate::isize;
use crate::iter::*;
use crate::ops::{FnMut, Try, self};
Expand Down Expand Up @@ -2998,14 +2998,27 @@ macro_rules! is_empty {
// unexpected way. (Tested by `codegen/slice-position-bounds-check`.)
macro_rules! len {
($self: ident) => {{
#![allow(unused_unsafe)] // we're sometimes used within an unsafe block

let start = $self.ptr;
let diff = ($self.end as usize).wrapping_sub(start as usize);
let size = size_from_ptr(start);
if size == 0 {
// This _cannot_ use `unchecked_sub` because we depend on wrapping
// to represent the length of long ZST slice iterators.
let diff = ($self.end as usize).wrapping_sub(start as usize);
diff
} else {
// Using division instead of `offset_from` helps LLVM remove bounds checks
diff / size
// We know that `start <= end`, so can do better than `offset_from`,
// which needs to deal in signed. By setting appropriate flags here
// we can tell LLVM this, which helps it remove bounds checks.
// SAFETY: By the type invariant, `start <= end`
let diff = unsafe { unchecked_sub($self.end as usize, start as usize) };
// By also telling LLVM that the pointers are apart by an exact
// multiple of the type size, it can optimize `len() == 0` down to
// `start == end` instead of `(end - start) < size`.
// SAFETY: By the type invariant, the pointers are aligned so the
// distance between them must be a multiple of pointee size
unsafe { exact_div(diff, size) }
}
}}
}
Expand Down
14 changes: 14 additions & 0 deletions src/test/codegen/slice-iter-len-eq-zero.rs
@@ -0,0 +1,14 @@
// no-system-llvm
// compile-flags: -O
#![crate_type = "lib"]

type Demo = [u8; 3];

// CHECK-LABEL: @slice_iter_len_eq_zero
#[no_mangle]
pub fn slice_iter_len_eq_zero(y: std::slice::Iter<'_, Demo>) -> bool {
// CHECK-NOT: sub
// CHECK: %2 = icmp eq i8* %1, %0
// CHECK: ret i1 %2
y.len() == 0
}

0 comments on commit af0e35e

Please sign in to comment.