packages feed

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

module Language.OpLang.Optimizer(optimize) where

import Language.OpLang.Syntax

syncTally :: Bool -> Val -> Offset -> [Instr] -> [Instr]
syncTally False 0 _ acc = acc
syncTally False n offset acc = Add n offset : acc
syncTally True n offset acc = Set n offset : acc

syncOffset :: Offset -> [Instr] -> [Instr]
syncOffset 0 acc = acc
syncOffset n acc = Move n : acc

syncAll :: Bool -> Val -> Offset -> [Instr] -> [Instr]
syncAll known tally offset = syncTally known tally 0 . syncOffset offset

removeSet0 :: [Instr] -> [Instr]
removeSet0 (Set 0 0 : ops) = ops
removeSet0 ops = ops

optimizeOps :: [Op] -> [Instr]
optimizeOps = removeSet0 . go False True 0 0 []
  where
    go :: Bool -> Bool -> Val -> Offset -> [Instr] -> [Op] -> [Instr]
    go False _ _ _ acc [] = reverse acc
    go True known tally offset acc [] = reverse $ syncAll known tally offset acc
    go loop known tally offset acc (op : ops) =
      case op of
        Incr -> go loop known (tally + 1) offset acc ops
        Decr -> go loop known (tally - 1) offset acc ops
        Read' -> go loop False 0 offset (Read offset : acc) ops
        Pop' -> go loop False 0 offset (Pop offset : acc) ops

        MoveL -> go loop False 0 (offset - 1) (syncTally known tally offset acc) ops
        MoveR -> go loop False 0 (offset + 1) (syncTally known tally offset acc) ops
        Write' -> go loop False 0 offset (Write offset : syncTally known tally offset acc) ops
        Push' -> go loop False 0 offset (Push offset : syncTally known tally offset acc) ops
        Call' c -> go loop False 0 offset (Call c : syncTally known tally offset acc) ops

        Loop' _ | (True, 0) <- (known, tally) -> go loop known tally offset acc ops
        Loop' l ->
          case go True False 0 0 [] l of
            [Add n 0] | abs n == 1 -> go loop True 0 offset acc ops
            [Add n o] | abs n == 1 -> go loop known tally offset (Set 0 (offset + o) : acc) ops

            [Add n o, Add m 0] | abs m == 1 -> go loop False 0 offset (AddCell n (o + offset) offset : syncTally known tally offset acc) ops
            [Add m 0, Add n o] | abs m == 1 -> go loop False 0 offset (AddCell n (o + offset) offset : syncTally known tally offset acc) ops

            l' -> go loop False 0 0 (Loop l' : syncAll known tally offset acc) ops

optimize :: Program Op -> Program Instr
optimize Program{..} =
  Program
  { opDefs = optimizeOps <$> opDefs
  , topLevel = optimizeOps topLevel
  }