start builder api

This commit is contained in:
Ilya Rezvov
2018-05-12 22:37:50 -07:00
parent 8ca3319ba4
commit 1330eb41f6
9 changed files with 304 additions and 32 deletions
+1
View File
@@ -1,2 +1,3 @@
.stack-work .stack-work
tests/runnable/* tests/runnable/*
dist/
+4 -3
View File
@@ -1,5 +1,6 @@
{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE FlexibleInstances #-}
module Language.Wasm.Binary ( module Language.Wasm.Binary (
dumpModule, dumpModule,
@@ -297,7 +298,7 @@ instance Serialize MemArg where
put (MemArg align offset) = putULEB128 align >> putULEB128 offset put (MemArg align offset) = putULEB128 align >> putULEB128 offset
get = MemArg <$> getULEB128 32 <*> getULEB128 32 get = MemArg <$> getULEB128 32 <*> getULEB128 32
instance Serialize Instruction where instance Serialize (Instruction Natural) where
put Unreachable = putWord8 0x00 put Unreachable = putWord8 0x00
put Nop = putWord8 0x01 put Nop = putWord8 0x01
put (Block result body) = do put (Block result body) = do
@@ -682,7 +683,7 @@ putExpression expr = do
getExpression :: Get Expression getExpression :: Get Expression
getExpression = go [] getExpression = go []
where where
go :: [Instruction] -> Get Expression go :: Expression -> Get Expression
go acc = do go acc = do
nextByte <- lookAhead getWord8 nextByte <- lookAhead getWord8
if nextByte == 0x0B -- END OF EXPR if nextByte == 0x0B -- END OF EXPR
@@ -692,7 +693,7 @@ getExpression = go []
getTrueBranch :: Get (Expression, Bool) getTrueBranch :: Get (Expression, Bool)
getTrueBranch = go [] getTrueBranch = go []
where where
go :: [Instruction] -> Get (Expression, Bool) go :: Expression -> Get (Expression, Bool)
go acc = do go acc = do
nextByte <- lookAhead getWord8 nextByte <- lookAhead getWord8
case nextByte of case nextByte of
+271
View File
@@ -0,0 +1,271 @@
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE TypeInType #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE FlexibleInstances #-}
module Language.Wasm.Builder (
) where
import Prelude hiding (and)
import qualified Data.List as List
import qualified Data.Maybe as Maybe
import Control.Monad.State (State, execState, get, put, modify)
import Numeric.Natural
import Data.Word (Word32, Word64)
import Data.Int (Int32, Int64)
import Data.Proxy
import qualified Data.Text.Lazy as TL
import Language.Wasm.Structure
data FuncDef = FuncDef {
args :: [ValueType],
results :: [ValueType],
locals :: [ValueType],
instrs :: Expression
} deriving (Show, Eq)
type GenFun = State FuncDef
newtype Loc t = Loc Natural deriving (Show, Eq)
param :: (ValueTypeable t) => Proxy t -> GenFun (Loc t)
param t = do
f@FuncDef { args } <- get
put $ f { args = args ++ [getValueType t] }
return $ Loc $ fromIntegral $ length args
local :: (ValueTypeable t) => Proxy t -> GenFun (Loc t)
local t = do
f@FuncDef { args, locals } <- get
put $ f { locals = locals ++ [getValueType t]}
return $ Loc $ fromIntegral $ length args + length locals
appendExpr :: Expression -> GenFun ()
appendExpr expr = do
modify $ \def -> def { instrs = instrs def ++ expr }
return ()
after :: Expression -> GenFun a -> GenFun a
after instr expr = do
res <- expr
modify $ \def -> def { instrs = instrs def ++ instr }
return res
class Producer expr where
type OutType expr
asValueType :: expr -> ValueType
produce :: expr -> GenFun (OutType expr)
instance (ValueTypeable t) => Producer (Loc t) where
type OutType (Loc t) = Proxy t
asValueType e = getValueType (t e)
where
t :: Loc t -> Proxy t
t _ = Proxy
produce (Loc i) = appendExpr [GetLocal i] >> return Proxy
instance (ValueTypeable t) => Producer (Glob t) where
type OutType (Glob t) = Proxy t
asValueType e = getValueType (t e)
where
t :: Glob t -> Proxy t
t _ = Proxy
produce (Glob i) = appendExpr [GetGlobal i] >> return Proxy
instance (ValueTypeable t) => Producer (GenFun (Proxy t)) where
type OutType (GenFun (Proxy t)) = Proxy t
asValueType e = getValueType (t e)
where
t :: GenFun (Proxy t) -> Proxy t
t _ = Proxy
produce = id
ret :: (Producer expr) => expr -> GenFun ()
ret e = produce e >> return ()
arg :: (Producer expr) => expr -> GenFun ()
arg e = produce e >> return ()
getSize :: ValueType -> BitSize
getSize I32 = BS32
getSize I64 = BS32
getSize F32 = BS64
getSize F64 = BS64
binOp :: (Producer a, Producer b, OutType a ~ OutType b) => IBinOp -> a -> b -> GenFun (OutType a)
binOp 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 = binOp IAdd
and :: (Producer a, Producer b, OutType a ~ OutType b) => a -> b -> GenFun (OutType a)
and = binOp IAnd
i32const :: (Integral i) => i -> GenFun (Proxy I32)
i32const i = appendExpr [I32Const $ asWord32 $ fromIntegral i] >> return Proxy
call :: Proxy t -> Natural -> [GenFun a] -> GenFun (Proxy t)
call t idx args = sequence_ args >> appendExpr [Call idx] >> return t
-- if' :: (Producer pred, OutType pred ~ I32, OutType true ~ OutType false) => pred -> true -> false -> GenFun (OutType true)
-- if' pred true false = do
-- appendExpr [If idx]
class Consumer loc where
(.=) :: (Producer expr) => loc -> expr -> GenFun ()
instance Consumer (Loc t) where
(.=) (Loc i) expr = produce expr >> appendExpr [SetLocal i]
instance Consumer (Glob t) where
(.=) (Glob i) expr = produce expr >> appendExpr [SetGlobal i]
fun :: (Natural -> GenFun a) -> GenMod Natural
fun generator = do
st@GenModState { target = m@Module { types, functions }, funcIdx } <- get
let FuncDef { args, results, locals, instrs } = execState (generator funcIdx) $ FuncDef [] [] [] []
let t = FuncType args results
let (idx, inserted) = Maybe.fromMaybe (length types, types ++ [t]) $ (\i -> (i, types)) <$> List.findIndex (== t) types
put $ st {
target = m { functions = functions ++ [Function (fromIntegral idx) locals instrs], types = inserted },
funcIdx = funcIdx + 1
}
return funcIdx
data GenModState = GenModState {
funcIdx :: Natural,
globIdx :: Natural,
target :: Module
} deriving (Show, Eq)
type GenMod = State GenModState
genMod :: GenMod a -> Module
genMod = target . flip execState (GenModState 0 0 emptyModule)
importFunc :: TL.Text -> TL.Text -> FuncType -> GenMod Natural
importFunc mod name t = do
st@GenModState { target = m@Module { types, imports }, funcIdx } <- get
let (idx, inserted) = Maybe.fromMaybe (length types, types ++ [t]) $ (\i -> (i, types)) <$> List.findIndex (== t) types
put $ st {
target = m { imports = imports ++ [Import mod name $ ImportFunc $ fromIntegral idx], types = inserted },
funcIdx = funcIdx + 1
}
return funcIdx
class ValueTypeable a where
type ValType a
getValueType :: (Proxy a) -> ValueType
initWith :: (Proxy a) -> (ValType a) -> Expression
instance ValueTypeable I32 where
type ValType I32 = Word32
getValueType _ = I32
initWith _ w = [I32Const w]
instance ValueTypeable I64 where
type ValType I64 = Word64
getValueType _ = I64
initWith _ w = [I64Const w]
instance ValueTypeable F32 where
type ValType F32 = Float
getValueType _ = F32
initWith _ f = [F32Const f]
instance ValueTypeable F64 where
type ValType F64 = Double
getValueType _ = F64
initWith _ d = [F64Const d]
i32 = Proxy @I32
i64 = Proxy @I64
f32 = Proxy @F32
f64 = Proxy @F64
newtype Glob t = Glob Natural deriving (Show, Eq)
global :: (ValueTypeable t) => (ValueType -> GlobalType) -> Proxy t -> (ValType t) -> GenMod (Glob t)
global mkType t val = do
st@GenModState { target = m@Module { globals }, globIdx } <- get
put $ st {
target = m { globals = globals ++ [Global (mkType $ getValueType t) (initWith t val)] },
globIdx = globIdx + 1
}
return $ Glob globIdx
asWord32 :: Int32 -> Word32
asWord32 i
| i >= 0 = fromIntegral i
| otherwise = 0xFFFFFFFF - (fromIntegral (abs i)) + 1
asWord64 :: Int64 -> Word64
asWord64 i
| i >= 0 = fromIntegral i
| otherwise = 0xFFFFFFFFFFFFFFFF - (fromIntegral (abs i)) + 1
rts :: Module
rts = genMod $ do
gc <- importFunc "rts" "gc" (FuncType [I32] [])
stackStart <- global Const i32 0
stackEnd <- global Const i32 0
stackBase <- global Mut i32 0
stackTop <- global Mut i32 0
retReg <- global Mut i32 0
tmpReg <- global Mut i32 0
heapStart <- global Mut i32 0
heapNext <- global Mut i32 0
heapEnd <- global Mut i32 0
aligned <- fun $ \_ -> do
size <- param i32
(size `add` i32const 3) `and` i32const 0xFFFFFFFC
alloc <- fun $ \self -> do
size <- param i32
alignedSize <- local i32
addr <- local i32
alignedSize .= call i32 aligned [arg size]
-- if' ((heapNext `plus` alignedSize) `lt_u` heapEnd)
-- (do
-- addr .= nextHeap
-- nextHeap .= nextHeap `plus` alignedSize
-- ret addr
-- )
-- (do
-- call gc []
-- call alloc [ref size]
-- )
ret addr
return ()
{-
(func $alloc (param $size i32) (result i32)
(local $aligned-size i32)
(local $addr i32)
(set_local $aligned-size (call $alligned (get_local $size)))
(if (i32.lt_u (i32.add (get_global $heap-next) (get_local $aligned-size)) (get_global $heap-end))
(then
(set_local $addr (get_global $heap-next))
(set_global $heap-next (i32.add (get_global $heap-next) (get_local $aligned-size)))
(get_local $addr)
)
(else
(call $run-gc)
(call $alloc (get_local $size))
)
)
)
-}
+4 -4
View File
@@ -431,7 +431,7 @@ getGlobalValue inst store idx =
GIMut _ ref -> readIORef ref GIMut _ ref -> readIORef ref
-- due the validation there can be only these instructions -- due the validation there can be only these instructions
evalConstExpr :: ModuleInstance -> Store -> [Instruction] -> IO Value evalConstExpr :: ModuleInstance -> Store -> Expression -> IO Value
evalConstExpr _ _ [I32Const v] = return $ VI32 v evalConstExpr _ _ [I32Const v] = return $ VI32 v
evalConstExpr _ _ [I64Const v] = return $ VI64 v evalConstExpr _ _ [I64Const v] = return $ VI64 v
evalConstExpr _ _ [F32Const v] = return $ VF32 v evalConstExpr _ _ [F32Const v] = return $ VF32 v
@@ -442,7 +442,7 @@ evalConstExpr _ _ instrs = error $ "Global initializer contains unsupported inst
allocAndInitGlobals :: ModuleInstance -> Store -> [Global] -> IO (Vector GlobalInstance) allocAndInitGlobals :: ModuleInstance -> Store -> [Global] -> IO (Vector GlobalInstance)
allocAndInitGlobals inst store globs = Vector.fromList <$> mapM allocGlob globs allocAndInitGlobals inst store globs = Vector.fromList <$> mapM allocGlob globs
where where
runIniter :: [Instruction] -> IO Value runIniter :: Expression -> IO Value
-- the spec says get global can ref only imported globals -- the spec says get global can ref only imported globals
-- only they are in store for this moment -- only they are in store for this moment
runIniter = evalConstExpr inst store runIniter = evalConstExpr inst store
@@ -594,7 +594,7 @@ eval budget store FunctionInstance { funcType, moduleInstance, code = Function {
initLocal F32 = VF32 0 initLocal F32 = VF32 0
initLocal F64 = VF64 0 initLocal F64 = VF64 0
go :: EvalCtx -> [Instruction] -> IO EvalResult go :: EvalCtx -> Expression -> IO EvalResult
go ctx [] = return $ Done ctx go ctx [] = return $ Done ctx
go ctx (instr:rest) = do go ctx (instr:rest) = do
res <- step ctx instr res <- step ctx instr
@@ -630,7 +630,7 @@ eval budget store FunctionInstance { funcType, moduleInstance, code = Function {
return $ Done ctx { stack = rest } return $ Done ctx { stack = rest }
makeStoreInstr _ _ _ _ = error "Incorrect value on top of stack for memory instruction" makeStoreInstr _ _ _ _ = error "Incorrect value on top of stack for memory instruction"
step :: EvalCtx -> Instruction -> IO EvalResult step :: EvalCtx -> Instruction Natural -> IO EvalResult
step _ Unreachable = return Trap step _ Unreachable = return Trap
step ctx Nop = return $ Done ctx step ctx Nop = return $ Done ctx
step ctx (Block resType expr) = do step ctx (Block resType expr) = do
+4 -6
View File
@@ -1440,8 +1440,6 @@ data Module = Module {
type Script = [Command] type Script = [Command]
type Expression = [Instruction]
data ModuleDef data ModuleDef
= RawModDef (Maybe Ident) S.Module = RawModDef (Maybe Ident) S.Module
| TextModDef (Maybe Ident) TL.Text | TextModDef (Maybe Ident) TL.Text
@@ -1457,14 +1455,14 @@ data Command
deriving (Show, Eq) deriving (Show, Eq)
data Action data Action
= Invoke (Maybe Ident) TL.Text [[S.Instruction]] = Invoke (Maybe Ident) TL.Text [S.Expression]
| Get (Maybe Ident) TL.Text | Get (Maybe Ident) TL.Text
deriving (Show, Eq) deriving (Show, Eq)
type FailureString = TL.Text type FailureString = TL.Text
data Assertion data Assertion
= AssertReturn Action [[S.Instruction]] = AssertReturn Action [S.Expression]
| AssertReturnCanonicalNaN Action | AssertReturnCanonicalNaN Action
| AssertReturnArithmeticNaN Action | AssertReturnArithmeticNaN Action
| AssertTrap (Either Action ModuleDef) FailureString | AssertTrap (Either Action ModuleDef) FailureString
@@ -1489,7 +1487,7 @@ data FunCtx = FunCtx {
ctxParams :: [ParamType] ctxParams :: [ParamType]
} deriving (Eq, Show) } deriving (Eq, Show)
constInstructionToValue :: Instruction -> S.Instruction constInstructionToValue :: Instruction -> S.Instruction Natural
constInstructionToValue (PlainInstr (I32Const v)) = S.I32Const $ integerToWord32 v constInstructionToValue (PlainInstr (I32Const v)) = S.I32Const $ integerToWord32 v
constInstructionToValue (PlainInstr (F32Const v)) = S.F32Const v constInstructionToValue (PlainInstr (F32Const v)) = S.F32Const v
constInstructionToValue (PlainInstr (I64Const v)) = S.I64Const $ integerToWord64 v constInstructionToValue (PlainInstr (I64Const v)) = S.I64Const $ integerToWord64 v
@@ -1639,7 +1637,7 @@ desugarize fields = do
Nothing -> Left "unknown label" Nothing -> Left "unknown label"
-- functions -- functions
synInstrToStruct :: FunCtx -> Instruction -> Either String S.Instruction synInstrToStruct :: FunCtx -> Instruction -> Either String (S.Instruction Natural)
synInstrToStruct _ (PlainInstr Unreachable) = return S.Unreachable synInstrToStruct _ (PlainInstr Unreachable) = return S.Unreachable
synInstrToStruct _ (PlainInstr Nop) = return S.Nop synInstrToStruct _ (PlainInstr Nop) = return S.Nop
synInstrToStruct ctx (PlainInstr (Br labelIdx)) = synInstrToStruct ctx (PlainInstr (Br labelIdx)) =
+1 -1
View File
@@ -106,7 +106,7 @@ runScript onAssertFail script = do
getModule st (Just (Ident i)) = Map.lookup i (modules st) getModule st (Just (Ident i)) = Map.lookup i (modules st)
getModule st Nothing = lastModule st getModule st Nothing = lastModule st
asArg :: [Struct.Instruction] -> Interpreter.Value asArg :: Struct.Expression -> Interpreter.Value
asArg [Struct.I32Const v] = Interpreter.VI32 v asArg [Struct.I32Const v] = Interpreter.VI32 v
asArg [Struct.F32Const v] = Interpreter.VF32 v asArg [Struct.F32Const v] = Interpreter.VF32 v
asArg [Struct.I64Const v] = Interpreter.VI64 v asArg [Struct.I64Const v] = Interpreter.VI64 v
+13 -13
View File
@@ -102,28 +102,28 @@ type LocalsType = [ValueType]
data FuncType = FuncType { params :: ParamsType, results :: ResultType } deriving (Show, Eq, Generic, NFData) data FuncType = FuncType { params :: ParamsType, results :: ResultType } deriving (Show, Eq, Generic, NFData)
data Instruction = data Instruction index =
-- Control instructions -- Control instructions
Unreachable Unreachable
| Nop | Nop
| Block { result :: ResultType, body :: Expression } | Block { result :: ResultType, body :: Expression }
| Loop { result :: ResultType, body :: Expression } | Loop { result :: ResultType, body :: Expression }
| If { result :: ResultType, true :: Expression, false :: Expression } | If { result :: ResultType, true :: Expression, false :: Expression }
| Br LabelIndex | Br index
| BrIf LabelIndex | BrIf index
| BrTable [LabelIndex] LabelIndex | BrTable [index] index
| Return | Return
| Call FuncIndex | Call index
| CallIndirect TypeIndex | CallIndirect index
-- Parametric instructions -- Parametric instructions
| Drop | Drop
| Select | Select
-- Variable instructions -- Variable instructions
| GetLocal LocalIndex | GetLocal index
| SetLocal LocalIndex | SetLocal index
| TeeLocal LocalIndex | TeeLocal index
| GetGlobal GlobalIndex | GetGlobal index
| SetGlobal GlobalIndex | SetGlobal index
-- Memory instructions -- Memory instructions
| I32Load MemArg | I32Load MemArg
| I64Load MemArg | I64Load MemArg
@@ -176,7 +176,7 @@ data Instruction =
| FReinterpretI BitSize | FReinterpretI BitSize
deriving (Show, Eq, Generic, NFData) deriving (Show, Eq, Generic, NFData)
type Expression = [Instruction] type Expression = [Instruction Natural]
data Function = Function { data Function = Function {
funcType :: TypeIndex, funcType :: TypeIndex,
@@ -203,7 +203,7 @@ data Global = Global {
data ElemSegment = ElemSegment { data ElemSegment = ElemSegment {
tableIndex :: TableIndex, tableIndex :: TableIndex,
offset :: [Instruction], offset :: Expression,
funcIndexes :: [FuncIndex] funcIndexes :: [FuncIndex]
} deriving (Show, Eq, Generic, NFData) } deriving (Show, Eq, Generic, NFData)
+4 -4
View File
@@ -177,7 +177,7 @@ checkMemoryInstr size memarg = do
Ctx { mems } <- ask Ctx { mems } <- ask
if length mems < 1 then throwError MemoryIndexOutOfRange else return () if length mems < 1 then throwError MemoryIndexOutOfRange else return ()
getInstrType :: Instruction -> Checker Arrow getInstrType :: Instruction Natural -> Checker Arrow
getInstrType Unreachable = return $ Any ==> Any getInstrType Unreachable = return $ Any ==> Any
getInstrType Nop = return $ empty ==> empty getInstrType Nop = return $ empty ==> empty
getInstrType Block { result, body } = do getInstrType Block { result, body } = do
@@ -375,10 +375,10 @@ replace :: (Eq a) => a -> a -> [a] -> [a]
replace _ _ [] = [] replace _ _ [] = []
replace x y (v:r) = (if x == v then y else v) : replace x y r replace x y (v:r) = (if x == v then y else v) : replace x y r
getExpressionType :: [Instruction] -> Checker Arrow getExpressionType :: Expression -> Checker Arrow
getExpressionType = fmap ([] `Arrow`) . foldM go [] getExpressionType = fmap ([] `Arrow`) . foldM go []
where where
go :: [VType] -> Instruction -> Checker [VType] go :: [VType] -> Instruction Natural -> Checker [VType]
go stack instr = do go stack instr = do
(f `Arrow` t) <- getInstrType instr (f `Arrow` t) <- getInstrType instr
matchStack stack (reverse f) t matchStack stack (reverse f) t
@@ -397,7 +397,7 @@ getExpressionType = fmap ([] `Arrow`) . foldM go []
matchStack [] args res = throwError $ TypeMismatch ((reverse args) `Arrow` res) ([] `Arrow` []) matchStack [] args res = throwError $ TypeMismatch ((reverse args) `Arrow` res) ([] `Arrow` [])
matchStack _ _ _ = error "inconsistent checker state" matchStack _ _ _ = error "inconsistent checker state"
isConstExpression :: [Instruction] -> Checker () isConstExpression :: Expression -> Checker ()
isConstExpression [] = return () isConstExpression [] = return ()
isConstExpression ((I32Const _):rest) = isConstExpression rest isConstExpression ((I32Const _):rest) = isConstExpression rest
isConstExpression ((I64Const _):rest) = isConstExpression rest isConstExpression ((I64Const _):rest) = isConstExpression rest
+1
View File
@@ -45,6 +45,7 @@ library
Language.Wasm.Interpreter Language.Wasm.Interpreter
Language.Wasm.Script Language.Wasm.Script
Language.Wasm.FloatUtils Language.Wasm.FloatUtils
Language.Wasm.Builder
Language.Wasm Language.Wasm
other-modules: other-modules:
Paths_wasm Paths_wasm