Description
This ties into place/address uniqueness discussions.
Consider that we have some type Data
which has pass-by-reference ABI and isn't Copy
. We have code with the shape:
extern fn consume(data: Data);
fn entry(data: Box<Data>) {
consume(*data);
}
Using &move
to represent the ABI, this lowers to roughly look like:
fn entry(data: Box<Data>) {
'bb0: {
let tmp = *data;
consume(&move tmp) -> [return: 'bb1, unwind: 'bb2];
}
'bb1: {
Drop::drop(&mut data); // outer drop
return;
}
'bb2: {
Drop::drop(&mut data); // outer drop
k#resume_unwind;
}
}
Optimized MIR
fn entry(_1: Box<Data>) -> () {
debug x => _1;
let mut _0: ();
let _2: ();
let mut _3: Data;
let mut _4: &mut std::boxed::Box<Data>;
let mut _5: ();
let mut _6: &mut std::boxed::Box<Data>;
let mut _7: ();
let mut _8: *const Data;
bb0: {
StorageLive(_3);
_8 = (((_1.0: std::ptr::Unique<Data>).0: std::ptr::NonNull<Data>).0: *const Data);
_3 = move (*_8);
_2 = consume(move _3) -> [return: bb1, unwind: bb4];
}
bb1: {
StorageDead(_3);
_4 = &mut _1;
_5 = <Box<Data> as Drop>::drop(move _4) -> bb3;
}
bb2 (cleanup): {
resume;
}
bb3: {
return;
}
bb4 (cleanup): {
_6 = &mut _1;
_7 = <Box<Data> as Drop>::drop(move _6) -> [return: bb2, unwind terminate];
}
}
My question: would it be a valid optimization to remove the temporary place and instead directly call consume(&move *data)
? [context]
Reasons for:
- The boxed place, by virtue of being passed as a
Box
, is known to be uniquely accessible byfn entry
. - As such, no other code has a valid pointer which would alias the potentially reused place.
- Eliding the copy would be a desirable optimization for address insensitive data. Which, given the lack of
Pin
involved, SHOULD be the case.
Reasons against:
- Opsem doesn't care about SHOULD.
- The boxed place is semantically a distinct place from the temporary place.
- The boxed place (despite being unused and inaccessible) is still live across the region where the temporary place is live.
- Code could observe the address colocation of the two live places if it knows the address of the boxed place.
Obviously, being able to prove more about the code (i.e. that the address of one or the other place isn't observed) would permit this copy elision optimization. I'm specifically curious about the know-nothing case.
If we categorically forbid live places from (observably) having the same address, then I believe this optimization would indeed be incorrect. If we can't perform this optimization automatically, that could serve as partial motivation for the introduction of a surface level &move
which would permit such (well-scoped) place colocation.
(This is not the place to discuss the exact semantics of &move
. Just to determine if something like &move
would be required to do this optimization of logical ownership transfer but concrete place reuse.)