-
Notifications
You must be signed in to change notification settings - Fork 49
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
Added MIR optimization to avoid unnecessary re-borrows #907
base: master
Are you sure you want to change the base?
Conversation
The alternative would be to do it as an FMIR -> FMIR pass.
Indeed, but one thing we're trying to balance with this is ensuring that |
I think that indeed removing these unnecessary reborrows can help simplify the generated MLCFG code and thus the goal in Why3IDE. That being said, I don't think this helps much for having non-extensional reborrows that would solve the unsoundness in #869. The reason for that is that we want to address simultaneously solve the following goals:
#[ensures(result == &mut x.0)]
fn f(x : &mut (i32, i32)) -> &mut i32 {
&mut x.0
} I don't think this last example can be handle by a MIR/FMIR optimization, it should really be handle by translating "dead reborrows" in a specific way. A particularly tricky example is in #[open]
#[predicate]
fn completed(&mut self) -> bool {
pearlite! { self.iter.completed() }
}
#[ensures(match result {
None => self.completed(),
Some(v) => ...
})]
fn next(&mut self) -> Option<Self::Item> {
match self.iter.next() {
None => None,
Some(x) => {
let n = self.count;
self.count += 1;
Some((n, x))
}
}
} Here, in the We could even imagine an example where we reborrow |
I think doing MIR->MIR was easier since the change is more local (it doesn't need to consider resolves), and it could use the existing MIR liveness analysis. |
What's the status of this? Now that #912 is merged, it seems like the only motivation for this is simplifying the generated MLCFG. Is this something that we still want to put some energy in? |
Perhaps not this exact code, but I think it would probably be worthwhile to validate whether we have improved performance with this, especially if we can whip up a simple PoC version. My biggest worry is that this optimization would fundamentally change which borrows are final, and thus be observable at verification time. |
That's a good point that we should take care, although I'm pretty sure that in most of the cases the finality of borrows should not change. |
(My point being that these unecessaries reborrows that we are removing in this PR would have been considered final afterwards, thus we would essentially have the same behavior.) |
Yes, but we would need to verify that this is true. |
In trying to come up with a good way to handle #869 and #902, most of the trouble seems to come from unnecessary reborrowing in the MIR. If it wasn't for that adding a third field to mutable references might be enough to solve these issues while not breaking too many tests (
{ x with current = y }
would preserve the third field butBorrow.borrow_mut y
would not).Removing these reborrows also simplifies the Why3 a bit, it probably doesn't make a huge difference to provers, but it might be nice for people using the Why3IDE.
Since I didn't want to mess with the MIR to FMIR transformation I implemented this as a MIR optimization that runs before the analysis so the borrow-checker facts used to determine resolution are accurate. The "optimization" translates statements of the form
_x = &mut *_y
to_x = move _y
when_y: &mut _
and_y
is dead after the statement. I think this should be sound but I'm just making this a draft to see what you think.