-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Move Option::as_slice
to an always-sound implementation
#108623
Conversation
r? @m-ou-se (rustbot has picked a reviewer for you, use r? to override) |
This comment was marked as resolved.
This comment was marked as resolved.
5fcedf2
to
c2df8b9
Compare
// CHECK-NOT: select | ||
// CHECK-NOT: br | ||
// CHECK-NOT: switch | ||
// CHECK-NOT: icmp | ||
o.as_slice() |
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.
Output of the codegen test on my machine, in case it helps a reviewer:
; Function Attrs: mustprogress nofree nosync nounwind willreturn uwtable
define { ptr, i64 } @u64_opt_as_slice(ptr noalias noundef readonly align 8 dereferenceable(16) %o) unnamed_addr #0 {
start:
%_3.i = load i64, ptr %o, align 8, !range !1, !alias.scope !2, !noundef !5
%payload.i = getelementptr inbounds { i64, i64 }, ptr %o, i64 0, i32 1
%0 = insertvalue { ptr, i64 } undef, ptr %payload.i, 0
%1 = insertvalue { ptr, i64 } %0, i64 %_3.i, 1
ret { ptr, i64 } %1
}
; Function Attrs: mustprogress nofree nosync nounwind willreturn uwtable
define { ptr, i64 } @nonzero_u64_opt_as_slice(ptr noalias noundef readonly align 8 dereferenceable(8) %o) unnamed_addr #0 {
start:
%0 = load i64, ptr %o, align 8, !alias.scope !6, !noundef !5
%.not3.i = icmp ne i64 %0, 0
%len.i = zext i1 %.not3.i to i64
%1 = insertvalue { ptr, i64 } undef, ptr %o, 0
%2 = insertvalue { ptr, i64 } %1, i64 %len.i, 1
ret { ptr, i64 } %2
}
!1 = !{i64 0, i64 2}
Let's flip this to a hopefully-less-busy non-libs-api reviewer: |
library/core/src/option.rs
Outdated
|
||
// These are all true by construction, but to make it obvious: | ||
assert!(offset >= 0); | ||
assert!((offset as usize) <= mem::size_of::<Self>()); |
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.
Yeah, same point as 8472 above, but I don't really see why this has to hold in the face of very silly layouts for Option<MaybeUninit<T>>
. The other two I agree with.
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.
Yup, agreed with both of you. Enough spurious padding and it could be out of bounds.
@rustbot author
This approach depends on CSE to not have any branches or selects when the guessed offset is correct -- which it always will be right now -- but to also be *sound* (just less efficient) if the layout algorithms change such that the guess is incorrect.
c2df8b9
to
f6a57c1
Compare
As I was doing this, I realized that the layout-optimized case is actually a subset of the "guess is totally wrong case", so I think this came out pretty elegantly. @rustbot ready |
Maybe this could use a FIXME that what we really want here is a offset_of into an enum variant, if such a feature ever materializes. But we don't even have an RFC open that we could point to. r=me either way |
library/core/src/option.rs
Outdated
|
||
let max_offset = mem::size_of::<Self>() - mem::size_of::<T>(); | ||
if offset as usize <= max_offset { | ||
// The offset is at least inside the object, so let's try it. |
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.
Wouldn't this be "inside or one past the end"?
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.
Only if T
is a ZST, since I'm checking the offset against sizeof(Option<T>) - sizeof(T)
, not just against sizeof(Option<T>)
.
("Inside" is always a hard question when it comes to ZSTs...)
@bors r=the8472 |
☀️ Test successful - checks-actions |
1 similar comment
☀️ Test successful - checks-actions |
Finished benchmarking commit (cf8d98b): comparison URL. Overall result: ❌ regressions - ACTION NEEDEDNext Steps: If you can justify the regressions found in this perf run, please indicate this with @rustbot label: +perf-regression Instruction countThis is a highly reliable metric that was used to determine the overall result at the top of this comment.
Max RSS (memory usage)ResultsThis is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
CyclesResultsThis is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
|
This approach depends on CSE to not have any branches or selects when the guessed offset is correct -- which it always will be right now -- but to also be sound (just less efficient) if the layout algorithms change such that the guess is incorrect.
The codegen test confirms that CSE handles this as expected, leaving the optimal codegen.
cc JakobDegen #108545