Ken Wakita (https://wakita.github.io/fp2018/)
Nov. 1, 2018
let rec iter n e = (* Iterative optimization *)
if n = 0 then e else
let e' = Elim.f (ConstFold.f (Inline.f (Assoc.f (Beta.f e)))) in
if e = e' then e else
iter (n - 1) e'
let lexbuf outchan l =
...... .......
(..........
(......
(.........
(.........
(iter !limit
(.......
(.........
(........
(.......... ........... .)))))))))
\[\begin {align} {\cal F}&: \text {KNormal.t M.t} \rightarrow \text {KNormal.t} \rightarrow \text {KNormal.t} \\ \varepsilon&: v \mapsto e \\ {\cal F}_\varepsilon(e) &= e' \end {align}\]
\[\begin {align} {\cal F}_\varepsilon(1 + 2) &\Rightarrow 3 \\ \text {let } x = 5 \text { in } x + x &\Rightarrow 10 \\ {\cal F}_\varepsilon(\text {if } x = y \text { then } e_1 \text { else } e_2) &\Rightarrow {\cal F}_\varepsilon(e_1) \quad \varepsilon(x) \equiv \varepsilon(y) \\ \end {align}\]
# constfold constfold_p;;
Let (Ti3.4,
Let (x.5, 1, Let (y.6, 2,
Let (Ti2.7, Let (Ti1.8, 1, 1),
IfEq (Ti2.7, x.5, 3, 2)))), ...)
Constant folding has reduced x + y
to 3 and y
to 2 but there seems to be room for more optimization.
What if combined with let
-associativity reduction?
# ConstFold.f (assoc constfold_p);;
Let (x.13, 1,
Let (y.14, 2,
Let (Ti9.16, 1,
Let (Ti10.15, 1,
Let (Ti11.12, 3, ...)))))
Now the if
expression and the else
clause of the if
expression has disappeared.
\[\begin {align} {\cal F}_\varepsilon(\text {let } x = e_1 \text { in } e_2) &= \text {let } x = {\cal F}_\varepsilon(e_1) \text { in } {\cal F}_{\varepsilon'}(e_2) \\ & \text {where } \varepsilon' = {\varepsilon, x \mapsto {\cal F}_\varepsilon(e_1)} \\ & \\ {\cal F}_\varepsilon(\mathit {op}(x_1,\ldots,x_n)) &= c \quad \text {if } \mathit {op}(\varepsilon(x_1), \ldots, \varepsilon(x_n)) = c \end {align}\]
let findi x env = (match M.find x env with Int(i) -> i | _ -> raise Not_found)
...
let rec g env = function (* The body of the constant folding routine *)
| ...
| Neg(x) when memi x env -> Int(-(findi x env))
| ...
| Let((x, t), e1, e2) -> (* Case "let" *)
let e1' = g env e1 in
let e2' = g (M.add x e1' env) e2 in
Let((x, t), e1', e2')
| ...
\[\begin {align} \varepsilon &: \text {KNormal.t} \rightarrow \text {KNormal.t} \\ \varepsilon(e) &= e' \\ \end {align}\]
\[\begin {align} \mathit {effect} &: \text {KNormal.t} \rightarrow \text {bool} \\ \mathit {effect}(e) &= \text {true/false} \\ \mathit {effect}(1) &= \text {false} \\ \mathit {effect}(\text {println(1)}) &= \text {true} \\ \mathit {effect}(\text {array.(0) <- 0}) &= \text {true} \qquad \text {# Array assignment} \end {align}\]
\[\begin {align} \varepsilon(\text {let x = 1 in 0}) &\Rightarrow 0 \\ \varepsilon(\text {let x = 1 in x + 1}) &\Rightarrow \text {let x = 1 in x + 1} \end {align}\]
\[\begin {align} \varepsilon(\text {let rec f x = 1 in 0}) &\Rightarrow 0 \\ \varepsilon(\text {let rec f x = x + 1 in f 0}) &\Rightarrow \text {let f x = x + 1 in f 0} \end {align}\]
\[\begin {align} \varepsilon(\text {let } x = f() \text { in } 0) &= \ ??? \\ \\ ^*\text {case 1} &: \text {f() = 1} \quad\text {// effect free} \\ ^*\text {case 2} &: \text {f() = y} \quad\text {// effect free} \\ \text {case 3} &: \text {f() = array.(0) <- 0} \quad\text {// Assignment}\\ \text {case 4} &: \text {f() = println("Effect!")} \quad\text {// Input-Output}\\ \end {align}\]
Why the let rec
rule in RDE definition says
\[ \mathit {effect}(\text {let rec } x\ y_1\ \ldots\ y_n = e_1 \text { in } e_2) = \mathit {effect}(e_2) \]
but not
\[ \mathit {effect}(\text {let rec } x\ y_1\ \ldots\ y_n = e_1 \text { in } e_2) = \mathit {effect}(e_1) \vee \mathit {effect}(e_2) \]
let rec effect = function (* detection of side effects *)
| Let(_, e1, e2) -> effect e1 || effect e2
| LetRec(_, e)
| LetTuple(_, _, e) -> effect e
| IfEq(_, _, e1, e2) | IfLE(_, _, e1, e2) -> effect e1 || effect e2
| App _ | ExtFunApp _
| Put _ -> true
| _ -> false
Naming constructs (Let*
) have no side effects
Conditionals’ side effects depends on those of their subcomponents
ExtFunApp
: external function application, alsoAssignments have side effects
Others are free of side effects
For let
:
\[\begin {align} \varepsilon(\text {let } &x = e_1 \text { in } e_2) = \\ & \begin {cases} \varepsilon(e_2) & \neg \mathit {effect}(\varepsilon(e_1)) \wedge\\ & x \not\in \mathit {free\_variables}(\varepsilon(e_2)) \\ \text {let } x = \varepsilon(e_1) \text { in } \varepsilon(e_2) & \text {Otherwise} \end {cases} \end {align}\]
For let rec
:
\[\begin {align} \varepsilon(&\text {let rec } x\ y_1\ \ldots\ y_n = e_1 \text { in } e_2) = \\ & \begin {cases} \varepsilon(e_2) & \text {When } x \not\in \mathit {free\_variables}(\varepsilon(e_2)) \\ \text {let rec } x\ y_1\ \ldots\ y_n = \varepsilon(e_1) \text { in } \varepsilon(e_2) \\ & \text {Otherwise} \end {cases} \end {align}\]
What’s gone
let rec
: locally-defined functionsWhat’s new
Top-level function declarations:
\(D = \{ L_{\text {fib}}(n)() = \text {if n <= 1 then 1 else fib (n - 1) + fib (n - 2)} \}\)
make_closure
What’s different