packages feed

oplang-0.1.0.0: src/Language/OpLang/Optimizer.hs

module Language.OpLang.Optimizer(optimize) where

import Data.List((\\), union)

import qualified Data.HashMap.Strict as HashMap

import Language.OpLang.AST

canDoWithOffset :: Op -> Bool
canDoWithOffset (Move _) = False
canDoWithOffset (Loop _) = False
canDoWithOffset (OpCall _) = False
canDoWithOffset TailCall = False
canDoWithOffset _ = True

optimizeOnce :: Def -> (Bool, Body)
optimizeOnce (name, body) = go False [] body
  where
    go :: Bool -> Body -> Body -> (Bool, Body)
    go changed acc ops' = case ops' of
      Add 0 : ops -> go True acc ops
      Move 0 : ops -> go True acc ops
      Pop 0 : ops -> go True acc ops

      Loop [Add (-1)] : ops -> go True acc (set0 : ops)
      Loop [Add 1] : ops -> go True acc (set0 : ops)

      Set s : Add a : ops -> go True acc (Set (s + a) : ops)
      Add a : Add b : ops -> go True acc (Add (a + b) : ops)
      Move a : Move b : ops -> go True acc (Move (a + b) : ops)
      Pop a : Pop b : ops -> go True acc (Pop (a + b) : ops)
      Write a : Write b : ops -> go True acc (Write (a + b) : ops)

      Add _ : ops@(Read : _) -> go True acc ops
      Add _ : ops@(Pop _ : _) -> go True acc ops
      Add _ : ops@(Set _ : _) -> go True acc ops

      Set _ : ops@(Read : _) -> go True acc ops
      Set _ : ops@(Pop _ : _) -> go True acc ops
      Set _ : ops@(Set _ : _) -> go True acc ops

      Pop n : Push : ops -> go True acc (Pop (n - 1) : Peek : ops)
      Push : Pop n : ops -> go True acc (Pop (n - 1) : ops)

      Move m : op : Move n : ops 
        | canDoWithOffset op && m == -n -> case op of
            WithOffset o op' -> go True acc (WithOffset (m + o) op' : ops)
            _ -> go True acc (WithOffset m op : ops)

      Set 0 : Loop _ : ops -> go True acc (set0 : ops)
      l@(Loop _) : Loop _ : ops -> go True acc (l : ops)
      Loop [l@(Loop _)] : ops -> go True acc (l : ops)
      Loop l : ops ->
        let (changed', l') = go False [] l
        in go changed' (Loop l' : acc) ops

      [OpCall c] | c == name -> go True acc [TailCall]
      op : ops -> go changed (op : acc) ops
      [] -> (changed, reverse acc)
  
optimizeN :: Word -> Def -> Body
optimizeN 0 (_, ops) = ops
optimizeN n (name, ops) =
  if changed
  then optimizeN (n - 1) (name, ops')
  else ops'

  where
    (changed, ops') = optimizeOnce (name, ops)

removeSet0 :: Body -> Body
removeSet0 (Set 0 : ops) = ops
removeSet0 ops = ops

optimizeOps :: Word -> Def -> Body
optimizeOps passes (name, ops) = removeSet0 $ optimizeN passes (name, set0 : ops)

callGraph :: Word -> Dict -> Dict -> [Name] -> Name -> Dict
callGraph pass d acc toGo crr =
  let
    body = optimizeOps pass (crr, d HashMap.! crr)
    called = calledOps (crr, body) \\ [crr]
    newAcc = (HashMap.insert crr body acc)
  in
    case filter (not . (`HashMap.member` acc)) $ (toGo `union` called) of
      [] -> newAcc
      (next : nexts) ->
        callGraph pass d newAcc nexts next

optimize :: Word -> Dict -> Dict
optimize passes d =
  callGraph passes d HashMap.empty [] Nothing