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 a packed/tagged pointer abstraction and utilize it for ParamEnv #75590

Merged
merged 2 commits into from
Aug 19, 2020

Conversation

Mark-Simulacrum
Copy link
Member

@Mark-Simulacrum Mark-Simulacrum commented Aug 16, 2020

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.

@rust-highfive
Copy link
Collaborator

r? @ecstatic-morse

(rust_highfive has picked a reviewer for you, use r? to override)

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Aug 16, 2020
@Mark-Simulacrum
Copy link
Member Author

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 #[repr(no_reference, transparent)] or so which would permit rustc to use niches that prevent taking references to the inner field. I think adding something like that would be quite worthwhile, but I don't know how feasible that is. cc @eddyb

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)

@eddyb
Copy link
Member

eddyb commented Aug 16, 2020

@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 Copy vs Drop: we should be able to limit ourselves to Copy (or at least assert !needs_drop) for most rustc things, e.g. a lot of our arenas are Copy-only and can erase types entirely without recording destructors, as there are none to run.

@RalfJung
Copy link
Member

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.

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. ;)

@Mark-Simulacrum
Copy link
Member Author

As for the Copy vs Drop: we should be able to limit ourselves to Copy (or at least assert !needs_drop) for most rustc things, e.g. a lot of our arenas are Copy-only and can erase types entirely without recording destructors, as there are none to run.

I am hoping to use this specifically for Rc which needs Drop impls.

fn into_usize(self) -> usize {
match self {
traits::Reveal::UserFacing => 0,
traits::Reveal::All => 1,
Copy link
Contributor

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?

Copy link
Member Author

@Mark-Simulacrum Mark-Simulacrum Aug 16, 2020

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.

Copy link
Contributor

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(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

Copy link
Contributor

@ecstatic-morse ecstatic-morse left a 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.

src/librustc_data_structures/tagged_ptr.rs Show resolved Hide resolved
src/librustc_data_structures/tagged_ptr/copy.rs Outdated Show resolved Hide resolved
src/librustc_data_structures/tagged_ptr/copy.rs Outdated Show resolved Hide resolved
src/librustc_data_structures/tagged_ptr.rs Outdated Show resolved Hide resolved
Comment on lines +137 to +170
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);
}
}
Copy link
Contributor

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?

Copy link
Member Author

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.

Copy link
Contributor

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.

src/librustc_middle/ty/list.rs Show resolved Hide resolved
src/librustc_data_structures/tagged_ptr.rs Show resolved Hide resolved
@Mark-Simulacrum
Copy link
Member Author

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.

@Mark-Simulacrum
Copy link
Member Author

@bors try @rust-timer queue

@rust-timer
Copy link
Collaborator

Awaiting bors try build completion

@bors
Copy link
Contributor

bors commented Aug 17, 2020

⌛ Trying commit 5f23bf7aa38f0b4447ff39593bcc49f804092721 with merge 4b58fff90f503a8ebea6194ad38c64aa35fea076...

@bors
Copy link
Contributor

bors commented Aug 17, 2020

☀️ Try build successful - checks-actions, checks-azure
Build commit: 4b58fff90f503a8ebea6194ad38c64aa35fea076 (4b58fff90f503a8ebea6194ad38c64aa35fea076)

@rust-timer
Copy link
Collaborator

Queued 4b58fff90f503a8ebea6194ad38c64aa35fea076 with parent 67e7b9b, future comparison URL.

@rust-timer
Copy link
Collaborator

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 rollup- to bors.

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// pointer to `<Self as Deref>::Target`. Note that pointers to `Pointee` should
/// pointer to `<Self as Deref>::Target`. Note that pointers to `Pointee` *must*

@ecstatic-morse
Copy link
Contributor

ecstatic-morse commented Aug 19, 2020

My broader concerns have been addressed except for the use of a const-generic boolean to select the PartialEq impl and the lack of a PartialEq impl in the case where shallow equality is incorrect. I've proposed a solution using min_specialization above that (I think) would resolve both issues. If you want to try this as part of this PR, go for it, but it could be done in a follow up as well.

If you choose to defer, r=me once the RFC 2119 nit is fixed.

@Mark-Simulacrum
Copy link
Member Author

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 const SHALLOW_EQ_AND_HASH: bool; on Pointer and Tag traits and use if statements to dispatch. We'll remove the dead code in mir opts anyway.

@bors r=ecstatic-morse

@bors
Copy link
Contributor

bors commented Aug 19, 2020

📌 Commit 107e290 has been approved by ecstatic-morse

@bors bors removed the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Aug 19, 2020
@bors bors added the S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. label Aug 19, 2020
@bors
Copy link
Contributor

bors commented Aug 19, 2020

⌛ Testing commit 107e290 with merge 32c654a...

@bors
Copy link
Contributor

bors commented Aug 19, 2020

☀️ Test successful - checks-actions, checks-azure
Approved by: ecstatic-morse
Pushing 32c654a to master...

@bors bors added the merged-by-bors This PR was explicitly merged by bors. label Aug 19, 2020
@bors bors merged commit 32c654a into rust-lang:master Aug 19, 2020
@ecstatic-morse
Copy link
Contributor

Final perf results show a slight win on keccak and inflate. This is somewhat unexpected, since ParamEnv already had an ad hoc version of this optimization.

@Mark-Simulacrum Mark-Simulacrum deleted the tagged-ptr branch August 24, 2020 21:23
@Mark-Simulacrum
Copy link
Member Author

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).

@andjo403
Copy link
Contributor

I think this commit seems to make the new symbol mangling to fail.
This is the first PR that fails with the error described in #76365
I do not know if the fault is here or in the symbol mangling but it as I understand it that function that is called is only used for the the size of an array and it feels strange that it shall be a bool

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
merged-by-bors This PR was explicitly merged by bors. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion.
Projects
None yet
Development

Successfully merging this pull request may close these issues.