-
Notifications
You must be signed in to change notification settings - Fork 13.4k
Rewrite the Visitor
for non_ssa_locals
#73854
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Rewrite the Visitor
for non_ssa_locals
#73854
Conversation
r? @varkor (rust_highfive has picked a reviewer for you, use r? to override) |
r? @eddyb |
35cd09f
to
ccd63ea
Compare
☔ The latest upstream changes (presumably #69749) made this pull request unmergeable. Please resolve the merge conflicts. |
8f82918
to
86bf63a
Compare
I made the requested changes @eddyb, can you take another look? |
// Projections like `(*x)[12]` are allowed but not `*(x[12])`, since a `Deref` of a | ||
// local acts like a `Copy` of that local. |
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 a bit iffy in that it doesn't mention pointers at all, which is what is being "copied", and the arbitrary example of indexing had me confused for a while, although I see what it's saying now.
I would frankly just say that a Deref
is a pointer/reference Copy
and be done with it.
continue; | ||
} | ||
|
||
// Ignoring `Deref`s, the only allowed projections are reads of scalar fields. |
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.
s/allowed/SSA-capable (or similar phrasing) would help IMO.
if ty_requires_alloca(&analyzer.fx, layout) { | ||
analyzer.not_ssa(local); | ||
} |
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.
Maybe we should do this first, and even keep the monomorphized TyAndLayout
s around, to make it cheaper to check if "consume" field projections can be handled in an SSA way.
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.
Actually, huh, we have access to FunctionCx
, I'm tempted to say we should create the FunctionCx
with all of the local TyAndLayout
s already computed (although we couldn't have placeholder LocalRef
s easily I don't think?) - but maybe that's too much for this PR?
while let [ref proj_base @ .., elem] = *projection { | ||
projection = proj_base; | ||
self.super_projection_elem(local, proj_base, elem, context, location); | ||
|
||
// Projections like `(*x)[12]` are allowed but not `*(x[12])`, since a `Deref` of a | ||
// local acts like a `Copy` of that local. | ||
if let mir::PlaceElem::Deref = elem { | ||
has_disqualifying_projection = false; | ||
context = PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy); | ||
continue; | ||
} | ||
|
||
// Ignoring `Deref`s, the only allowed projections are reads of scalar fields. | ||
if is_consume(context) && matches!(elem, mir::ProjectionElem::Field(..)) { | ||
let base_ty = mir::Place::ty_from(local, proj_base, self.fx.mir, self.fx.cx.tcx()); | ||
let base_ty = self.fx.monomorphize(&base_ty); | ||
let span = self.fx.mir.local_decls[local].source_info.span; | ||
let layout = self.fx.cx.spanned_layout_of(base_ty.ty, span); | ||
|
||
if !ty_requires_alloca(self.fx, layout) { | ||
continue; | ||
} | ||
} | ||
|
||
has_disqualifying_projection = true; | ||
} |
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.
Looking more at this loop, I would split it into several parts:
- a loop to just call
super_projection_elem
uniformly on all the elems, to get it out of the way (it only serves to visit the index local of anIndex
projection, with a `Copy context) - find the first
Deref
, if there is one, e.g. with.position(|elem| matches!(elem, mir::PlaceElem::Deref))
- loop over the "direct projections" (i.e. before any
Deref
, if any) and doself.not_ssa(local); break;
at the first sign of trouble
I mostly want this for clarity, but that last part has some promise for making this more efficient, so here's a few more ideas:
context
wouldn't change across it, meaningif is_consume(context)
can be placed around the whole loop- there could be a quick check that all the elements are fields, to avoid time even looking at the layouts
- we probably should check all the field projections (like today / this PR) but if we go forward, from the
local
to the innermost field, we should almost always hit the non-SSA layout first, making the rejection cheaper - we could call
visit_local
before the loop, and skip the loop ifnon_ssa_locals
already contains the local (see also the comment at the top of this file, which would make this more useful) - if we keep the monomorphized
TyAndLayout
of the local (which we always have to compute anyway) around, we could start from it and just golayout = layout.field(self.fx, i)
at every step instead of computing the whole type and monomorphizing every time
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 tried implementing some of these ideas in the latest commit. It's more efficient, but seems more complex to me. If you have a clear idea for how this code should look, you should implement it on top of or in place of this PR. I don't think I'm going to keep working on this.
let span = self.fx.mir.local_decls[local].source_info.span; | ||
let layout = self.fx.cx.spanned_layout_of(base_ty.ty, span); | ||
|
||
if !ty_requires_alloca(self.fx, layout) { |
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.
Hmm, I feel like this doesn't capture the important aspect here: only for some layouts we can extract their fields as SSA values. So maybe instead of !ty_requires_alloca
it could be layout_allows_ssa
(i.e. with flipped meaning).
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 wouldn't have done the renaming in 45fd741, since being assigned exactly once before any read (SSA-like) seems to be only one of several requirements for a value to be forced to memory. I've been using the term "requires alloca" for locals that fail this check, but it feels like there should be a good term for locals that do not require allocas that I'm not seeing.
This is the same size as `Location`. Using an invalid location is premature optimization.
c0fb44c
to
99badcf
Compare
99badcf
to
277d4ca
Compare
☔ The latest upstream changes (presumably #74862) made this pull request unmergeable. Please resolve the merge conflicts. |
@ecstatic-morse if you can resolve the conflicts, we can get this merged |
Closing this due to inactivity. Thanks |
Resolves #72931. Seems like we're not going to do #73732 anytime soon, so might as well clean this up while it's fresh in my head. This tries to implement what I perceived as the intent of the old code, since there were several bugs which I mention in #72931. Also, we now only look for ZSTs in the final type of the
mir::Place
, not in all the types of its projections besides the base local. I couldn't see a good reason for the old behavior.