soyuz-0.0.0: DCPU16/Assembly/Optimizer.hs
-- | Simple assembly optimizations.
--
-- Focus on small tweaks, like shortening instructions with small literals,
-- and any NOP\/call\/arithmetic optimizations that come up.
module DCPU16.Assembly.Optimizer
( Optimization(..)
, sizeVariant
) where
import DCPU16.Instructions
import DCPU16.Instructions.Size
import Data.Maybe (fromMaybe)
import Data.Word (Word16)
import Data.ByteString (ByteString)
import qualified Data.ByteString as B
import Data.Vector (Vector)
import qualified Data.Vector as V
import Data.Generics.Uniplate.Data
import qualified Data.Map as M
-- | Optimizations, listed in the order they're performed. (Earliest first.)
data Optimization
-- | Shortens instructions by 1 word if they use literals smaller than 0x20.
--
-- > set a,1 ; 1 word, not 2
-- > add [0x8000],1 ; 2 words, not 3
= ShortLiterals
-- | Performs the short literal optimization on labels.
--
-- > :l_0000 set pc,l.0000 ; 1 word, address < 0x20
-- > :l_8000 set pc,l.8000 ; 2 words, large address
| ShortLabelLiterals
deriving (Eq,Read,Show,Ord,Enum,Bounded)
type Step = Vector Instruction -> Vector Instruction
-- | Compose a series of optimizations.
--
-- First in the list = first run. Last = last.
pipeline :: [(Optimization,Step)] -> Step
pipeline = foldl1 (flip (.)) . map snd
-- | Optimizations that change the size of instructions, but not order.
--
-- Should be run before label-to-address translation.
sizeVariant :: Step
sizeVariant = pipeline
[(ShortLiterals, shortLiterals)
,(ShortLabelLiterals, shortLabelLiterals)
]
--
-- Actual optimization implementations follow:
--
shortLiterals :: Vector Instruction -> Vector Instruction
-- ^ Rewrite small constants to make the instructions one word shorter.
--
-- Only works for literals <=32.
shortLiterals = V.map (rewriteBi f) where
f (DirectLiteral c@(Const w)) | w<=0x1f = Just $ ShortLiteral c
f _ = Nothing
shortLabelLiterals :: Vector Instruction -> Vector Instruction
-- ^ Rewrite small label addresses to make instructions one word shorter.
--
-- Only works for addresses <=32 (not many) and vulnerable to size changes.
--
-- Should probably be one of the last steps.
shortLabelLiterals is = rewriteBi f `V.map` is where
f (DirectLiteral l@(LabelAddr s)) | addr s<=0x1f = Just $ ShortLiteral l
f _ = Nothing
addr :: ByteString -> Word16
addr s = fromMaybe (error $ "undefined label "++show s) (M.lookup s lut)
lut = M.fromList . snd $ V.foldl fun (0,[]) is where
fun (addr,acc) (Label s) = (addr,(s,addr):acc)
fun (addr,acc) i = (addr + size i, acc)