Skip to main content

Module tail_copy_to_move

Module tail_copy_to_move 

Source
Expand description

Rewrite final-use copies before return into moves.

§The problem

MIR building represents reads of values whose type is Copy using Operand::Copy, including when such a local is returned. If that local’s address has ever been observed, MoveElimination conservatively considers it live until its StorageDead or function exit. This keeps the local live across _0 = copy local, so its live range overlaps with the return place and MoveElimination cannot unify the source local with _0.

§The solution

At function return, all local allocations are about to become invalid anyway. After borrowck, this pass can therefore turn a final-use Copy into a Move, as long as shortening the source local’s live range has no observable effect before the return happens. Concretely, between the transformed copy (now a move) and the return, there may only be writes to unborrowed locals, storage markers, nops, and gotos.

§The algorithm

Start from every Return terminator, with _0 treated as used by the return. Then scan predecessor blocks backward through Goto edges, forming a return-tail tree. The scan maintains used_after, the set of locals accessed later on that path.

A Copy operand is rewritten to a Move when its base local is not in used_after. Then any locals touched by that operand, including index-projection locals, are added to used_after before the backward scan continues.

The scan stops when accessing an indirect place because that may access any borrowed local, which would make the pass unable to prove any useful final uses. It also stops at writes to borrowed locals, because those can create a new address-observed allocation range whose overlap with an earlier borrowed local must be preserved.

Structs§

TailCopyToMove 🔒
TailState 🔒

Functions§

process_operand 🔒
Process one operand in an rvalue.
process_rvalue 🔒
Process the RHS of an assignment in a pure return tail.
record_place_locals 🔒
Records all locals used in a place, including Index projections in used_after.
scan_block 🔒
Scan a block backward while the return-tail invariant still holds.