-
Notifications
You must be signed in to change notification settings - Fork 13.2k
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
Eliminate bounds checking in slice::Windows #77617
Eliminate bounds checking in slice::Windows #77617
Conversation
(rust_highfive has picked a reviewer for you, use r? to override) |
Would you mind adding a codegen test which checks that |
I expect that you need something like this: https://github.com/bugadani/rust/blob/1d157ce797dddcee16a577796199b1144b4f7f34/src/test/codegen/enum-bounds-check-issue-13926.rs (potentially without min-llvm-version, depending on how stable this optimization is) |
@lcnr I can add it. However, I see no way to run codegen check. How can I do it? |
|
@lcnr I've added test but I am not sure if it is correct. Or should I test |
seems good to me. Does this test fail without your changes? |
It... Passes without my changes. Apparently I don't know how to write codegen tests |
Looks like you need |
0f52d1b
to
b5096c1
Compare
@lcnr without my changes this codegen test fails on |
BTW I only now realised that my rather mechanical changes in other methods seem also to eliminate bounds check in |
b5096c1
to
a8f098b
Compare
hmm, it probably won't hurt to use explicitly extend the test to also add functions for these methods 🤷 feel free to do so r? @lcnr |
a8f098b
to
61fca88
Compare
@lcnr I've added codegen tests for |
Looking at the emitted code on https://godbolt.org/z/T716cx It looks to me like In general this looks to me like these tests might not be as useful as I thought, considering that method renames make them somewhat useless. Does it work to instead test with the following? // CHECK-NOT: panic
// CHECK-NOT: fail |
61fca88
to
e699e83
Compare
Indeed, it correctly checks with |
@bors r+ |
📌 Commit e699e83 has been approved by |
@@ -751,7 +752,7 @@ impl<T> [T] { | |||
#[stable(feature = "rust1", since = "1.0.0")] | |||
#[inline] | |||
pub fn windows(&self, size: usize) -> Windows<'_, T> { | |||
assert_ne!(size, 0); | |||
let size = NonZeroUsize::new(size).expect("size is zero"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we have guideline about what the phrase used in expect call ?
let size = NonZeroUsize::new(size).expect("size is zero"); | |
let size = NonZeroUsize::new(size).expect("`size` cannot be zero"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure, but before my change panic message was assertion failed: size != 0
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is bike-shredding so don't worry much about this.
BTW while inspecting the assembly on order to determine which changed methods benefit from my changes I checked // impl DoubleEndedIteator ...
pub fn nth_back(&mut self, n: usize) -> Option<&'a [T]> {
let (end, overflow) = self.v.len().overflowing_sub(n);
if end < self.size.get() || overflow {
self.v = &[];
None
} else {
let ret = &self.v[end - self.size.get()..end];
self.v = &self.v[..end - 1];
Some(ret)
}
} Here else branch is taken when both conditions in The compiler understands that |
TBH I was surprised that |
☀️ Test successful - checks-actions, checks-azure |
This is how
<core::slice::Windows as Iterator>::next
looks right now:The line with
self.v = &self.v[1..];
relies on assumption thatself.v
is definitely not empty at this point. Else branch is taken whenself.size <= self.v.len()
, soself.v
can be empty ifself.size
is zero. In practice, sinceWindows
is never created directly but rather trough[T]::windows
which panics whensize
is zero,self.size
is never zero. However, the compiler doesn't know about this check, so it keeps the code which checks bounds and panics.Using
NonZeroUsize
lets the compiler know about this invariant and reliably eliminate bounds checking withoutunsafe
on-O2
. Here is assembly ofWindows<'a, u32>::next
before and after this change (goldbolt):Before
After
Note the lack of call to
core::slice::slice_index_order_fail
in second snippet.Possible reasons not to merge this PR:
[T]::windows
. However, AFAIK this messages are not covered by backwards compatibility policy.