extend supported instructions set

This commit is contained in:
Ilya Rezvov
2018-05-16 14:45:34 -07:00
parent edf072ed2a
commit 9ca5353b0f
+96 -22
View File
@@ -2,6 +2,7 @@
{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE GADTs #-} {-# LANGUAGE GADTs #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE DataKinds #-} {-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-} {-# LANGUAGE TypeOperators #-}
@@ -11,13 +12,16 @@
{-# LANGUAGE TypeInType #-} {-# LANGUAGE TypeInType #-}
{-# LANGUAGE TypeSynonymInstances #-} {-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Language.Wasm.Builder ( module Language.Wasm.Builder (
GenMod, GenMod,
genMod, genMod,
global, fun, funRec, table, memory, dataSegment, global, typedef, fun, funRec, table, memory, dataSegment,
importFunction, importGlobal, importMemory, importTable, importFunction, importGlobal, importMemory, importTable,
nextFuncIndex, setGlobalInitializer,
GenFun, GenFun,
Glob, Loc,
param, param,
local, local,
ret, ret,
@@ -28,13 +32,17 @@ module Language.Wasm.Builder (
eq, lt_s, lt_u, eq, lt_s, lt_u,
load, store, load, store,
call, invoke, call, invoke,
ifExpr, ifStmt ifExpr, ifStmt, loopExpr, loopStmt, for,
trap, unreachable,
appendExpr, after,
Producer, OutType, produce, Consumer, (.=)
) where ) where
import Prelude hiding (and) import Prelude hiding (and)
import qualified Data.List as List import qualified Data.List as List
import qualified Data.Maybe as Maybe import qualified Data.Maybe as Maybe
import Control.Monad.State (State, execState, get, gets, put, modify) import Control.Monad.State (State, execState, get, gets, put, modify)
import Control.Monad.Reader (ReaderT, ask, runReaderT)
import Numeric.Natural import Numeric.Natural
import Data.Word (Word32, Word64) import Data.Word (Word32, Word64)
import Data.Int (Int32, Int64) import Data.Int (Int32, Int64)
@@ -52,10 +60,10 @@ data FuncDef = FuncDef {
instrs :: Expression instrs :: Expression
} deriving (Show, Eq) } deriving (Show, Eq)
type GenFun = State FuncDef type GenFun = ReaderT Natural (State FuncDef)
genExpr :: GenFun a -> Expression genExpr :: Natural -> GenFun a -> Expression
genExpr gen = instrs $ execState gen $ FuncDef [] [] [] [] genExpr deep gen = instrs $ flip execState (FuncDef [] [] [] []) $ runReaderT gen deep
newtype Loc t = Loc Natural deriving (Show, Eq) newtype Loc t = Loc Natural deriving (Show, Eq)
@@ -123,7 +131,15 @@ getSize I64 = BS32
getSize F32 = BS64 getSize F32 = BS64
getSize F64 = BS64 getSize F64 = BS64
iBinOp :: (Producer a, Producer b, OutType a ~ OutType b) => IBinOp -> a -> b -> GenFun (OutType a) type family IsInt i :: Bool where
IsInt (Proxy I32) = True
IsInt (Proxy I64) = True
IsInt any = False
nop :: GenFun ()
nop = appendExpr [Nop]
iBinOp :: (Producer a, Producer b, OutType a ~ OutType b, IsInt (OutType a) ~ True) => IBinOp -> a -> b -> GenFun (OutType a)
iBinOp op a b = produce a >> after [IBinOp (getSize $ asValueType a) op] (produce b) iBinOp op a b = produce a >> after [IBinOp (getSize $ asValueType a) op] (produce b)
add :: (Producer a, Producer b, OutType a ~ OutType b) => a -> b -> GenFun (OutType a) add :: (Producer a, Producer b, OutType a ~ OutType b) => a -> b -> GenFun (OutType a)
@@ -153,9 +169,15 @@ mul a b = do
F32 -> after [FBinOp BS32 FMul] (produce b) F32 -> after [FBinOp BS32 FMul] (produce b)
F64 -> after [FBinOp BS64 FMul] (produce b) F64 -> after [FBinOp BS64 FMul] (produce b)
and :: (Producer a, Producer b, OutType a ~ OutType b) => a -> b -> GenFun (OutType a) and :: (Producer a, Producer b, OutType a ~ OutType b, IsInt (OutType a) ~ True) => a -> b -> GenFun (OutType a)
and = iBinOp IAnd and = iBinOp IAnd
or :: (Producer a, Producer b, OutType a ~ OutType b, IsInt (OutType a) ~ True) => a -> b -> GenFun (OutType a)
or = iBinOp IOr
xor :: (Producer a, Producer b, OutType a ~ OutType b, IsInt (OutType a) ~ True) => a -> b -> GenFun (OutType a)
xor = iBinOp IXor
relOp :: (Producer a, Producer b, OutType a ~ OutType b) => IRelOp -> a -> b -> GenFun (Proxy I32) relOp :: (Producer a, Producer b, OutType a ~ OutType b) => IRelOp -> a -> b -> GenFun (Proxy I32)
relOp op a b = do relOp op a b = do
produce a produce a
@@ -220,26 +242,59 @@ invoke idx args = sequence_ args >> appendExpr [Call idx]
call :: Proxy t -> Natural -> [GenFun a] -> GenFun (Proxy t) call :: Proxy t -> Natural -> [GenFun a] -> GenFun (Proxy t)
call t idx args = sequence_ args >> appendExpr [Call idx] >> return t call t idx args = sequence_ args >> appendExpr [Call idx] >> return t
br :: Label t -> GenFun ()
br (Label labelDeep) = do
deep <- ask
appendExpr [Br $ deep - labelDeep]
newtype Label i = Label Natural deriving (Show, Eq)
ifExpr :: (Producer pred, OutType pred ~ Proxy I32, ValueTypeable t, Producer true, OutType true ~ Proxy t, Producer false, OutType false ~ Proxy t) ifExpr :: (Producer pred, OutType pred ~ Proxy I32, ValueTypeable t, Producer true, OutType true ~ Proxy t, Producer false, OutType false ~ Proxy t)
=> Proxy t => Proxy t
-> pred -> pred
-> true -> (Label t -> true)
-> false -> (Label t -> false)
-> GenFun (Proxy t) -> GenFun (Proxy t)
ifExpr t pred true false = do ifExpr t pred true false = do
produce pred produce pred
appendExpr [If [getValueType t] (genExpr $ produce true) (genExpr $ produce false)] deep <- (+1) <$> ask
appendExpr [If [getValueType t] (genExpr deep $ produce $ true $ Label deep) (genExpr deep $ produce $ false $ Label deep)]
return Proxy return Proxy
ifStmt :: (Producer pred, OutType pred ~ Proxy I32) ifStmt :: (Producer pred, OutType pred ~ Proxy I32)
=> Proxy t => pred
-> pred -> (Label () -> GenFun a)
-> GenFun a -> (Label () -> GenFun a)
-> GenFun a
-> GenFun () -> GenFun ()
ifStmt t pred true false = do ifStmt pred true false = do
produce pred produce pred
appendExpr [If [] (genExpr true) (genExpr false)] deep <- (+1) <$> ask
appendExpr [If [] (genExpr deep $ true $ Label deep) (genExpr deep $ false $ Label deep)]
for :: (Producer pred, OutType pred ~ Proxy I32) => GenFun () -> pred -> GenFun () -> (Label () -> GenFun ()) -> GenFun ()
for initer pred after body = do
initer
let loopBody lbl = body lbl >> after >> ifStmt pred (const $ br lbl) (const nop)
ifStmt pred (const $ loopStmt loopBody) (const nop)
loopExpr :: (Producer body, OutType body ~ Proxy t, ValueTypeable t) => Proxy t -> (Label t -> body) -> GenFun (OutType body)
loopExpr t body = do
deep <- (+1) <$> ask
appendExpr [Loop [getValueType t] (genExpr deep $ produce $ body $ Label deep)]
return t
loopStmt :: (Label () -> GenFun ()) -> GenFun ()
loopStmt body = do
deep <- (+1) <$> ask
appendExpr [Loop [] (genExpr deep $ body $ Label deep)]
trap :: Proxy t -> GenFun (Proxy t)
trap t = do
appendExpr [Unreachable]
return t
unreachable :: GenFun ()
unreachable = appendExpr [Unreachable]
class Consumer loc where class Consumer loc where
(.=) :: (Producer expr) => loc -> expr -> GenFun () (.=) :: (Producer expr) => loc -> expr -> GenFun ()
@@ -250,10 +305,17 @@ instance Consumer (Loc t) where
instance Consumer (Glob t) where instance Consumer (Glob t) where
(.=) (Glob i) expr = produce expr >> appendExpr [SetGlobal i] (.=) (Glob i) expr = produce expr >> appendExpr [SetGlobal i]
typedef :: FuncType -> GenMod Natural
typedef t = do
st@GenModState { target = m@Module { types } } <- get
let (idx, inserted) = Maybe.fromMaybe (length types, types ++ [t]) $ (\i -> (i, types)) <$> List.findIndex (== t) types
put $ st { target = m { types = inserted } }
return $ fromIntegral idx
funRec :: (Natural -> GenFun a) -> GenMod Natural funRec :: (Natural -> GenFun a) -> GenMod Natural
funRec generator = do funRec generator = do
st@GenModState { target = m@Module { types, functions }, funcIdx } <- get st@GenModState { target = m@Module { types, functions }, funcIdx } <- get
let FuncDef { args, results, locals, instrs } = execState (generator funcIdx) $ FuncDef [] [] [] [] let FuncDef { args, results, locals, instrs } = execState (runReaderT (generator funcIdx) 0) $ FuncDef [] [] [] []
let t = FuncType args results let t = FuncType args results
let (idx, inserted) = Maybe.fromMaybe (length types, types ++ [t]) $ (\i -> (i, types)) <$> List.findIndex (== t) types let (idx, inserted) = Maybe.fromMaybe (length types, types ++ [t]) $ (\i -> (i, types)) <$> List.findIndex (== t) types
put $ st { put $ st {
@@ -265,6 +327,9 @@ funRec generator = do
fun :: GenFun a -> GenMod Natural fun :: GenFun a -> GenMod Natural
fun = funRec . const fun = funRec . const
nextFuncIndex :: GenMod Natural
nextFuncIndex = gets funcIdx
data GenModState = GenModState { data GenModState = GenModState {
funcIdx :: Natural, funcIdx :: Natural,
globIdx :: Natural, globIdx :: Natural,
@@ -286,14 +351,14 @@ importFunction mod name t = do
} }
return funcIdx return funcIdx
importGlobal :: (ValueTypeable t) => TL.Text -> TL.Text -> Proxy t -> GenMod Natural importGlobal :: (ValueTypeable t) => TL.Text -> TL.Text -> Proxy t -> GenMod (Glob t)
importGlobal mod name t = do importGlobal mod name t = do
st@GenModState { target = m@Module { imports }, globIdx } <- get st@GenModState { target = m@Module { imports }, globIdx } <- get
put $ st { put $ st {
target = m { imports = imports ++ [Import mod name $ ImportGlobal $ Const $ getValueType t] }, target = m { imports = imports ++ [Import mod name $ ImportGlobal $ Const $ getValueType t] },
globIdx = globIdx + 1 globIdx = globIdx + 1
} }
return globIdx return $ Glob globIdx
importMemory :: TL.Text -> TL.Text -> Natural -> Maybe Natural -> GenMod () importMemory :: TL.Text -> TL.Text -> Natural -> Maybe Natural -> GenMod ()
importMemory mod name min max = do importMemory mod name min max = do
@@ -348,6 +413,15 @@ global mkType t val = do
} }
return $ Glob idx return $ Glob idx
setGlobalInitializer :: forall t . (ValueTypeable t) => Glob t -> (ValType t) -> GenMod ()
setGlobalInitializer (Glob idx) val = do
modify $ \(st@GenModState { target = m }) ->
let globImpsLen = length $ filter isGlobalImport $ imports m in
let (h, glob:t) = splitAt (fromIntegral idx - globImpsLen) $ globals m in
st {
target = m { globals = h ++ [glob { initializer = initWith (Proxy @t) val }] ++ t }
}
memory :: Natural -> Maybe Natural -> GenMod () memory :: Natural -> Maybe Natural -> GenMod ()
memory min max = do memory min max = do
modify $ \(st@GenModState { target = m }) -> st { modify $ \(st@GenModState { target = m }) -> st {
@@ -363,7 +437,7 @@ table min max = do
dataSegment :: (Producer offset, OutType offset ~ Proxy I32) => offset -> LBS.ByteString -> GenMod () dataSegment :: (Producer offset, OutType offset ~ Proxy I32) => offset -> LBS.ByteString -> GenMod ()
dataSegment offset bytes = dataSegment offset bytes =
modify $ \(st@GenModState { target = m }) -> st { modify $ \(st@GenModState { target = m }) -> st {
target = m { datas = datas m ++ [DataSegment 0 (genExpr (produce offset)) bytes] } target = m { datas = datas m ++ [DataSegment 0 (genExpr 0 (produce offset)) bytes] }
} }
asWord32 :: Int32 -> Word32 asWord32 :: Int32 -> Word32
@@ -402,12 +476,12 @@ rts = genMod $ do
addr <- local i32 addr <- local i32
alignedSize .= call i32 aligned [arg size] alignedSize .= call i32 aligned [arg size]
ifExpr i32 ((heapNext `add` alignedSize) `lt_u` heapEnd) ifExpr i32 ((heapNext `add` alignedSize) `lt_u` heapEnd)
(do (const $ do
addr .= heapNext addr .= heapNext
heapNext .= (heapNext `add` alignedSize) heapNext .= (heapNext `add` alignedSize)
ret addr ret addr
) )
(do (const $ do
invoke gc [] invoke gc []
call i32 self [arg size] call i32 self [arg size]
) )