3

Implement SSA-based reference propagation by cjgillot · Pull Request #106285 · r...

 1 year ago
source link: https://github.com/rust-lang/rust/pull/106285
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Implement SSA-based reference propagation #106285

Conversation

Contributor

Rust has a tendency to create a lot of short-lived borrows, in particular for method calls. This PR aims to remove those short-lived borrows with a const-propagation dedicated to pointers to local places.

This pass aims to transform the following pattern:

  _1 = &raw? mut? PLACE;
  _3 = *_1;
  _4 = &raw? mut? *_1;
  _1 = &raw? mut? PLACE;
  _3 = PLACE;
  _4 = &raw? mut? PLACE;

where PLACE is a direct or an indirect place expression.

By removing indirection, this pass should help both dest-prop and const-prop to handle more cases.
This optimization is distinct from const-prop and dataflow const-prop since the borrow-reborrow patterns needs to preserve borrowck invariants, especially the uniqueness property of mutable references.

The pointed-to places are computed using a SSA analysis. We suppose that removable borrows are typically temporaries from autoref, so they are by construction assigned only once, and a SSA analysis is enough to catch them. For each local, we store both where and how it is used, in order to efficiently compute the all-or-nothing property. Thanks to Derefer, we only have to track locals, not places in general.


There are 3 properties that need to be upheld for this transformation to be legal:

  • place constness: PLACE must refer to the same memory wherever it appears;
  • pointer liveness: we must not introduce dereferences of dangling pointers;
  • &mut borrow uniqueness.

Constness

If PLACE is an indirect projection, if its of the form (*LOCAL).PROJECTIONS where:

  • LOCAL is SSA;
  • all projections in PROJECTIONS are constant (no dereference and no indexing).

If PLACE is a direct projection of a local, we consider it as constant if:

  • the local is always live, or it has a single StorageLive that dominates all uses;
  • all projections are constant.

Liveness

When performing a substitution, we must take care not to introduce uses of dangling locals.

Using a dangling borrow is UB. Therefore, we assume that for any use of *x, where x is a borrow, the pointed-to memory is live.

Limitations:

  • occurrences of *x in an &raw mut? *x are accepted;
  • raw pointers are allowed to be dangling.

In those 2 case, we do not substitute anything, to be on the safe side.

Open question: we do not differentiate borrows of ZST and non-ZST. The UB rules may be
different depending on the layout. Having a different treatment would effectively prevent this
pass from running on polymorphic MIR, which defeats the purpose of MIR opts.

Uniqueness

For &mut borrows, we also need to preserve the uniqueness property:
we must avoid creating a state where we interleave uses of *_1 and _2.
To do it, we only perform full substitution of mutable borrows:
we replace either all or none of the occurrences of *_1.

Some care has to be taken when _1 is copied in other locals.

   _1 = &raw? mut? _2;
   _3 = *_1;
   _4 = _1
   _5 = *_4

In such cases, fully substituting _1 means fully substituting all of the copies.

For immutable borrows, we do not need to preserve such uniqueness property,
so we perform all the possible substitutions without removing the _1 = &_2 statement.

mati865, terrarier2111, compiler-errors, memoryruins, juntyr, lqd, Virgiel, Rageking8, mominul, wesleywiser, and 3 more reacted with heart emoji

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK