-
Notifications
You must be signed in to change notification settings - Fork 13k
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 a packed/tagged pointer abstraction and utilize it for ParamEnv #75590
Conversation
(rust_highfive has picked a reviewer for you, use r? to override) |
I was hoping that we could let rustc take care of the packing for us with layout optimizations, but currently that's not quite possible: we need something like I am also unhappy with needing to split between Copy/Drop tagged pointers but AFAICT that's not possible to abstract around today (see Zulip thread here https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/struct.20conditionally.20implementing.20Drop.2FCopy) |
@Mark-Simulacrum Disallowing taking references to fields, in order to allow more intricate optimizations, has been desired for a long time, but I don't know what's been happening in this space. cc @nikomatsakis @RalfJung As for the |
I don't know anything about that; layout optimizations are not something I follow closely. For Miri, all I care about is that something computes layout somehow, and then we make sure everything works with that and all the right checks are done. ;) |
I am hoping to use this specifically for |
b335678
to
25da9d4
Compare
fn into_usize(self) -> usize { | ||
match self { | ||
traits::Reveal::UserFacing => 0, | ||
traits::Reveal::All => 1, |
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.
Can you add = 0
/ = 1
at the Reveal
definition, and then do self as usize
instead of the match
, as is done here?
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 think at that point we should stick a note on Reveal and use transmute
here to go back and forth, which I don't mind personally but seems like more work.
Note that you still need the "deserialization" match even with = 0/1
, because you can't usize as Enum
no matter what.
Let me know if you think that a transmute would be better.
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 like preserving the exhaustive match
here, although I assume @nnethercote's concern is the quality of generated code? I'm fine with this as-is.
match ptr { | ||
0 => traits::Reveal::UserFacing, | ||
1 => traits::Reveal::All, | ||
_ => std::hint::unreachable_unchecked(), |
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.
ditto
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.
Left some comments from an API design perspective. In particular, I find the true
const-generic parameter confusing. Since this is for internal use only, it's probably okay for it to be a messy, but I think we can do a bit better.
impl<P, T> PartialEq for CopyTaggedPtr<P, T, true> | ||
where | ||
P: Pointer, | ||
T: Tag, | ||
{ | ||
fn eq(&self, other: &Self) -> bool { | ||
self.packed == other.packed | ||
} | ||
} | ||
|
||
impl<P, T> Eq for CopyTaggedPtr<P, T, true> | ||
where | ||
P: Pointer, | ||
T: Tag, | ||
{ | ||
} | ||
|
||
impl<P, T> std::hash::Hash for CopyTaggedPtr<P, T, true> | ||
where | ||
P: Pointer, | ||
T: Tag, | ||
{ | ||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) { | ||
self.packed.hash(state); | ||
} | ||
} |
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've not seen this const generics pattern before. I would have used a ShallowEq
newtype wrapper instead and had deep equality by default when P: Eq
to mirror the semantics of other pointer types. Is there a reason you went with this approach?
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.
If I understand you correctly, ShallowEq
would require duplicating the non-{Hash, PartialEq, Eq} impls again -- having already done that for the drop impl, I was hesitant to add another 2 sets (for Drop and for Copy). But I can if you think it's the right move.
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.
That's true. One more suggestion: how about a ShallowEq
marker trait used to specialize the PartialEq
and Hash
impls? This doesn't need to block this PR, however.
r? @ecstatic-morse -- I think I resolved your comments in the just pushed commit, or replied to them inline if I felt further discussion was warranted before changing code. |
@bors try @rust-timer queue |
Awaiting bors try build completion |
⌛ Trying commit 5f23bf7aa38f0b4447ff39593bcc49f804092721 with merge 4b58fff90f503a8ebea6194ad38c64aa35fea076... |
☀️ Try build successful - checks-actions, checks-azure |
Queued 4b58fff90f503a8ebea6194ad38c64aa35fea076 with parent 67e7b9b, future comparison URL. |
Finished benchmarking try commit (4b58fff90f503a8ebea6194ad38c64aa35fea076): comparison url. Benchmarking this pull request likely means that it is perf-sensitive, so we're automatically marking it as not fit for rolling up. Please note that if the perf results are neutral, you should likely undo the rollup=never given below by specifying Importantly, though, if the results of this run are non-neutral do not roll this PR up -- it will mask other regressions or improvements in the roll up. @bors rollup=never |
/// # Safety | ||
/// | ||
/// The usize returned from `into_usize` must be a valid, dereferenceable, | ||
/// pointer to `<Self as Deref>::Target`. Note that pointers to `Pointee` should |
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.
/// pointer to `<Self as Deref>::Target`. Note that pointers to `Pointee` should | |
/// pointer to `<Self as Deref>::Target`. Note that pointers to `Pointee` *must* |
My broader concerns have been addressed except for the use of a const-generic boolean to select the If you choose to defer, r=me once the RFC 2119 nit is fixed. |
5f23bf7
to
107e290
Compare
I was unable to come up with a scheme for ShallowEq to work how I wanted. Ideally this is our set of impls: // currently requires both to be ShallowEq (or not), but one could imagine a full matrix of 4 options
impl<P: Pointer + PartialEq, T: Tag + PartialEq> PartialEq for TaggedPtr<P, T> {}
impl<P: Pointer + ShallowEq, T: Tag + ShallowEq> PartialEq for TaggedPtr<P, T> {}
impl<P: Pointer + Eq, T: Tag + Eq> Eq for TaggedPtr<P, T> {}
impl<P: Pointer + ShallowEq, T: Tag + ShallowEq> Eq for TaggedPtr<P, T> {} AFAICT this set of impls isn't possible to achieve with specialization today, even in the non-min form, as you can't "de-require" a trait bound that's present on the default impl. At least I wasn't able to get it to work. I think maybe we could have the easier to satisfy requirement that even with ShallowEq implemented you still need PartialEq/Eq, but then I wouldn't bother with specialization I think -- we can just have a @bors r=ecstatic-morse |
📌 Commit 107e290 has been approved by |
☀️ Test successful - checks-actions, checks-azure |
Final perf results show a slight win on |
It seems like pointer packing via shifting instead of or and and operations is slightly more efficient (or LLVM optimizes it better). I think that's the only difference between the two schemes (beyond splitting into a separate crate etc). |
I think this commit seems to make the new symbol mangling to fail. |
The intent here is mostly just to add the abstraction; I suspect that there are definitely more use cases for it, and we can explore those over time now that there's a (mostly) safe abstraction that can be used in rustc.