-
Notifications
You must be signed in to change notification settings - Fork 13.1k
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
Implement const propagation for the MIR Len
operand
#61081
Conversation
The job Click to expand the log.
I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact |
) => { | ||
// mutable statics can change value at any time so we can't const | ||
// propagate their values | ||
if self.tcx.is_mutable_static(def_id) { |
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.
Does this also need to take internal mutability into account?
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.
It's even worse, the moment you encounter a union or raw pointer you need to give up. Is this change needed for this PR or could it be done in an extra PR?
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.
could it be done in an extra PR?
Possibly? I had some trouble getting a Len
operand to appear in the MIR and this was necessary to get the current test case I came up with to pass. The obvious test case
let x= &[1, 2, 3];
x[1]
doesn't seem to generate a Len
operand at all.
I'll look into this tonight but this might not be necessary to get this test case to work:
const TEST: &[u32] = &[1, 2, 3];
fn test() -> u32 {
TEST[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.
You get one if you use let x: &[u8] = &[1, 2, 3];
. Arrays don't generate Len
Rvalue
s
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.
Oh wow. I didn't realize &[1, 2, 3]
got inferred as a &[{integer}; 3]
instead of a slice. That makes sense why my original test case didn't work then. A lot of these changes are probably not necessary then.
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... The original test wasn't ((&[1, 2, 3]) as &[u32])[1]
, because I didn't what the MIR after the &[1, 2, 3]
to know about the actual array size. But that's not really useful I guess. Feel free to just change it to one big expression and scrap the rest of the test.
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.
Sorry, my previous comment was kind of vague. So far, I haven't been able to find a test case that contains a Len
operand that we can const propagate without the changes to handle statics. Here's what I've tried:
let x: &[u32] = &[1, 2, 3];
x[1];
This style test doesn't work at all because x
is a local and we don't const propagate locals.
(&[1, 2, 3] as &[u32])[1];
This doesn't compile because it's a non-primitive cast.
static X: &[u32] = &[u32];
X[1];
This could maybe work but would require additional changes like those included in this PR to handle statics.
const X: &[u32] = &[1, 2, 3];
X[1];
This I believe will work once we have support in replace_with_const()
for Operand::Indirect
.
So, as it stands currently, I don't think there's anyway to write a test which will exercise this part of the const propagator.
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.
The let
binding case we can't do until we get some sort of dataflow analysis going
The static case scares me. I'm not sure we should be propagating statics, like ever. A static is something with a name. Reading from it seems like something we shouldn't optimize out, not sure how aggressive we can or want to be there. It's probably alright if the value being read is not from a static mut and the type has no interior mutability, but if we read beyond that (like from things with UnsafeCell
or behind raw pointers) we have no guarantees. The problem is, once we read from a static, the propagated value has no notion of "I'm from a static" anymore. So we don't know anymore that we can't just read further values. Most likely not a problem for now, as propagating raw pointer derefs is its very own crazy topic that probably needs UGC discussions. But for now I'd rather not open this box.
The other two cases seem reasonable to me, pick either I guess? Though you may end up needing Indirect
support for the cast case, too, so just go with the constant, which requires the least changes.
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.
The static case scares me. I'm not sure we should be propagating statics, like ever.
Yeah, I agree. There's so many edge cases here that it would be very complicated to ensure this was done correctly.
The let binding case we can't do until we get some sort of dataflow analysis going
Do we need full dataflow analysis or is there a simpler thing we could do now and eventually replace with dataflow? For example, I have a change I've been playing with that allows propagating locals which were themselves propagated into. This only is only at the in the ConstPropagator
and not the CanConstProp
pass so it doesn't affect error reporting. (See e699063 for my code)
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... we'd also need to make sure to propagate neither from nor into locals that have more than one mutable usage. You don't want any const prop for let x = if y { 5 } else { 6 };
or for let mut x = 42; if y { x = 9; }
// FIXME(oli-obk): evaluate static/constant slice lengths | ||
Rvalue::Len(_) => None, | ||
let is_slice = | ||
if let ty::Slice(_) = mplace.layout.ty.sty { 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.
This is scary. Do we really encounter both &[T]
and [T]
?
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, after *&[T]
is evaluated, we're left with a bare [T]
.
Rvalue::Len(_) => None, | ||
let is_slice = | ||
if let ty::Slice(_) = mplace.layout.ty.sty { true } | ||
else { mplace.layout.ty.is_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.
this will also return true for *const [T]
, which we should probably not const prop for now
☔ The latest upstream changes (presumably #61258) made this pull request unmergeable. Please resolve the merge conflicts. |
I'm closing this for now since there's a number of issues with the current implementation and I don't see any way to actually test this code right now because of the issues identified here. |
r? @oli-obk