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