Skip to content

Missed optimization on value range of slice::len #67186

Closed
@197g

Description

@197g

The internals of llvm do not permit creation of slices spanning more
than isize::MAX bytes, as a limit resulting from pointer offsetting
inbounds. It must be both possible to represent arbitrary pointer
differences of the same allocation within an isize and there must be
no wrapping in the ptr::offset and array indexing. This has influenced
the surface documentation for slice::from_raw_parts:

The total size of the slice must be no larger than isize::MAX bytes in
memory. See the safety documentation of pointer::offset.

However, the implementation in the core library can not leverage this
property for optimization. To illustrate this, the following code should never
require an overflow check and never panic but the compiler is unable to
remove the panic path.

pub fn len_add(a: &[u8], b: &[u8]) -> usize {
    a.len().checked_add(b.len()).unwrap()
}

play

The core library could add an optimization hint in its implementation
of slice::len asserting that the value range is in fact at most as large as
the maximum possible values of isize. This information should then be
propagated automatically at each call site in optimizer passes.

// This is libcore, referring to the stabilized `unreachable_unchecked`.
use crate::intrinsics::unreachable;

// Alternative of `[T]::len` in libcore
pub const fn len(&self) -> usize {
    let raw_len = unsafe {
        crate::ptr::Repr { rust: self }.raw.len
    };
    
    if mem::size_of::<T>() > 0 {
        if raw_len > isize::max_value() as usize / mem::size_of::<T>() {
            // SAFETY: allocations can not be larger than `isize::MAX`
            // bytes. Since slices refer to a single allocation, this extends
            // to slice lengths of sized types. At this point we would have
            // however:
            //
            // > `len() * mem::size_of::<T>() > isize::MAX`.
            //
            // Since this path can be deduced as unreachable due to the 
            // below hint, the optimizer can restrict the value range of
            // return values for the purpose of optimization at each
            // call site.
            unsafe { unreachable() }
        }
    }
    
    raw_len
}

This currently allows the optimizer to remove the bounds check, as can be seen in this implementation: https://play.rust-lang.org/?version=nightly&mode=release&edition=2018&gist=6e46ce72b9affea080a33eee72b53267

I don't see a way to enable this on current nightly as slice::len is a const fn and several parts of the above implementation will consequently not yet work.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-codegenArea: Code generationC-enhancementCategory: An issue proposing an enhancement or a PR with one.I-slowIssue: Problems and improvements with respect to performance of generated code.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions