* Closes#3028
* Detects fully-applied expressions of the form
```
f a (\{ x1 .. xk := body }) a'
```
where `f` is a recursive function which does not modify its function
argument (the one for which a lambda is provided), i.e., passes it to
recursive invocations unchanged (it's the second argument in the above
example). A function `f` satisfying this requirement implements some
kind of loop with the lambda-expression usually being the loop body /
action.
Let e.g. `body = g x1 .. xk b1 b2` where `b1`, `b2` do not contain any
variables bound in the lambda (`x1`,..,`xk`). If `b1`, `b2` are
non-trivial (require computation), then these loop-invariant
subexpressions are hoisted outside of the loop, obtaining:
```
let
y1 := b1;
y2 := b2;
in
f a (\{ x1 .. xk := g x1 .. xk y1 y2) a'
```
* Adds a volatility info in Core which prevents the lets created by
loop-invariant expression hoisting from being folded back.
* Removes potential duplication in the specialization optimization.
* Adds another optimization phase before lambda-lifting where hoisting
of loop-invariant subexpressions is performed. This allows to reduce the
number of iterations in the main optimization phase after
lambda-lifting.