packages feed

libriscv-0.1.0.0: internal/LibRISCV/Internal/Decoder/Generator.hs

{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TupleSections #-}

module LibRISCV.Internal.Decoder.Generator where

import Control.Monad (forM)
import Control.Monad.IO.Class (MonadIO, liftIO)
import Data.Bits ((.&.))
import Data.Char (toUpper)
import Data.FileEmbed (makeRelativeToProject)
import Data.List (nub)
import qualified Data.Map.Strict as M
import Data.Maybe (fromJust, isJust)
import Data.Word (Word32)
import Data.Yaml
import Language.Haskell.TH hiding (match)
import LibRISCV.Internal.Decoder.YamlParser

readAndParse :: (MonadIO m) => FilePath -> m [(String, InstructionFields)]
readAndParse filePath = M.toList <$> liftIO (decodeFileThrow filePath)

generateConsts :: [(String, InstructionFields)] -> String -> (InstructionFields -> String) -> Q [Dec]
generateConsts entries suffix f = do
    forM entries $ \(instructionName, fields) -> do
        let
            nameD = mkName $ instructionName <> "_" <> suffix
            valE = read $ f fields
        pure $
            ValD
                (VarP nameD)
                (NormalB $ LitE $ IntegerL valE)
                []

generateExtType :: [(String, InstructionFields)] -> Q [Dec]
generateExtType entries = do
    let
        byExts =
            M.fromListWith (<>)
                . map (\(x, y) -> (x, [y]))
                . concatMap
                    ( \(inst, fs) ->
                        map (,inst) (extension fs)
                    )
                $ entries
        superType =
            DataD
                []
                (mkName "InstructionType")
                []
                Nothing
                ( NormalC (mkName "InvalidInstruction") []
                    : map
                        ( \ex ->
                            NormalC
                                (mkName $ map toUpper ex)
                                [(Bang NoSourceUnpackedness NoSourceStrictness, ConT (mkName $ map toUpper ex))]
                        )
                        (M.keys byExts)
                )
                [DerivClause Nothing [ConT ''Eq, ConT ''Show]]
    extTypes <- forM (M.toList byExts) $ \(ext, instructions) -> do
        pure $
            DataD
                []
                (mkName $ map toUpper ext)
                []
                Nothing
                (map (\instr -> NormalC (mkName $ map toUpper instr) []) instructions)
                [DerivClause Nothing [ConT ''Eq, ConT ''Show]]

    pure $ extTypes <> [superType]

generateExtDecodeFns :: [(String, InstructionFields)] -> Q [Dec]
generateExtDecodeFns entries = do
    let
        byExts =
            M.fromListWith (<>)
                . map (\(x, y) -> (x, [y]))
                . concatMap
                    ( \(inst, fs) ->
                        map (,inst) (extension fs)
                    )
                $ entries

    sigs <- forM (M.toList byExts) $ \(ex, _) ->
        pure $
            SigD
                (mkName $ "decode_" <> map toUpper ex)
                ( AppT
                    ( AppT
                        ArrowT
                        (ConT ''Word32)
                    )
                    ( AppT
                        (ConT ''Maybe)
                        (ConT $ mkName $ map toUpper ex)
                    )
                )
    defs <- forM (M.toList byExts) $ \(ex, instrs) ->
        pure $
            FunD
                (mkName $ "decode_" <> map toUpper ex)
                [ Clause
                    [VarP (mkName "w")]
                    ( GuardedB $
                        map
                            ( \instr ->
                                ( NormalG $
                                    InfixE
                                        ( Just
                                            ( InfixE
                                                (Just (VarE $ mkName "w"))
                                                (VarE '(.&.))
                                                (Just (VarE $ mkName $ instr <> "_mask"))
                                            )
                                        )
                                        (VarE '(==))
                                        (Just (VarE $ mkName $ instr <> "_match"))
                                , AppE (ConE 'Just) $ ConE $ mkName $ map toUpper instr
                                )
                            )
                            instrs
                            <> [(NormalG (ConE 'True), ConE 'Nothing)]
                    )
                    []
                ]
    pure $ sigs <> defs

generateDecodeFn :: [(String, InstructionFields)] -> Q [Dec]
generateDecodeFn entries = do
    let
        exts =
            map (map toUpper)
                . nub
                . concatMap (extension . snd)
                $ entries
        sig =
            SigD
                (mkName "decode")
                ( AppT
                    ( AppT
                        ArrowT
                        (ConT ''Word32)
                    )
                    (ConT $ mkName "InstructionType")
                )
        def =
            FunD
                (mkName "decode")
                [ Clause
                    [VarP $ mkName "w"]
                    ( GuardedB $
                        map
                            ( \ex ->
                                ( NormalG $
                                    AppE
                                        (VarE 'isJust)
                                        ( AppE
                                            (VarE $ mkName $ "decode_" <> ex)
                                            (VarE $ mkName "w")
                                        )
                                , AppE
                                    (ConE $ mkName ex)
                                    ( AppE
                                        (VarE 'fromJust)
                                        ( AppE
                                            (VarE $ mkName $ "decode_" <> ex)
                                            (VarE $ mkName "w")
                                        )
                                    )
                                )
                            )
                            exts
                            <> [(NormalG (ConE 'True), ConE $ mkName "InvalidInstruction")]
                    )
                    []
                ]
    pure [def, sig]

generateMasks :: [(String, InstructionFields)] -> Q [Dec]
generateMasks entries = generateConsts entries "mask" mask

generateMatches :: [(String, InstructionFields)] -> Q [Dec]
generateMatches entries = generateConsts entries "match" match

generateAll :: [[(String, InstructionFields)] -> Q [Dec]] -> FilePath -> Q [Dec]
generateAll seqs path = do
    entries <- liftIO $ readAndParse path
    concat <$> mapM (\f -> f entries) seqs

generateDefaultDecoder :: Q [Dec]
generateDefaultDecoder =
    makeRelativeToProject "data/instr_dict.yaml"
        >>= generateAll
            [ generateMasks
            , generateMatches
            , generateExtType
            , generateExtDecodeFns
            , generateDecodeFn
            ]