module GcodeOpt1(gcodeOpt1) where
import Gcode
import GcodeLow(gcodeStack,primStack)
import AssocTree
import Util.Extra
import IntState
import Prim
import Maybe
gcodeOpt1 state gcode = (fixOne state gcode,state)
fixOne state [] = []
fixOne state (g@(STARTFUN pos fun):gs) =
let arity = arityIS state (toEnum fun)
in case gOpt initAT (OptDown (arity+1) (map (`pair` False) [1 .. arity]) []) gs of
OptUp at gs -> g:gs
fixOne state (g:gs) =
g : fixOne state gs
data OptDown =
OptDown Int [(Int,Bool)] [(Int,Bool)] deriving (Show)
data OptUp =
OptUp
(AssocTree Int (Int,Maybe Gcode)) -- Mapping from label to (new destiny,code at destiny)
[Gcode] -- Fixed gcode
updST st (OptDown _ eargs estack) = OptDown st eargs estack
getST (OptDown st eargs estack) = st
bothOf (OptDown st eargs estack) (OptDown st' eargs' estack') = OptDown st (zipWith both eargs eargs') (zipWith both estack estack')
where both (u,e) (u',e') = (u,e && e')
pushNews :: Int -> OptDown -> OptDown
pushNews i (OptDown st eargs estack) = OptDown (st+i) eargs (map (`pair`False) [st .. st+i-1] ++ estack)
pushNew :: Bool -> OptDown -> OptDown
pushNew e down@(OptDown st eargs estack) = OptDown (st+1) eargs ((st,e):estack)
pushArg :: Int -> OptDown -> OptDown
pushArg i (OptDown st eargs estack) = OptDown st eargs ((eargs !! (i-1)) : estack)
push :: Int -> OptDown -> OptDown
push i (OptDown st eargs estack) = OptDown st eargs ((estack !! i) : estack)
pop :: Int -> OptDown -> OptDown
pop i (OptDown st eargs estack) = OptDown st eargs (drop i estack)
slide :: Int -> OptDown -> OptDown
slide i (OptDown st eargs (tos:estack)) = OptDown st eargs (tos:drop i estack)
unpack :: Int -> OptDown -> OptDown
unpack i (OptDown st eargs (_:estack)) = OptDown (st+i) eargs (map (`pair`False) [st .. st+i-1] ++ estack)
eval :: OptDown -> OptDown
eval (OptDown st eargs ((u,_):estack)) = OptDown st (map (update u) eargs) ((u,True) : map (update u) estack)
where update u (u',e') = (u', e' || u == u')
eval (OptDown st eargs ([])) = error "eval "
qEval down@(OptDown st eargs ((_,e):estack)) = e
addDown at label inline value = addAT at comb label (inline,value)
where comb (inline,evals@(OptDown st eargs estack)) (inline',evals'@(OptDown st' eargs' estack')) =
(if length estack /= length estack' then strace ("Different depths for " ++ show label) else id) $
(inline && inline',bothOf evals evals')
addUp at label value = addAT at (sndOf . strace ("redefinition of " ++ show label)) label value
maybeG (RETURN:_) = Just RETURN
maybeG (RETURN_EVAL:_) = Just RETURN_EVAL
maybeG _ = Nothing
justG e jg (Just RETURN_EVAL) gs = (if e then RETURN else RETURN_EVAL) : gs
justG e jg (Just g) gs = g : gs
justG e jg Nothing gs = jg : gs
gOpt0 :: (AssocTree Int (Bool,OptDown)) -> Int -> [Gcode] -> OptUp
gOpt0 at st [] = OptUp initAT []
gOpt0 at st (g@(ALIGN_CONST):gs) = OptUp initAT (g:gs) -- only data declarations
gOpt0 at st (g@(LABEL i):gs) =
case lookupAT at i of
Nothing -> -- Unused label
gOpt0 at st gs
Just (inline,evals) ->
case (skipGs gs) of
(JUMP j:gs) -> case gOpt0 (addDown at j inline evals) st gs of
OptUp at gs ->
case lookupAT at j of
Just value -> OptUp (addUp at i value) gs
Nothing -> error ("LABEL JUMP (in gOpt0) couldn't find " ++ show j)
_ -> gOpt at (updST st evals) (g:gs) -- Simplest to use code for the fall through case in gOpt
gOpt0 at st (g:gs) =
gOpt0 at st gs
skipGs (POP 0:gs) = skipGs gs
skipGs (SLIDE 0:gs) = skipGs gs
skipGs gs = gs
endGs (LABEL i:gs) = endGs gs
endGs (SLIDE i:gs) = endGs gs
endGs (RETURN:gs) = Just RETURN
endGs (RETURN_EVAL:gs) = Just RETURN_EVAL
endGs gs = Nothing
next at evals g gs = case gOpt at evals gs of OptUp at gs -> OptUp at (g:gs)
next0 at evals g gs = case gOpt0 at (getST evals) gs of OptUp at gs -> OptUp at (g:gs)
gOpt :: (AssocTree Int (Bool,OptDown)) -> OptDown -> [Gcode] -> OptUp
-- Detect end of code
gOpt at evals [] = OptUp initAT []
gOpt at evals (g@(ALIGN_CONST):gs) = OptUp initAT (g:gs)
-- First some easy peep hole optimisations
{---------- DAVID ---------
gOpt at evals (MATCHCON : JUMPS_T : JUMPTABLE l : LABEL l' : gs) = gOpt at evals gs -- Must be pattern match on type with only one constructor
---------- DAVID --------- -}
gOpt at evals (TABLESWITCH 1 _ [_] : LABEL _ : gs) = gOpt at evals gs -- DAVID
gOpt at evals (SLIDE 0:gs) = gOpt at evals gs
gOpt at evals (SLIDE i:SLIDE j:gs) = gOpt at evals (SLIDE (i+j):gs)
gOpt at evals (SLIDE i:g@RETURN:gs) = gOpt at evals (g:gs)
gOpt at evals (SLIDE i:g@RETURN_EVAL:gs) = gOpt at evals (g:gs)
gOpt at evals (g@(SLIDE i):gs) =
case gOpt at (slide i evals) gs of
OptUp upat gs -> OptUp upat (justG (qEval evals) g (endGs gs) gs)
--gOpt at evals (NEEDHEAP i:NEEDSTACK j:gs) = gOpt at evals (NEEDHEAP (i+j):gs) -- MW
gOpt at evals (POP 0:gs) = gOpt at evals gs
gOpt at evals (POP i:POP j:gs) = gOpt at evals (POP (i+j):gs)
gOpt at evals (g@(POP i):gs) = next at (pop i evals) g gs
gOpt at evals (UNPACK 0:gs) = gOpt at evals (POP 1:gs)
gOpt at evals (g@(UNPACK i):gs) = next at (unpack i evals) g gs
-- Skip unnecessary evals
gOpt at evals (g@RETURN:gs) = next0 at evals g gs
gOpt at evals (g@RETURN_EVAL:gs) =
if qEval evals then next0 at evals RETURN gs
else next0 at evals g gs
gOpt at evals (g@EVAL:gs) =
if qEval evals then gOpt at evals gs
else next at (eval evals) g gs
gOpt at evals (g@EVALUATED:gs) = -- GcodeFix guarantee that tos is evaluated, so update our evals-info.
gOpt at (eval evals) gs
-- this eval is always necessary
gOpt at evals (g@SELECTOR_EVAL:gs) = next at (eval (pushArg 1 evals)) g gs
-- Keep track of jumps
gOpt at evals (g@(LABEL i):gs) = -- fall through label
case lookupAT at i of
Nothing -> -- unused label, but fall through so skip only the label
gOpt at evals gs
Just (inline',evals') ->
case gOpt at (bothOf evals evals') gs of
OptUp at gs ->
case gs of
JUMP j:_ ->
case lookupAT at j of
Just value -> OptUp (addUp at i value) gs -- short cut jump
Nothing -> error ("gOpt LABEL JUMP can not find " ++ show j)
gs | inline' && isJust (maybeG gs) -> OptUp (addUp at i (i,maybeG gs)) gs -- remove label, inline everywhere
_ -> OptUp (addUp at i (i,maybeG gs)) (g:gs)
gOpt at evals (g@(JUMP i):gs) =
case gOpt0 (addDown at i True evals) (getST evals) gs of
OptUp upat gs ->
case lookupAT upat i of
Just (i',mg) -> OptUp upat (justG (qEval evals) (JUMP i') mg gs)
Nothing -> error ("gOpt JUMP couldn't find " ++ show i)
gOpt at evals (g@(JUMPFALSE i):gs) = -- DAVID
let evals' = pop 1 evals
in case gOpt (addDown at i False evals') evals' gs of
OptUp at gs ->
case lookupAT at i of
Just (i',_) -> OptUp at (JUMPFALSE i':gs) -- DAVID
Nothing -> error ("gOpt JUMPFALSE couldn't find " ++ show i)
gOpt at evals (TABLESWITCH size pad ls:gs) = -- DAVID
let nat = foldr (\l tr -> addDown tr l False evals) at ls in
case gOpt0 nat (getST evals) gs of
OptUp at gs ->
OptUp at (TABLESWITCH size pad (map (gLab at) ls):gs)
where
gLab at l =
case lookupAT at l of
Just (l',_) -> l'
Nothing -> error ("gOpt TABLESWITCH couldn't find " ++ show l)
gOpt at evals (LOOKUPSWITCH size pad tls def:gs) = -- DAVID
let (tgs,ls) = unzip tls in
let xls = def : ls in
let nat = foldr (\l tr -> addDown tr l False evals) at xls in
case gOpt0 nat (getST evals) gs of
OptUp at gs ->
OptUp at (LOOKUPSWITCH size pad (zip tgs (map (gLab at) ls))
(gLab at def):gs)
where
gLab at l =
case lookupAT at l of
Just (l',_) -> l'
Nothing -> error ("gOpt LOOKUPSWITCH couldn't find " ++ show l)
{--------- DAVID ------------
gOpt at evals (gj@JUMPS_T:gs) =
gOpt' at [] gs
where
gOpt' at ls (g@(JUMPTABLE l):gs) = gOpt' (addDown at l False evals) (g:ls) gs
gOpt' at ls gs =
case gOpt0 at (getST evals) gs of
OptUp at gs ->
gOpt'' at gs ls
gOpt'' at gs [] = OptUp at (gj:gs)
gOpt'' at gs (JUMPTABLE l:ls) =
case lookupAT at l of
Just (l',_) -> gOpt'' at (JUMPTABLE l':gs) ls
Nothing -> error ("gOpt JUMPS_T couldn't find " ++ show l)
gOpt at evals (gj@JUMPS_L:(JUMPLENGTH s gll):gs) =
gOpt' (addDown at gll False evals) [] gs
where
gOpt' at ls (g@(JUMPLIST v l):gs) = gOpt' (addDown at l False evals) (g:ls) gs
gOpt' at ls gs =
case gOpt0 at (getST evals) gs of
OptUp at gs ->
case lookupAT at gll of
Just (gll',_) -> gOpt'' (JUMPLENGTH s gll') at gs ls
Nothing -> error ("gOpt JUMPS_L (1) couldn't find " ++ show gll)
gOpt'' gl at gs [] = OptUp at (gj:gl:gs)
gOpt'' gl at gs (JUMPLIST v l:ls) =
case lookupAT at l of
Just (l',_) -> gOpt'' gl at (JUMPLIST v l':gs) ls
Nothing -> error ("JUMPS_L (2) couldn't find " ++ show l)
---------- DAVID ---------- -}
-- misc stack manipulating instructions
gOpt at evals (g@PRIMITIVE:gs) = next at (pushNew False evals) g gs
gOpt at evals (g@(PUSH_ARG i):gs) = next at (pushArg i evals) g gs
gOpt at evals (g@(PUSH i):gs) = next at (push i evals) g gs
gOpt at evals (g@(SELECT i):gs) = next at (pushNew False (pop 1 evals)) g gs
gOpt at evals (g@(APPLY i):gs) = next at (pushNew False (pop (i+1) evals)) g gs
-- Instructions that push evaluated things onto the stack
gOpt at evals (g@(PUSH_INT i):gs) = next at (pushNew True evals) g gs
gOpt at evals (g@(PUSH_CHAR i):gs) = next at (pushNew True evals) g gs
gOpt at evals (g@(PUSH_INTEGER i):gs) = next at (pushNew True evals) g gs
gOpt at evals (g@(PUSH_FLOAT i):gs) = next at (pushNew True evals) g gs
gOpt at evals (g@(PUSH_DOUBLE i):gs) = next at (pushNew True evals) g gs
-- Inbuilt primitive that returns unevaluated value
gOpt at evals (g@(PRIM SEQ):gs) = next at (pop 1 evals) g gs
-- Inbuilt primitivs that returns evaluated value
gOpt at evals (g@(PRIM prim):gs) =
case primStack prim of
id ->
if id <= 0 then next at (eval (pop (-id) evals)) g gs
else error ("gOpt at: primitive that increases the stack!" ++ strPrim prim)
-- Everything else, if it push assume unevaluated
gOpt at evals (g:gs) =
case gcodeStack g of
to_push ->
if to_push <= 0 then next at (pop (-to_push) evals) g gs
else next at (pushNews to_push evals) g gs
|