Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add unreachable hints for length getters? #106737

Open
newpavlov opened this issue Jan 11, 2023 · 7 comments
Open

Add unreachable hints for length getters? #106737

newpavlov opened this issue Jan 11, 2023 · 7 comments
Labels
C-enhancement Category: An issue proposing an enhancement or a PR with one. C-optimization Category: An issue highlighting optimization opportunities or PRs implementing such T-libs Relevant to the library team, which will review and decide on the PR/issue.

Comments

@newpavlov
Copy link
Contributor

As discussed in this thread, Rust does not provide LLVM any information about possible lengths of slices, vectors, and other types. In some cases it prevents LLVM from applying optimizations since it has to account for invalid lengths.

I suggest updating length getters of slice-like types to something like this:

#[inline]
pub const fn len(&self) -> usize {
    let len = ...;
    let size = core::mem::size_of::<T>();
    // on most arches we can use `isize::MAX as usize`
    // instead of `usize::MAX`, but IIRC not everywhere
    if size != 0 && len > usize::MAX / size {
        unsafe { core::hint::unreachable_unchecked() }
    }
    len
}
@Noratrieb Noratrieb added the T-libs Relevant to the library team, which will review and decide on the PR/issue. label Jan 12, 2023
@Dylan-DPC Dylan-DPC added the C-enhancement Category: An issue proposing an enhancement or a PR with one. label Jan 13, 2023
@thomcc
Copy link
Member

thomcc commented Jan 13, 2023

In many cases it would be much better to do this by emitting range metadata info in the compiler -- https://rust-lang.zulipchat.com/#narrow/stream/187780-t-compiler.2Fwg-llvm/topic/Range.20metadata.20for.20.60size_of_val.60.20and.20other.20isize.3A.3AMAX.20limits is a conversation about it (for size_of_val) I started a while ago. If that were done we could consider rewriting <[T]>::len() in terms of size_of_val, although that's not guaranteed to be a win either.

In general assumes (including indirect ones like this) can often impact compile time quite a bit (and sometimes even runtime) negatively, since the compiler doesn't really know how long to keep the conditional expressions that drive it (keep them too long, then it blocks optimizations and perhaps hurts runtime, discard them too soon and it ends up not being available when needed) -- it tends to be better as metadata.

My hunch is that it's unlikely that we'll be able to accept the impact of doing this as a library change on slices.

@newpavlov
Copy link
Contributor Author

@thomcc
Would it be possible to add range metadata to a variable in library code? For example, using an unsafe intrinsic? Or does it need introduction of special types? Such intrinsic would be useful in user code as well, e.g. block-buffer uses unreachable_unchecked to eliminate unreachable panics.

In the linked discussion @erikdesjardins has expressed concern about range metadata getting eliminated too early to positively impact optimization passes. I guess using #[inline(always)] may help here?

@thomcc
Copy link
Member

thomcc commented Jan 13, 2023

Would it be possible to add range metadata to a variable in library code

Not really, because slice.len() turns into something special in MIR right away anyway (a Len instruction). IIUC the compiler doesn't ever actually emit calls to the function, it just knows what it does. -- this is likely a good thing FWIW, it gets value out of that knowledge, and should be able to use that to attach the range metadata to the result of the load (which should avoid the concern about it being inlined too soon?)

I don't know that there's a good way to anything like this to libraries. The closest would be the niche RFC. But the concerns of the standard library here are very different than for other library code, and really this is probably not an issue that should be used for discussion of new language features like that anyway.

@newpavlov
Copy link
Contributor Author

newpavlov commented Jan 13, 2023

Not really, because slice.len() turns into something special in MIR right away anyway (a Len instruction). IIUC the compiler doesn't ever actually emit calls to the function, it just knows what it does.

IIUC it happens only after the method gets inlined. Otherwise, it's a simple method to which we can add whatever we want, be it unreachable_unchecked or the hypothetical unsafe intrinsic.

P.S.: Feel free to close this issue, if you think that the unreachable_unchecked approach is a non-starter.

@erikdesjardins
Copy link
Contributor

should be able to use that to attach the range metadata to the result of the load

The problem is, there often isn't a load, e.g. whenever a slice is passed directly as an argument: https://godbolt.org/z/z6ocsEfzx. Even if it were temporarily spilled to an alloca, that load would get optimized out by SROA early in the pipeline, dropping the metadata. (If the slice is behind another layer of indirection, e.g. in fn foo(x: &Bar) { x.slice.len() }, then the load will stay around, and metadata ought to help.)

This is not to say that we shouldn't add range metadata, just that I think it won't help that much. I agree that adding metadata has a much better cost/benefit tradeoff vs. adding assumes.

What would really help is range attributes on arguments. If LLVM had that, we could codegen
fn foo(x: &[i32])
as
@foo(ptr <...> %data, i64 range {0, max} %len)
which would cover a lot more cases.

@scottmcm
Copy link
Member

What would really help is range attributes on arguments

That'd be a big help for NonZeroU32 and friends too.

@workingjubilee workingjubilee added the C-optimization Category: An issue highlighting optimization opportunities or PRs implementing such label Oct 8, 2023
@scottmcm
Copy link
Member

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-enhancement Category: An issue proposing an enhancement or a PR with one. C-optimization Category: An issue highlighting optimization opportunities or PRs implementing such T-libs Relevant to the library team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

7 participants