Intermediate Subexpression Elimination
In this step we add intermediate variables for every subexpression.
Specifically, if an expression that isn’t a simple binding can be written expr!($subexpr),
we rewrite it.
If $subexpr is a value expression,
we rewrite it to { let tmp = $subexpr; expr!(tmp) };
if it is a place expression,
we rewrite it to { let place tmp = $subexpr; expr!(tmp) };
We do this in an order that preserves normal left-to-right evaluation order. We skip subexpressions that are constants.
#![allow(unused)]
fn main() {
let mut vec = Vec::new();
vec.push(42);
vec[0] += 1;
// becomes before this step:
(*<Vec<_> as DerefMut>::deref_mut(&mut vec))[0] += 1;
// becomes after this step:
{
let tmp1 = &mut vec;
let tmp2 = <Vec<_> as DerefMut>::deref_mut(tmp1);
*tmp2 += 1;
}
}
#![allow(unused)]
fn main() {
let x = 1 + 2 + Some(3).as_ref().unwrap();
// becomes, before this step:
let x = {
let tmp1 = Some(3);
1 + <u32 as Add<&u32>>::add(2, Option::unwrap(Option::as_ref(&tmp1)))
};
// becomes, after this step:
let x = {
let tmp1 = Some(3);
let tmp2 = &tmp1;
let tmp3 = Option::as_ref(tmp2);
let tmp4 = Option::unwrap(tmp3);
let tmp5 = <u32 as Add<&u32>>::add(2, tmp4);
1 + tmp5
};
}
An example that will be important for Bounds Checks:
#![allow(unused)]
fn main() {
expr!($place[$i][$j])
// becomes
{
let place p = $place;
let i = $i;
let place q = p[i]; // order is important because the bound check happens here
let j = $j;
let place r = q[j];
expr!(r)
}
}
At the end of this step, every value context contains either a constant or a variable and every place expression is non-nested.
Discussion
We don’t really have to skip constants. I just did it that way because MIR allows constants a operands. I think it’s a practical matter of not ending up with billions of variables.
Backlinks