225 lines
7.9 KiB
Plaintext
225 lines
7.9 KiB
Plaintext
%if false
|
|
Copyright (c) 2009, ETH Zurich.
|
|
All rights reserved.
|
|
|
|
This file is distributed under the terms in the attached LICENSE file.
|
|
If you do not find this file, copies can be found by writing to:
|
|
ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
|
|
%endif
|
|
|
|
%include polycode.fmt
|
|
|
|
%if false
|
|
|
|
> module IL.Paka.Optimizer where
|
|
|
|
> import Data.List
|
|
> import qualified Data.Map as Map
|
|
|
|
> import IL.Paka.Syntax
|
|
> import IL.Paka.Compile
|
|
|
|
%endif
|
|
|
|
\section{@IL.Paka@ Code Optimizer}
|
|
\label{sec:il_paka_optimizer}
|
|
|
|
The currently implemented optimizer is a naive redundant assignment
|
|
simplifier, which happens to do constant propagation at the same
|
|
time. It is naive in the several dimensions. An important one is that
|
|
it is entirely hard-coded, while we all know that optimization is
|
|
simply a matter of dataflow analysis. So, at some point, we should use
|
|
a more generic framework for that. It is also naive because it does
|
|
not try to reach a fix-point: it is single phase, while it is obvious
|
|
that more assignments could still be eliminated in subsequent
|
|
phases. Finally, it is naive because any case that was not easy to
|
|
deal with have been discarded: more redundant assignments could be
|
|
removed if the logic were more precise.
|
|
|
|
The purpose of that module is to show that ``it is possible to do
|
|
optimization''. It is a proof of concept. Now, it is Future Work
|
|
(Chapter~\ref{chap:future_work}) to get a clever optimization
|
|
framework. The ease I had in implementing that stuff convince me that
|
|
we are not far from this heaven.
|
|
|
|
So, if you want optimized Paka code, you will only get a slightly less
|
|
redundant code:
|
|
|
|
> optimizePaka :: PakaCode -> PakaCode
|
|
> optimizePaka = optimizeAssgmtElim
|
|
|
|
Because this analysis is intra-procedural, we go over each function
|
|
and apply an intra-procedural optimizer:
|
|
|
|
> optimizeAssgmtElim :: PakaCode -> PakaCode
|
|
> optimizeAssgmtElim code = code { functions = optFunc }
|
|
> where funcs = functions code
|
|
> optFunc = Map.mapMaybe (\(b,c,d,e,f,fun) -> Just (b,c,d,e,f, assgmtElim fun)) funcs
|
|
|
|
\subsection{Implementation}
|
|
|
|
This optimizer is quite easy to implement, assuming we have the right
|
|
tools at hand. That is, assuming that we are able to replace a
|
|
variable |x| by a variable |y| in a code |k| -- using
|
|
|replace (Var x) (Var y) k| --, that we are able to say if a
|
|
variable |y| is either a constant or never used in a code |k| -- using
|
|
|isUsed flatten y k| --, and that we are able to say if a variable |x|
|
|
is used without side-effects in a code |k| --
|
|
using |isUsed flattenS x k|.
|
|
|
|
The intra-procedural optimizer will turn an |ILPaka| into a better
|
|
|ILPaka|:
|
|
|
|
> assgmtElim :: ILPaka -> ILPaka
|
|
|
|
The interesting case is obviously the variable assignment: a value
|
|
|y| is assigned to a variable |x|. We remove that assignment and
|
|
replace |x| by |y| if and only if |y| is never used again and |x| is
|
|
not involved in some weird computation. Otherwise, we go ahead.
|
|
|
|
A small issue here is that we ask for |y| to be never used
|
|
again. That's quite restrictive. This results in being able to carry
|
|
only 4 assignment eliminations on today's Hamlet and Fugu inputs. This
|
|
is shame, compared to the numerous opportunities. To solve that issue,
|
|
we would have to extend or re-design |isUsed| to allow the definition
|
|
of more fine-grained predicates, such as ``is overwritten''.
|
|
|
|
> assgmtElim (PStatement a@(PAssign (Var x) _ [Var y]) k) =
|
|
> if (not (isUsed flatten y k))
|
|
> && (not (isUsed flattenS x k)) then
|
|
> assgmtElim $ replace (Var x) (Var y) k
|
|
> else
|
|
> PStatement a $
|
|
> assgmtElim k
|
|
|
|
All other assignments that do not fit this scheme, or the
|
|
instructions are skipped:
|
|
|
|
> assgmtElim (PStatement a k) =
|
|
> PStatement a $
|
|
> assgmtElim k
|
|
|
|
Finally, control-flow operators are simply iterated over:
|
|
|
|
> assgmtElim (PIf c t ifT ifF k) =
|
|
> PIf
|
|
> (assgmtElim c)
|
|
> t
|
|
> (assgmtElim ifT)
|
|
> (assgmtElim ifF)
|
|
> (assgmtElim k)
|
|
> assgmtElim (PWhile c t l k) =
|
|
> PWhile
|
|
> (assgmtElim c)
|
|
> t
|
|
> (assgmtElim l)
|
|
> (assgmtElim k)
|
|
> assgmtElim (PDoWhile l c t k) =
|
|
> PDoWhile
|
|
> (assgmtElim l)
|
|
> (assgmtElim c)
|
|
> t
|
|
> (assgmtElim k)
|
|
> assgmtElim (PSwitch t cases d k) =
|
|
> PSwitch
|
|
> t
|
|
> (map (\(a,b) -> (a, assgmtElim b)) cases)
|
|
> (assgmtElim d)
|
|
> (assgmtElim k)
|
|
> assgmtElim x = x
|
|
|
|
\subsection{Code predication}
|
|
|
|
First, if I correctly remember my Software Testing lecture, a
|
|
\emph{use site} is a place where a variable is read. In opposition to
|
|
a \emph{def site} where a variable is written to. Well, then the
|
|
following is misleading.
|
|
|
|
|isUsed f x k| tells you that |x| has been found in a use or def site
|
|
of |k| in a situation where it played a role caught by |f|. To
|
|
simplify, |isUsed flatten| will catch any kind of use or def. |isUsed
|
|
flattenS| will catch a use or def in a |Complex| state.
|
|
|
|
As for the implementation, it is simply going over |ILPaka| terms and
|
|
doing the necessary on |PStatement|.
|
|
|
|
> isUsed :: (PakaVarName -> Maybe String) -> String -> ILPaka -> Bool
|
|
> isUsed p var PVoid = False
|
|
> isUsed p var (PClosing (PReturn k)) = Just var == (flatten $ pakaValName k)
|
|
> isUsed p var (PClosing _) = False
|
|
> isUsed p var (PStatement s k) = isUsedStmt s || isUsed p var k
|
|
> where isUsedStmt (PAssign t _ ls) =
|
|
> Just var `elem` map flatten (t : ls)
|
|
> isUsedStmt (PInstruction _ ls) =
|
|
> Just var `elem` map flatten ls
|
|
> isUsed p var (PIf c t ifT ifF k)
|
|
> = (Just var == (flatten $ pakaValName t)) ||
|
|
> (isUsed p var c || isUsed p var ifT
|
|
> || isUsed p var ifF || isUsed p var k)
|
|
> isUsed p var (PWhile c t l k)
|
|
> = (Just var == (flatten $ pakaValName t)) ||
|
|
> isUsed p var c || isUsed p var l || isUsed p var k
|
|
> isUsed p var (PDoWhile l c t k)
|
|
> = (Just var == (flatten $ pakaValName t)) ||
|
|
> isUsed p var c || isUsed p var l || isUsed p var k
|
|
> isUsed p var (PSwitch t c d k)
|
|
> = (Just var == (flatten $ pakaValName t)) ||
|
|
> foldl' (\a (_,b) -> a || isUsed p var b) False c
|
|
> || isUsed p var d || isUsed p var k
|
|
|
|
In light of the explanation above, the definition of |flatten| and
|
|
|flattenS| should be obvious. Aren't they?
|
|
|
|
> flatten :: PakaVarName -> Maybe String
|
|
> flatten (Var s) = Just $ s
|
|
> flatten (Ptr x) = flatten x
|
|
> flatten (Deref x) = flatten x
|
|
> flatten (Complex x) = flatten x
|
|
> flatten (K _) = Nothing
|
|
>
|
|
> flattenS :: PakaVarName -> Maybe String
|
|
> flattenS (Var s) = Nothing
|
|
> flattenS (Ptr x) = Nothing
|
|
> flattenS (Deref x) = Nothing
|
|
> flattenS (Complex x) = flatten x
|
|
> flattenS (K _) = Nothing
|
|
|
|
\subsection{Code transformation}
|
|
|
|
As for |replace|, it is by now standard: go over the terms, hunt the
|
|
|dest|, and kill it with |source|. It is surgical striking, in its
|
|
full glory.
|
|
|
|
> replace :: PakaVarName -> PakaVarName -> ILPaka -> ILPaka
|
|
> replace dest source (PStatement (PAssign dst stmt srcs) k) =
|
|
> PStatement (PAssign dst stmt srcs')
|
|
> (replace dest source k)
|
|
> where srcs' = replaceL dest source srcs
|
|
> replace dest source (PStatement (PInstruction stmt srcs) k) =
|
|
> PStatement (PInstruction stmt srcs')
|
|
> (replace dest source k)
|
|
> where srcs' = replaceL dest source srcs
|
|
> replace dest source (PIf c t ifT ifF k) =
|
|
> PIf (replace dest source c) t
|
|
> (replace dest source ifT)
|
|
> (replace dest source ifF)
|
|
> (replace dest source k)
|
|
> replace dest source (PWhile c t l k) =
|
|
> PWhile (replace dest source c)
|
|
> t
|
|
> (replace dest source l)
|
|
> (replace dest source k)
|
|
> replace dest source (PDoWhile l c t k) =
|
|
> PDoWhile (replace dest source l)
|
|
> (replace dest source c)
|
|
> t
|
|
> (replace dest source k)
|
|
> replace dest source (PSwitch t cases d k) =
|
|
> PSwitch t
|
|
> (map (\(a,b) -> (a, replace dest source b)) cases)
|
|
> (replace dest source d)
|
|
> (replace dest source k)
|
|
> replace dest source x = x
|
|
>
|
|
> replaceL x y = map (\z -> if z == x then y else z)
|