43 Commits

Author SHA1 Message Date
Ilya Rezvov f300874996 fail on too big align value deserialization 2025-01-17 20:19:23 -07:00
Ilya Rezvov 37833b67c1 fix comments lexing 2025-01-15 17:31:51 -07:00
Ilya Rezvov 64e2e14562 validate table type for call_indirect 2025-01-15 17:26:16 -07:00
Ilya Rezvov 78a2f33176 update spec global imports 2025-01-15 17:18:28 -07:00
Ilya Rezvov 720c234df8 fix table import initialization 2025-01-15 17:01:21 -07:00
Ilya Rezvov 001f4c92a9 update tests 2025-01-14 11:52:06 -07:00
Ilya Rezvov b9bb378d21 implement all simd instructiions 2025-01-14 11:32:25 -07:00
Ilya Rezvov c96456414c implement simd integer narrow instruction 2025-01-14 10:42:18 -07:00
Ilya Rezvov 8742966ece implement more simd conversions 2025-01-13 20:32:47 -07:00
Ilya Rezvov 727fc8694b parse and implement simd_i32x4_trunc_sat_f64x2 instruction 2025-01-13 10:22:06 -07:00
Ilya Rezvov 662b57326c parse and implement I32x4TruncSatF32x4 instruction 2025-01-12 21:25:53 -07:00
Ilya Rezvov 6637aa7275 implement simd int extensions 2025-01-12 20:58:28 -07:00
Ilya Rezvov 10d1db7b96 parse most of conversion simd instructions 2023-09-25 21:26:39 -06:00
Ilya Rezvov 3d77f80484 implement pseudo min/max simd operations 2023-09-24 19:52:09 -06:00
Ilya Rezvov 902819aee7 implement extadd simd operation 2023-09-24 19:32:10 -06:00
Ilya Rezvov ac2a326f18 implement extmul operation 2023-09-24 19:20:21 -06:00
Ilya Rezvov 22198d8d76 implement rest of unary float operations for simd 2023-09-10 17:28:37 -06:00
Ilya Rezvov 368ab45de5 implement min and max ops 2023-09-10 17:10:35 -06:00
Ilya Rezvov 6298be49b8 implement float cmp operations for simd 2023-09-10 16:30:12 -06:00
Ilya Rezvov a4a96af6aa implement unary float operations for simd 2023-09-10 14:27:28 -06:00
Ilya Rezvov 0d8159e553 reimplement script value matching logic and implement part of simd float arithmetic 2023-09-10 10:30:40 -06:00
Ilya Rezvov 6d6d21265a implement lanewise integer cmp operations 2023-09-09 10:22:18 -06:00
Ilya Rezvov d98d1b718f implement v128.imul 2023-09-08 22:39:12 -06:00
Ilya Rezvov 369cad8a25 saturated ops, neg and abs 2023-09-08 22:30:37 -06:00
Ilya Rezvov 707288debd implement bit shift operations 2023-09-08 21:44:02 -06:00
Ilya Rezvov d25e96d91c implement ishape.bitmask 2023-09-08 19:11:58 -06:00
Ilya Rezvov 6bb5ea841c implement v128.load[8|16|32|64}_lane 2023-09-08 18:34:55 -06:00
Ilya Rezvov 3944bf9113 implement bitwise operations 2023-09-08 18:08:35 -06:00
Ilya Rezvov f16ccb85a9 implement v128.load with signed and unsigined extend 2023-09-08 11:32:13 -06:00
Ilya Rezvov dad521025e implement v128.load[32|64]_zero 2023-09-08 10:40:27 -06:00
Ilya Rezvov d406776095 implement v128.load[8|16|32|64]_splat 2023-09-08 10:29:30 -06:00
Ilya Rezvov d9803479a0 implement v128.store[8|16|32|64]_lane 2023-09-06 21:35:16 -06:00
Ilya Rezvov 5c6e9b59bc implement extract_lane and replace_lane 2023-09-06 21:09:23 -06:00
Ilya Rezvov 4302e4b32f parse shuffle and replace_lane 2023-09-05 22:38:47 -06:00
Ilya Rezvov b4acb0e1e8 implement i8x16.swizzle and extract_lane 2023-09-04 21:21:12 -06:00
Ilya Rezvov daa52f0c40 implement v128_splat instruction 2023-09-04 19:18:09 -06:00
Ilya Rezvov c6e0bfea89 implement basic load/store for v128 2023-09-04 18:35:59 -06:00
Ilya Rezvov d81143e5f8 implement binary for v128const 2023-09-04 18:02:00 -06:00
Ilya Rezvov 170d828713 Use ByteArray as V128 storage and basic const interpretation 2023-08-28 09:29:07 -06:00
Ilya Rezvov 6c2bbc8478 parse v128 literals 2023-08-27 21:24:23 -06:00
Ilya Rezvov 7810040226 all tests are green 2023-08-26 21:44:36 -06:00
Ilya Rezvov d112b28233 fix parsing of select instruction and evaluation of const expressions 2023-08-26 21:35:36 -06:00
Ilya Rezvov be13488f69 update tests and fix some 2023-08-23 22:58:19 -06:00
10 changed files with 2395 additions and 512 deletions
+19 -2
View File
@@ -1,6 +1,7 @@
{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeApplications #-}
module Language.Wasm.Binary ( module Language.Wasm.Binary (
dumpModule, dumpModule,
@@ -16,6 +17,8 @@ import Data.Bits
import Data.Word (Word8, Word32, Word64) import Data.Word (Word8, Word32, Word64)
import Data.Int (Int8, Int32, Int64) import Data.Int (Int8, Int32, Int64)
import Data.Serialize import Data.Serialize
import Control.Monad (when)
import Data.Primitive.ByteArray as BA
import qualified Data.ByteString as BS import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as LBS import qualified Data.ByteString.Lazy as LBS
import qualified Data.Text.Lazy as TL import qualified Data.Text.Lazy as TL
@@ -221,6 +224,7 @@ instance Serialize ValueType where
put I64 = putWord8 0x7E put I64 = putWord8 0x7E
put F32 = putWord8 0x7D put F32 = putWord8 0x7D
put F64 = putWord8 0x7C put F64 = putWord8 0x7C
put V128 = putWord8 0x7B
get = do get = do
op <- getWord8 op <- getWord8
@@ -229,6 +233,7 @@ instance Serialize ValueType where
0x7E -> return I64 0x7E -> return I64
0x7D -> return F32 0x7D -> return F32
0x7C -> return F64 0x7C -> return F64
0x7B -> return V128
_ -> fail "unexpected byte in value type position" _ -> fail "unexpected byte in value type position"
instance Serialize FuncType where instance Serialize FuncType where
@@ -336,6 +341,7 @@ instance Serialize MemArg where
put MemArg { align, offset } = putULEB128 align >> putULEB128 offset put MemArg { align, offset } = putULEB128 align >> putULEB128 offset
get = do get = do
align <- getULEB128 32 align <- getULEB128 32
when (align >= 32) $ fail "malformed memop flags"
offset <- getULEB128 32 offset <- getULEB128 32
return $ MemArg { align, offset } return $ MemArg { align, offset }
@@ -458,6 +464,10 @@ instance Serialize (Instruction Natural) where
put (I64Const val) = putWord8 0x42 >> putSLEB128 (asInt64 val) put (I64Const val) = putWord8 0x42 >> putSLEB128 (asInt64 val)
put (F32Const val) = putWord8 0x43 >> putFloat32le val put (F32Const val) = putWord8 0x43 >> putFloat32le val
put (F64Const val) = putWord8 0x44 >> putFloat64le val put (F64Const val) = putWord8 0x44 >> putFloat64le val
put (V128Const val) = do
putWord8 0xFD
putWord8 12
put $ BA.foldrByteArray @Word8 (:) [] val
put I32Eqz = putWord8 0x45 put I32Eqz = putWord8 0x45
put (IRelOp BS32 IEq) = putWord8 0x46 put (IRelOp BS32 IEq) = putWord8 0x46
put (IRelOp BS32 INe) = putWord8 0x47 put (IRelOp BS32 INe) = putWord8 0x47
@@ -803,6 +813,13 @@ instance Serialize (Instruction Natural) where
0x06 -> return $ ITruncSatFS BS64 BS64 0x06 -> return $ ITruncSatFS BS64 BS64
0x07 -> return $ ITruncSatFU BS64 BS64 0x07 -> return $ ITruncSatFU BS64 BS64
_ -> fail "Unknown byte value after misc instruction byte" _ -> fail "Unknown byte value after misc instruction byte"
0xFD -> do -- simd
ext <- getULEB128 32
case (ext :: Word32) of
0x0C -> do
bytes <- getByteString 16
return $ V128Const $ BA.byteArrayFromListN 16 $ BS.unpack bytes
_ -> fail "Unknown byte value after simd instruction byte"
byte -> fail $ "Unknown byte value in place of instruction opcode: " ++ (show byte) byte -> fail $ "Unknown byte value in place of instruction opcode: " ++ (show byte)
putExpression :: Expression -> Put putExpression :: Expression -> Put
@@ -877,10 +894,10 @@ instance Serialize ElemSegment where
putVec $ map Expr elements putVec $ map Expr elements
get = do get = do
op <- getWord8
let funcIndexes = map ((:[]) . RefFunc . unIndex) <$> getVec let funcIndexes = map ((:[]) . RefFunc . unIndex) <$> getVec
let elemKind = byteGuard 0x00 >> return FuncRef let elemKind = byteGuard 0x00 >> return FuncRef
case op of op <- getULEB128 32
case (op :: Word8) of
0x00 -> do 0x00 -> do
offset <- getExpression offset <- getExpression
ElemSegment FuncRef (Active 0 offset) <$> funcIndexes ElemSegment FuncRef (Active 0 offset) <$> funcIndexes
File diff suppressed because it is too large Load Diff
+23 -19
View File
@@ -39,13 +39,13 @@ $alpha = [$lower $upper]
$namepunct = [\! \# \$ \% \& \' \* \+ \- \. \/ \: \< \= \> \? \@ \ \^ \_ \` \| \~] $namepunct = [\! \# \$ \% \& \' \* \+ \- \. \/ \: \< \= \> \? \@ \ \^ \_ \` \| \~]
$idchar = [$digit $alpha $namepunct] $idchar = [$digit $alpha $namepunct]
$space = [\ \x09 \x0A \x0D] $space = [\ \x09 \x0A \x0D]
$linechar = [^ \x09] $linechar = [^ \x09 \x0A \x0D]
$sign = [\+ \-] $sign = [\+ \-]
$doublequote = \" $doublequote = \"
@keyword = $lower $idchar* @keyword = $lower $idchar*
@reserved = $idchar+ @reserved = $idchar+
@linecomment = ";;" $linechar* \x0A @linecomment = ";;" $linechar* [\x0A \x0D]
@startblockcomment = "(;" @startblockcomment = "(;"
@endblockcomment = ";)" @endblockcomment = ";)"
@num = $digit (\_? $digit+)* @num = $digit (\_? $digit+)*
@@ -80,10 +80,10 @@ tokens :-
<0> @id { tokenStr TId } <0> @id { tokenStr TId }
<0> "(" { constToken TOpenBracket } <0> "(" { constToken TOpenBracket }
<0> ")" { constToken TCloseBracket } <0> ")" { constToken TCloseBracket }
<0> $sign? @hexfloat { parseHexFloat }
<0> $sign? @num { parseDecimalSignedInt } <0> $sign? @num { parseDecimalSignedInt }
<0> $sign? "0x" @hexnum { parseHexalSignedInt } <0> $sign? "0x" @hexnum { parseHexalSignedInt }
<0> $sign? @float { parseDecFloat } <0> $sign? @float { parseDecFloat }
<0> $sign? @hexfloat { parseHexFloat }
<0, blockComment> @startblockcomment { startBlockComment } <0, blockComment> @startblockcomment { startBlockComment }
<blockComment> [.\n] ; <blockComment> [.\n] ;
<blockComment> @endblockcomment { endBlockComment } <blockComment> @endblockcomment { endBlockComment }
@@ -119,22 +119,22 @@ minusNaN = negate nan
inf = infinity inf = infinity
minusInf = -infinity minusInf = -infinity
parseSign :: (Num a) => LBS.ByteString -> ((a -> a), Int64) parseSign :: (Num a) => LBS.ByteString -> ((a -> a), Int64, Maybe Bool)
parseSign str = parseSign str =
let Just (ch, _) = LBSUtf8.decode str in let Just (ch, _) = LBSUtf8.decode str in
case ch of case ch of
'-' -> (negate, 1) '-' -> (negate, 1, Just True)
'+' -> (abs, 1) '+' -> (abs, 1, Just False)
otherwise -> (abs, 0) otherwise -> (abs, 0, Nothing)
{-# SPECIALIZE parseSign :: LBS.ByteString -> ((Integer -> Integer), Int64) #-} {-# SPECIALIZE parseSign :: LBS.ByteString -> ((Integer -> Integer), Int64, Maybe Bool) #-}
{-# SPECIALIZE parseSign :: LBS.ByteString -> ((Double -> Double), Int64) #-} {-# SPECIALIZE parseSign :: LBS.ByteString -> ((Double -> Double), Int64, Maybe Bool) #-}
parseHexalSignedInt :: AlexAction Lexeme parseHexalSignedInt :: AlexAction Lexeme
parseHexalSignedInt = token $ \(pos, _, s, _) len -> parseHexalSignedInt = token $ \(pos, _, s, _) len ->
let (sign, slen) = parseSign s in let (sign, slen, nat) = parseSign s in
let num = readHexFromPrefix (len - 2 - slen) $ LBSUtf8.drop (2 + slen) s in let num = readHexFromPrefix (len - 2 - slen) $ LBSUtf8.drop (2 + slen) s in
Lexeme (Just pos) $ TIntLit $ sign num Lexeme (Just pos) $ TIntLit nat $ sign num
parseNanSigned :: AlexAction Lexeme parseNanSigned :: AlexAction Lexeme
parseNanSigned = token $ \(pos, _, s, _) len -> parseNanSigned = token $ \(pos, _, s, _) len ->
@@ -148,9 +148,9 @@ parseNanSigned = token $ \(pos, _, s, _) len ->
parseDecimalSignedInt :: AlexAction Lexeme parseDecimalSignedInt :: AlexAction Lexeme
parseDecimalSignedInt = token $ \(pos, _, s, _) len -> parseDecimalSignedInt = token $ \(pos, _, s, _) len ->
let (sign, slen) = parseSign s in let (sign, slen, nat) = parseSign s in
let num = readDecFromPrefix (len - slen) $ LBSUtf8.drop slen s in let num = readDecFromPrefix (len - slen) $ LBSUtf8.drop slen s in
Lexeme (Just pos) $ TIntLit $ sign num Lexeme (Just pos) $ TIntLit nat $ sign num
parseDecFloat :: AlexAction Lexeme parseDecFloat :: AlexAction Lexeme
parseDecFloat = token $ \(pos, _, s, _) len -> parseDecFloat = token $ \(pos, _, s, _) len ->
@@ -226,11 +226,13 @@ readHexFloat toFloat sz expLimit manitisaSize str = do
then ([True], 0, exp' + 1) then ([True], 0, exp' + 1)
else (rounded, 1, exp') else (rounded, 1, exp')
else (rounded, 0, exp') else (rounded, 0, exp')
if exp'' > expLimit || exp'' < (negate $ expLimit + manitisaSize) then Left "constant out of range" else return () e <- if exp'' > expLimit then Left "const out of range"
if exp'' >= (negate $ expLimit - 1) else if exp'' < (negate $ expLimit + manitisaSize) then return $ negate $ expLimit + manitisaSize + 1
then return $ toFloat $ sign .|. ((fromIntegral $ exp'' + expLimit) `shiftL` manitisaSize) .|. ((fromBits (tail bits') + a) `shiftL` (manitisaSize + 1 - length bits')) else return exp''
if e >= (negate $ expLimit - 1)
then return $ toFloat $ sign .|. ((fromIntegral $ e + expLimit) `shiftL` manitisaSize) .|. ((fromBits (tail bits') + a) `shiftL` (manitisaSize + 1 - length bits'))
else do else do
let shift = expLimit + manitisaSize - length bits' - abs exp'' let shift = expLimit + manitisaSize - length bits' - abs e
if shift < 0 if shift < 0
then return $ toFloat sign then return $ toFloat sign
else return $ toFloat $ sign .|. ((fromBits bits' + a) `shiftL` shift) else return $ toFloat $ sign .|. ((fromBits bits' + a) `shiftL` shift)
@@ -299,7 +301,9 @@ endBlockComment _inp _len = do
alexMonadScan alexMonadScan
startStringLiteral :: AlexAction Lexeme startStringLiteral :: AlexAction Lexeme
startStringLiteral _inp _len = do startStringLiteral (_, prev, _, _) _len = do
when (prev `notElem` "() \x09\x0A\x0D")
$ alexError "string literal should start after space or parent character"
alexSetStartCode stringLiteral alexSetStartCode stringLiteral
setLexerStringFlag True setLexerStringFlag True
alexMonadScan alexMonadScan
@@ -362,7 +366,7 @@ data NaN
deriving (Show, Eq) deriving (Show, Eq)
data Token = TKeyword LBS.ByteString data Token = TKeyword LBS.ByteString
| TIntLit Integer | TIntLit {- Natural -} (Maybe Bool) Integer
| TFloatLit FloatRep | TFloatLit FloatRep
| TStringLit LBS.ByteString | TStringLit LBS.ByteString
| TId LBS.ByteString | TId LBS.ByteString
+1126 -421
View File
File diff suppressed because it is too large Load Diff
+47 -15
View File
@@ -11,6 +11,7 @@ import qualified Data.Text.Lazy.Encoding as TLEncoding
import qualified Control.Monad.State as State import qualified Control.Monad.State as State
import Control.Monad.IO.Class (liftIO) import Control.Monad.IO.Class (liftIO)
import Numeric.IEEE (identicalIEEE) import Numeric.IEEE (identicalIEEE)
import qualified Data.Primitive.ByteArray as ByteArray
import qualified Control.DeepSeq as DeepSeq import qualified Control.DeepSeq as DeepSeq
import Data.Maybe (fromJust, isNothing) import Data.Maybe (fromJust, isNothing)
import Debug.Trace (trace) import Debug.Trace (trace)
@@ -30,6 +31,9 @@ import qualified Language.Wasm.Structure as Struct
import qualified Language.Wasm.Parser as Parser import qualified Language.Wasm.Parser as Parser
import qualified Language.Wasm.Lexer as Lexer import qualified Language.Wasm.Lexer as Lexer
import qualified Language.Wasm.Binary as Binary import qualified Language.Wasm.Binary as Binary
import Language.Wasm.FloatUtils (floatToWord, wordToFloat, doubleToWord, wordToDouble)
import Numeric.IEEE (nan)
import Data.Bits ((.&.))
type OnAssertFail = String -> Assertion -> IO () type OnAssertFail = String -> Assertion -> IO ()
@@ -56,6 +60,7 @@ runScript onAssertFail script = do
(st, inst) <- Interpreter.makeHostModule Interpreter.emptyStore [ (st, inst) <- Interpreter.makeHostModule Interpreter.emptyStore [
("print", hostPrint []), ("print", hostPrint []),
("print_i32", hostPrint [Struct.I32]), ("print_i32", hostPrint [Struct.I32]),
("print_i64", hostPrint [Struct.I64]),
("print_i32_f32", hostPrint [Struct.I32, Struct.F32]), ("print_i32_f32", hostPrint [Struct.I32, Struct.F32]),
("print_f64_f64", hostPrint [Struct.F64, Struct.F64]), ("print_f64_f64", hostPrint [Struct.F64, Struct.F64]),
("print_f32", hostPrint [Struct.F32]), ("print_f32", hostPrint [Struct.F32]),
@@ -73,8 +78,8 @@ runScript onAssertFail script = do
hostGlobals = do hostGlobals = do
let globI32 = Interpreter.makeConstGlobal $ Interpreter.VI32 666 let globI32 = Interpreter.makeConstGlobal $ Interpreter.VI32 666
let globI64 = Interpreter.makeConstGlobal $ Interpreter.VI64 666 let globI64 = Interpreter.makeConstGlobal $ Interpreter.VI64 666
let globF32 = Interpreter.makeConstGlobal $ Interpreter.VF32 666 let globF32 = Interpreter.makeConstGlobal $ Interpreter.VF32 666.6
let globF64 = Interpreter.makeConstGlobal $ Interpreter.VF64 666 let globF64 = Interpreter.makeConstGlobal $ Interpreter.VF64 666.6
return ( return (
Interpreter.HostGlobal globI32, Interpreter.HostGlobal globI32,
Interpreter.HostGlobal globI64, Interpreter.HostGlobal globI64,
@@ -118,16 +123,23 @@ 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.Expression -> Interpreter.Value asArg :: Parser.ValuePattern -> Interpreter.Value
asArg [Struct.I32Const v] = Interpreter.VI32 v asArg (Parser.ExactValue (Struct.I32Const v)) = Interpreter.VI32 v
asArg [Struct.F32Const v] = Interpreter.VF32 v asArg (Parser.ExactValue (Struct.F32Const v)) = Interpreter.VF32 v
asArg [Struct.I64Const v] = Interpreter.VI64 v asArg (Parser.ExactValue (Struct.I64Const v)) = Interpreter.VI64 v
asArg [Struct.F64Const v] = Interpreter.VF64 v asArg (Parser.ExactValue (Struct.F64Const v)) = Interpreter.VF64 v
asArg [Struct.RefNull Struct.FuncRef] = Interpreter.RF Nothing asArg (Parser.ExactValue (Struct.V128Const v)) = Interpreter.VV128 v
asArg [Struct.RefNull Struct.ExternRef] = Interpreter.RE Nothing asArg (Parser.ExactValue (Struct.RefNull Struct.FuncRef)) = Interpreter.RF Nothing
asArg [Struct.RefExtern v] = Interpreter.RE (Just v) asArg (Parser.ExactValue (Struct.RefNull Struct.ExternRef))= Interpreter.RE Nothing
asArg (Parser.ExactValue (Struct.RefExtern v)) = Interpreter.RE (Just v)
asArg expr = error $ "Only const instructions supported as arguments for actions: " ++ show expr asArg expr = error $ "Only const instructions supported as arguments for actions: " ++ show expr
showArg :: Parser.ValuePattern -> String
showArg v@(Parser.ExactValue _) = show $ asArg v
showArg Parser.CanonicalNan = "nan:canonical"
showArg Parser.ArithmeticNan = "nan:arithmetic"
showArg (Parser.VectorPat _ pat) = show $ showArg <$> pat
runAction :: ScriptState -> Action -> IO (Maybe [Interpreter.Value]) runAction :: ScriptState -> Action -> IO (Maybe [Interpreter.Value])
runAction st (Invoke ident name args) = do runAction st (Invoke ident name args) = do
case getModule st ident of case getModule st ident of
@@ -141,12 +153,31 @@ runScript onAssertFail script = do
isValueEqual :: Interpreter.Value -> Interpreter.Value -> Bool isValueEqual :: Interpreter.Value -> Interpreter.Value -> Bool
isValueEqual (Interpreter.VI32 v1) (Interpreter.VI32 v2) = v1 == v2 isValueEqual (Interpreter.VI32 v1) (Interpreter.VI32 v2) = v1 == v2
isValueEqual (Interpreter.VI64 v1) (Interpreter.VI64 v2) = v1 == v2 isValueEqual (Interpreter.VI64 v1) (Interpreter.VI64 v2) = v1 == v2
isValueEqual (Interpreter.VF32 v1) (Interpreter.VF32 v2) = (isNaN v1 && isNaN v2) || identicalIEEE v1 v2 isValueEqual (Interpreter.VF32 v1) (Interpreter.VF32 v2) = identicalIEEE v1 v2
isValueEqual (Interpreter.VF64 v1) (Interpreter.VF64 v2) = (isNaN v1 && isNaN v2) || identicalIEEE v1 v2 isValueEqual (Interpreter.VF64 v1) (Interpreter.VF64 v2) = identicalIEEE v1 v2
isValueEqual (Interpreter.VV128 a) (Interpreter.VV128 b) = ByteArray.compareByteArrays a 0 b 0 16 == EQ
isValueEqual (Interpreter.RF f1) (Interpreter.RF f2) = f1 == f2 isValueEqual (Interpreter.RF f1) (Interpreter.RF f2) = f1 == f2
isValueEqual (Interpreter.RE e1) (Interpreter.RE e2) = e1 == e2 isValueEqual (Interpreter.RE e1) (Interpreter.RE e2) = e1 == e2
isValueEqual _ _ = False isValueEqual _ _ = False
isValueMatch :: Interpreter.Value -> Parser.ValuePattern -> Bool
isValueMatch val v@(Parser.ExactValue _) = isValueEqual val $ asArg v
isValueMatch (Interpreter.VF32 v) Parser.CanonicalNan = identicalIEEE v nan || identicalIEEE v (abs nan)
isValueMatch (Interpreter.VF32 v) Parser.ArithmeticNan =
let posNan = 0x7F800000 in
floatToWord v .&. posNan == posNan
isValueMatch (Interpreter.VF64 v) Parser.CanonicalNan = identicalIEEE v nan || identicalIEEE v (abs nan)
isValueMatch (Interpreter.VF64 v) Parser.ArithmeticNan =
let posNan = 0x7FF0000000000000 in
doubleToWord v .&. posNan == posNan
isValueMatch (Interpreter.VV128 v) (Parser.VectorPat Struct.F32x4 pat) =
let vals = Interpreter.VF32 . wordToFloat . ByteArray.indexByteArray v <$> [0..3] in
and $ zipWith isValueMatch vals pat
isValueMatch (Interpreter.VV128 v) (Parser.VectorPat Struct.F64x2 pat) =
let vals = Interpreter.VF64 . wordToDouble . ByteArray.indexByteArray v <$> [0, 1] in
and $ zipWith isValueMatch vals pat
isValueMatch _ _ = False
isNaNReturned :: Action -> Assertion -> AssertM () isNaNReturned :: Action -> Assertion -> AssertM ()
isNaNReturned action assert = do isNaNReturned action assert = do
result <- runActionInAssert action result <- runActionInAssert action
@@ -184,6 +215,7 @@ runScript onAssertFail script = do
getFailureString (Validate.FunctionIndexOutOfRange idx) = ["unknown function", "unknown function " <> TL.pack (show idx)] getFailureString (Validate.FunctionIndexOutOfRange idx) = ["unknown function", "unknown function " <> TL.pack (show idx)]
getFailureString (Validate.GlobalIndexOutOfRange idx) = ["unknown global", "unknown global " <> TL.pack (show idx)] getFailureString (Validate.GlobalIndexOutOfRange idx) = ["unknown global", "unknown global " <> TL.pack (show idx)]
getFailureString Validate.LabelIndexOutOfRange = ["unknown label"] getFailureString Validate.LabelIndexOutOfRange = ["unknown label"]
getFailureString Validate.LaneIndexOutOfRange = ["invalid lane index"]
getFailureString Validate.TypeIndexOutOfRange = ["unknown type"] getFailureString Validate.TypeIndexOutOfRange = ["unknown type"]
getFailureString Validate.MinMoreThanMaxInMemoryLimit = ["size minimum must not be greater than maximum"] getFailureString Validate.MinMoreThanMaxInMemoryLimit = ["size minimum must not be greater than maximum"]
getFailureString Validate.MemoryLimitExceeded = ["memory size must be at most 65536 pages (4GiB)"] getFailureString Validate.MemoryLimitExceeded = ["memory size must be at most 65536 pages (4GiB)"]
@@ -215,10 +247,10 @@ runScript onAssertFail script = do
result <- runActionInAssert action result <- runActionInAssert action
case result of case result of
Just result -> do Just result -> do
if length result == length expected && (all id $ zipWith isValueEqual result (map asArg expected)) if length result == length expected && (all id $ zipWith isValueMatch result expected)
then return () then return ()
else printFailedAssert ("Expected " ++ show (map asArg expected) ++ ", but action returned " ++ show result) assert else printFailedAssert ("Expected " ++ show (map showArg expected) ++ ", but action returned " ++ show result) assert
Nothing -> printFailedAssert ("Expected " ++ show (map asArg expected) ++ ", but action returned Trap") assert Nothing -> printFailedAssert ("Expected " ++ show (map showArg expected) ++ ", but action returned Trap") assert
runAssert assert@(AssertReturnCanonicalNaN action) = isNaNReturned action assert runAssert assert@(AssertReturnCanonicalNaN action) = isNaNReturned action assert
runAssert assert@(AssertReturnArithmeticNaN action) = isNaNReturned action assert runAssert assert@(AssertReturnArithmeticNaN action) = isNaNReturned action assert
runAssert assert@(AssertInvalid moduleDef failureString) = runAssert assert@(AssertInvalid moduleDef failureString) =
+62 -2
View File
@@ -33,6 +33,7 @@ module Language.Wasm.Structure (
FuncType(..), FuncType(..),
ValueType(..), ValueType(..),
BlockType(..), BlockType(..),
SimdShape(..),
ParamsType, ParamsType,
ResultType, ResultType,
LocalsType, LocalsType,
@@ -53,12 +54,15 @@ module Language.Wasm.Structure (
import Numeric.Natural (Natural) import Numeric.Natural (Natural)
import Data.Word (Word32, Word64) import Data.Word (Word32, Word64)
import qualified Data.Primitive.ByteArray as ByteArray
import qualified Data.ByteString.Lazy as LBS import qualified Data.ByteString.Lazy as LBS
import qualified Data.Text.Lazy as TL import qualified Data.Text.Lazy as TL
import Control.DeepSeq (NFData) import Control.DeepSeq (NFData)
import GHC.Generics (Generic) import GHC.Generics (Generic)
data BitSize = BS32 | BS64 deriving (Show, Eq, Generic, NFData) data SimdShape = I8x16 | I16x8 | I32x4 | I64x2 | F32x4 | F64x2 | I128x1 deriving (Show, Eq, Generic, NFData)
data BitSize = BS32 | BS64 | BS128 SimdShape deriving (Show, Eq, Generic, NFData)
data IUnOp = data IUnOp =
IClz IClz
@@ -67,17 +71,27 @@ data IUnOp =
| IExtend8S | IExtend8S
| IExtend16S | IExtend16S
| IExtend32S | IExtend32S
| INot
| IAbs
| INeg
| IExtAddPairwise {- Signed -} Bool
deriving (Show, Eq, Generic, NFData) deriving (Show, Eq, Generic, NFData)
data IBinOp = data IBinOp =
IAdd IAdd
| ISub | ISub
| IAddSatS
| ISubSatS
| IAddSatU
| ISubSatU
| IAvgrU
| IMul | IMul
| IDivU | IDivU
| IDivS | IDivS
| IRemU | IRemU
| IRemS | IRemS
| IAnd | IAnd
| IAndNot
| IOr | IOr
| IXor | IXor
| IShl | IShl
@@ -85,13 +99,18 @@ data IBinOp =
| IShrS | IShrS
| IRotl | IRotl
| IRotr | IRotr
| IMinU
| IMinS
| IMaxU
| IMaxS
| IExtMul {- Signed -} Bool {- High -} Bool
deriving (Show, Eq, Generic, NFData) deriving (Show, Eq, Generic, NFData)
data IRelOp = IEq | INe | ILtU | ILtS | IGtU | IGtS | ILeU | ILeS | IGeU | IGeS deriving (Show, Eq, Generic, NFData) data IRelOp = IEq | INe | ILtU | ILtS | IGtU | IGtS | ILeU | ILeS | IGeU | IGeS deriving (Show, Eq, Generic, NFData)
data FUnOp = FAbs | FNeg | FCeil | FFloor | FTrunc | FNearest | FSqrt deriving (Show, Eq, Generic, NFData) data FUnOp = FAbs | FNeg | FCeil | FFloor | FTrunc | FNearest | FSqrt deriving (Show, Eq, Generic, NFData)
data FBinOp = FAdd | FSub | FMul | FDiv | FMin | FMax | FCopySign deriving (Show, Eq, Generic, NFData) data FBinOp = FAdd | FSub | FMul | FDiv | FMin | FMax | FCopySign | FPMin | FPMax deriving (Show, Eq, Generic, NFData)
data FRelOp = FEq | FNe | FLt | FGt | FLe | FGe deriving (Show, Eq, Generic, NFData) data FRelOp = FEq | FNe | FLt | FGt | FLe | FGe deriving (Show, Eq, Generic, NFData)
@@ -112,6 +131,7 @@ data ValueType =
| I64 | I64
| F32 | F32
| F64 | F64
| V128
| Func | Func
| Extern | Extern
deriving (Show, Eq, Generic, NFData) deriving (Show, Eq, Generic, NFData)
@@ -159,6 +179,23 @@ data Instruction index =
| I64Load MemArg | I64Load MemArg
| F32Load MemArg | F32Load MemArg
| F64Load MemArg | F64Load MemArg
| V128Load MemArg
| V128Load8Lane MemArg Natural
| V128Load16Lane MemArg Natural
| V128Load32Lane MemArg Natural
| V128Load64Lane MemArg Natural
| V128Load8Splat MemArg
| V128Load16Splat MemArg
| V128Load32Splat MemArg
| V128Load64Splat MemArg
| V128Load32Zero MemArg
| V128Load64Zero MemArg
| V128Load8x8S MemArg
| V128Load8x8U MemArg
| V128Load16x4S MemArg
| V128Load16x4U MemArg
| V128Load32x2S MemArg
| V128Load32x2U MemArg
| I32Load8S MemArg | I32Load8S MemArg
| I32Load8U MemArg | I32Load8U MemArg
| I32Load16S MemArg | I32Load16S MemArg
@@ -173,6 +210,11 @@ data Instruction index =
| I64Store MemArg | I64Store MemArg
| F32Store MemArg | F32Store MemArg
| F64Store MemArg | F64Store MemArg
| V128Store MemArg
| V128Store8Lane MemArg Natural
| V128Store16Lane MemArg Natural
| V128Store32Lane MemArg Natural
| V128Store64Lane MemArg Natural
| I32Store8 MemArg | I32Store8 MemArg
| I32Store16 MemArg | I32Store16 MemArg
| I64Store8 MemArg | I64Store8 MemArg
@@ -198,6 +240,7 @@ data Instruction index =
| I64Const Word64 | I64Const Word64
| F32Const Float | F32Const Float
| F64Const Double | F64Const Double
| V128Const ByteArray.ByteArray
| IUnOp BitSize IUnOp | IUnOp BitSize IUnOp
| IBinOp BitSize IBinOp | IBinOp BitSize IBinOp
| I32Eqz | I32Eqz
@@ -219,6 +262,23 @@ data Instruction index =
| F64PromoteF32 | F64PromoteF32
| IReinterpretF BitSize | IReinterpretF BitSize
| FReinterpretI BitSize | FReinterpretI BitSize
-- Vector instructions
| V128Splat SimdShape
| V128ExtractLane SimdShape index {- signed -} Bool
| V128ReplaceLane SimdShape index
| V128AllTrue SimdShape
| V128BitMask SimdShape
| V128AnyTrue
| V128BitSelect
| I8x16Swizzle
| I8x16Shuffle [Int]
| V128Narrow SimdShape SimdShape {- signed -} Bool
| F64x2PromoteLowF32x4
| F32x4DemoteF64x2Zero
| V128IExtend SimdShape SimdShape {- high -} Bool {- signed -} Bool
| I32x4TruncSatF {- signed -} Bool {- Float Size -} BitSize
| I32x4DotI16x8S
| I16x8Q15MulrSatS
deriving (Show, Eq, Generic, NFData) deriving (Show, Eq, Generic, NFData)
type Expression = [Instruction Natural] type Expression = [Instruction Natural]
+160 -15
View File
@@ -40,6 +40,7 @@ data ValidationError =
| ElemIndexOutOfRange Natural | ElemIndexOutOfRange Natural
| DataIndexOutOfRange Natural | DataIndexOutOfRange Natural
| LabelIndexOutOfRange | LabelIndexOutOfRange
| LaneIndexOutOfRange
| TypeIndexOutOfRange | TypeIndexOutOfRange
| ResultTypeDoesntMatch | ResultTypeDoesntMatch
| TypeMismatch { actual :: Arrow, expected :: Arrow } | TypeMismatch { actual :: Arrow, expected :: Arrow }
@@ -141,7 +142,7 @@ isArrowMatch (f `Arrow` t) ( f' `Arrow` t') = isEndMatch f f' && isEndMatch t t'
data Ctx = Ctx { data Ctx = Ctx {
types :: [FuncType], types :: [FuncType],
funcs :: [FuncType], funcs :: [FuncType],
tables :: [TableType], tableTypes :: [TableType],
elems :: [ElemType], elems :: [ElemType],
datas :: [DataMode], datas :: [DataMode],
mems :: [Limit], mems :: [Limit],
@@ -192,10 +193,13 @@ getLabel lbl = do
withLabel :: [ValueType] -> Checker a -> Checker a withLabel :: [ValueType] -> Checker a -> Checker a
withLabel result = withReaderT (\ctx -> ctx { labels = result : labels ctx }) withLabel result = withReaderT (\ctx -> ctx { labels = result : labels ctx })
isMemArgValid :: Int -> MemArg -> Checker () isMemArgValid :: Natural -> MemArg -> Checker ()
isMemArgValid sizeInBytes MemArg { align } = if 2 ^ align <= sizeInBytes then return () else throwError AlignmentOverflow isMemArgValid sizeInBytes MemArg { align } =
if 2 ^ align <= sizeInBytes
then return ()
else throwError AlignmentOverflow
checkMemoryInstr :: Int -> MemArg -> Checker () checkMemoryInstr :: Natural -> MemArg -> Checker ()
checkMemoryInstr size memarg = do checkMemoryInstr size memarg = do
isMemArgValid size memarg isMemArgValid size memarg
Ctx { mems } <- ask Ctx { mems } <- ask
@@ -273,10 +277,12 @@ getInstrType _ (Call fun) = do
Ctx { funcs } <- ask Ctx { funcs } <- ask
maybeToEither (FunctionIndexOutOfRange fun) $ asArrow <$> funcs !? fun maybeToEither (FunctionIndexOutOfRange fun) $ asArrow <$> funcs !? fun
getInstrType _ (CallIndirect tableIdx sign) = do getInstrType _ (CallIndirect tableIdx sign) = do
Ctx { types, tables } <- ask Ctx { types, tableTypes = tables } <- ask
if length tables <= fromIntegral tableIdx if length tables <= fromIntegral tableIdx
then throwError (TableIndexOutOfRange tableIdx) then throwError (TableIndexOutOfRange tableIdx)
else do else do
let TableType _ elemType = tables !! fromIntegral tableIdx
when (elemType /= FuncRef) $ throwError (RefTypeMismatch FuncRef ExternRef)
Arrow from to <- maybeToEither TypeIndexOutOfRange $ asArrow <$> types !? sign Arrow from to <- maybeToEither TypeIndexOutOfRange $ asArrow <$> types !? sign
return $ (from ++ [Val I32]) ==> to return $ (from ++ [Val I32]) ==> to
getInstrType _ Drop = do getInstrType _ Drop = do
@@ -336,6 +342,61 @@ getInstrType _ (F32Load memarg) = do
getInstrType _ (F64Load memarg) = do getInstrType _ (F64Load memarg) = do
checkMemoryInstr 8 memarg checkMemoryInstr 8 memarg
return $ I32 ==> F64 return $ I32 ==> F64
getInstrType _ (V128Load memarg) = do
checkMemoryInstr 16 memarg
return $ I32 ==> V128
getInstrType _ (V128Load8Lane memarg idx) = do
checkMemoryInstr 1 memarg
when (idx >= 16) $ throwError LaneIndexOutOfRange
return $ [I32, V128] ==> V128
getInstrType _ (V128Load16Lane memarg idx) = do
checkMemoryInstr 2 memarg
when (idx >= 8) $ throwError LaneIndexOutOfRange
return $ [I32, V128] ==> V128
getInstrType _ (V128Load32Lane memarg idx) = do
checkMemoryInstr 4 memarg
when (idx >= 4) $ throwError LaneIndexOutOfRange
return $ [I32, V128] ==> V128
getInstrType _ (V128Load64Lane memarg idx) = do
checkMemoryInstr 8 memarg
when (idx >= 2) $ throwError LaneIndexOutOfRange
return $ [I32, V128] ==> V128
getInstrType _ (V128Load8Splat memarg) = do
checkMemoryInstr 1 memarg
return $ I32 ==> V128
getInstrType _ (V128Load16Splat memarg) = do
checkMemoryInstr 2 memarg
return $ I32 ==> V128
getInstrType _ (V128Load32Splat memarg) = do
checkMemoryInstr 4 memarg
return $ I32 ==> V128
getInstrType _ (V128Load64Splat memarg) = do
checkMemoryInstr 8 memarg
return $ I32 ==> V128
getInstrType _ (V128Load32Zero memarg) = do
checkMemoryInstr 4 memarg
return $ I32 ==> V128
getInstrType _ (V128Load64Zero memarg) = do
checkMemoryInstr 8 memarg
return $ I32 ==> V128
getInstrType _ (V128Load8x8S memarg) = do
checkMemoryInstr 8 memarg
return $ I32 ==> V128
getInstrType _ (V128Load8x8U memarg) = do
checkMemoryInstr 8 memarg
return $ I32 ==> V128
getInstrType _ (V128Load16x4S memarg) = do
checkMemoryInstr 8 memarg
return $ I32 ==> V128
getInstrType _ (V128Load16x4U memarg) = do
checkMemoryInstr 8 memarg
return $ I32 ==> V128
getInstrType _ (V128Load32x2S memarg) = do
checkMemoryInstr 8 memarg
return $ I32 ==> V128
getInstrType _ (V128Load32x2U memarg) = do
checkMemoryInstr 8 memarg
return $ I32 ==> V128
getInstrType _ (I32Load8S memarg) = do getInstrType _ (I32Load8S memarg) = do
checkMemoryInstr 1 memarg checkMemoryInstr 1 memarg
return $ I32 ==> I32 return $ I32 ==> I32
@@ -378,6 +439,25 @@ getInstrType _ (F32Store memarg) = do
getInstrType _ (F64Store memarg) = do getInstrType _ (F64Store memarg) = do
checkMemoryInstr 8 memarg checkMemoryInstr 8 memarg
return $ [I32, F64] ==> empty return $ [I32, F64] ==> empty
getInstrType _ (V128Store memarg) = do
checkMemoryInstr 16 memarg
return $ [I32, V128] ==> empty
getInstrType _ (V128Store8Lane memarg idx) = do
checkMemoryInstr 1 memarg
when (idx >= 16) $ throwError LaneIndexOutOfRange
return $ [I32, V128] ==> empty
getInstrType _ (V128Store16Lane memarg idx) = do
checkMemoryInstr 2 memarg
when (idx >= 8) $ throwError LaneIndexOutOfRange
return $ [I32, V128] ==> empty
getInstrType _ (V128Store32Lane memarg idx) = do
checkMemoryInstr 4 memarg
when (idx >= 4) $ throwError LaneIndexOutOfRange
return $ [I32, V128] ==> empty
getInstrType _ (V128Store64Lane memarg idx) = do
checkMemoryInstr 8 memarg
when (idx >= 2) $ throwError LaneIndexOutOfRange
return $ [I32, V128] ==> empty
getInstrType _ (I32Store8 memarg) = do getInstrType _ (I32Store8 memarg) = do
checkMemoryInstr 1 memarg checkMemoryInstr 1 memarg
return $ [I32, I32] ==> empty return $ [I32, I32] ==> empty
@@ -419,7 +499,7 @@ getInstrType _ (DataDrop dataIdx) = do
when (length datas <= fromIntegral dataIdx) $ throwError (DataIndexOutOfRange dataIdx) when (length datas <= fromIntegral dataIdx) $ throwError (DataIndexOutOfRange dataIdx)
return $ empty ==> empty return $ empty ==> empty
getInstrType _ (TableInit tableIdx elemIdx) = do getInstrType _ (TableInit tableIdx elemIdx) = do
Ctx { tables, elems } <- ask Ctx { tableTypes = tables, elems } <- ask
when (length tables <= fromIntegral tableIdx) $ throwError (TableIndexOutOfRange tableIdx) when (length tables <= fromIntegral tableIdx) $ throwError (TableIndexOutOfRange tableIdx)
when (length elems <= fromIntegral elemIdx) $ throwError (ElemIndexOutOfRange elemIdx) when (length elems <= fromIntegral elemIdx) $ throwError (ElemIndexOutOfRange elemIdx)
let TableType _ tableType = tables !! fromIntegral tableIdx let TableType _ tableType = tables !! fromIntegral tableIdx
@@ -427,7 +507,7 @@ getInstrType _ (TableInit tableIdx elemIdx) = do
when (elemType /= tableType) $ throwError (RefTypeMismatch tableType elemType) when (elemType /= tableType) $ throwError (RefTypeMismatch tableType elemType)
return $ [I32, I32, I32] ==> empty return $ [I32, I32, I32] ==> empty
getInstrType _ (TableCopy toIdx fromIdx) = do getInstrType _ (TableCopy toIdx fromIdx) = do
Ctx { tables } <- ask Ctx { tableTypes = tables } <- ask
let (from, to) = (fromIntegral fromIdx, fromIntegral toIdx) let (from, to) = (fromIntegral fromIdx, fromIntegral toIdx)
when (length tables <= from) $ throwError (TableIndexOutOfRange fromIdx) when (length tables <= from) $ throwError (TableIndexOutOfRange fromIdx)
when (length tables <= to) $ throwError (TableIndexOutOfRange toIdx) when (length tables <= to) $ throwError (TableIndexOutOfRange toIdx)
@@ -436,26 +516,26 @@ getInstrType _ (TableCopy toIdx fromIdx) = do
when (fromType /= toType) $ throwError (RefTypeMismatch fromType toType) when (fromType /= toType) $ throwError (RefTypeMismatch fromType toType)
return $ [I32, I32, I32] ==> empty return $ [I32, I32, I32] ==> empty
getInstrType _ (TableFill tableIdx) = do getInstrType _ (TableFill tableIdx) = do
Ctx { tables } <- ask Ctx { tableTypes = tables } <- ask
when (length tables <= fromIntegral tableIdx) $ throwError (TableIndexOutOfRange tableIdx) when (length tables <= fromIntegral tableIdx) $ throwError (TableIndexOutOfRange tableIdx)
let TableType _ tableType = tables !! fromIntegral tableIdx let TableType _ tableType = tables !! fromIntegral tableIdx
return $ [I32, elemTypeToRefType tableType, I32] ==> empty return $ [I32, elemTypeToRefType tableType, I32] ==> empty
getInstrType _ (TableSize tableIdx) = do getInstrType _ (TableSize tableIdx) = do
Ctx { tables } <- ask Ctx { tableTypes = tables } <- ask
when (length tables <= fromIntegral tableIdx) $ throwError (TableIndexOutOfRange tableIdx) when (length tables <= fromIntegral tableIdx) $ throwError (TableIndexOutOfRange tableIdx)
return $ empty ==> I32 return $ empty ==> I32
getInstrType _ (TableGrow tableIdx) = do getInstrType _ (TableGrow tableIdx) = do
Ctx { tables } <- ask Ctx { tableTypes = tables } <- ask
when (length tables <= fromIntegral tableIdx) $ throwError (TableIndexOutOfRange tableIdx) when (length tables <= fromIntegral tableIdx) $ throwError (TableIndexOutOfRange tableIdx)
let TableType _ tableType = tables !! fromIntegral tableIdx let TableType _ tableType = tables !! fromIntegral tableIdx
return $ [elemTypeToRefType tableType, I32] ==> I32 return $ [elemTypeToRefType tableType, I32] ==> I32
getInstrType _ (TableGet tableIdx) = do getInstrType _ (TableGet tableIdx) = do
Ctx { tables } <- ask Ctx { tableTypes = tables } <- ask
when (length tables <= fromIntegral tableIdx) $ throwError (TableIndexOutOfRange tableIdx) when (length tables <= fromIntegral tableIdx) $ throwError (TableIndexOutOfRange tableIdx)
let TableType _ tableType = tables !! fromIntegral tableIdx let TableType _ tableType = tables !! fromIntegral tableIdx
return $ I32 ==> (elemTypeToRefType tableType) return $ I32 ==> (elemTypeToRefType tableType)
getInstrType _ (TableSet tableIdx) = do getInstrType _ (TableSet tableIdx) = do
Ctx { tables } <- ask Ctx { tableTypes = tables } <- ask
when (length tables <= fromIntegral tableIdx) $ throwError (TableIndexOutOfRange tableIdx) when (length tables <= fromIntegral tableIdx) $ throwError (TableIndexOutOfRange tableIdx)
let TableType _ tableType = tables !! fromIntegral tableIdx let TableType _ tableType = tables !! fromIntegral tableIdx
return $ [I32, elemTypeToRefType tableType] ==> empty return $ [I32, elemTypeToRefType tableType] ==> empty
@@ -467,20 +547,29 @@ getInstrType _ (I32Const _) = return $ empty ==> I32
getInstrType _ (I64Const _) = return $ empty ==> I64 getInstrType _ (I64Const _) = return $ empty ==> I64
getInstrType _ (F32Const _) = return $ empty ==> F32 getInstrType _ (F32Const _) = return $ empty ==> F32
getInstrType _ (F64Const _) = return $ empty ==> F64 getInstrType _ (F64Const _) = return $ empty ==> F64
getInstrType _ (V128Const _) = return $ empty ==> V128
getInstrType _ (IUnOp BS32 _) = return $ I32 ==> I32 getInstrType _ (IUnOp BS32 _) = return $ I32 ==> I32
getInstrType _ (IUnOp BS64 _) = return $ I64 ==> I64 getInstrType _ (IUnOp BS64 _) = return $ I64 ==> I64
getInstrType _ (IUnOp (BS128 _) _) = return $ V128 ==> V128
getInstrType _ (IBinOp BS32 _) = return $ [I32, I32] ==> I32 getInstrType _ (IBinOp BS32 _) = return $ [I32, I32] ==> I32
getInstrType _ (IBinOp BS64 _) = return $ [I64, I64] ==> I64 getInstrType _ (IBinOp BS64 _) = return $ [I64, I64] ==> I64
getInstrType _ (IBinOp (BS128 _) op) | op == IShl || op == IShrS || op == IShrU =
return $ [V128, I32] ==> V128
getInstrType _ (IBinOp (BS128 _) _) = return $ [V128, V128] ==> V128
getInstrType _ I32Eqz = return $ I32 ==> I32 getInstrType _ I32Eqz = return $ I32 ==> I32
getInstrType _ I64Eqz = return $ I64 ==> I32 getInstrType _ I64Eqz = return $ I64 ==> I32
getInstrType _ (IRelOp BS32 _) = return $ [I32, I32] ==> I32 getInstrType _ (IRelOp BS32 _) = return $ [I32, I32] ==> I32
getInstrType _ (IRelOp BS64 _) = return $ [I64, I64] ==> I32 getInstrType _ (IRelOp BS64 _) = return $ [I64, I64] ==> I32
getInstrType _ (IRelOp (BS128 _) _) = return $ [V128, V128] ==> V128
getInstrType _ (FUnOp BS32 _) = return $ F32 ==> F32 getInstrType _ (FUnOp BS32 _) = return $ F32 ==> F32
getInstrType _ (FUnOp BS64 _) = return $ F64 ==> F64 getInstrType _ (FUnOp BS64 _) = return $ F64 ==> F64
getInstrType _ (FUnOp (BS128 _) _) = return $ V128 ==> V128
getInstrType _ (FBinOp BS32 _) = return $ [F32, F32] ==> F32 getInstrType _ (FBinOp BS32 _) = return $ [F32, F32] ==> F32
getInstrType _ (FBinOp BS64 _) = return $ [F64, F64] ==> F64 getInstrType _ (FBinOp BS64 _) = return $ [F64, F64] ==> F64
getInstrType _ (FBinOp (BS128 _) _) = return $ [V128, V128] ==> V128
getInstrType _ (FRelOp BS32 _) = return $ [F32, F32] ==> I32 getInstrType _ (FRelOp BS32 _) = return $ [F32, F32] ==> I32
getInstrType _ (FRelOp BS64 _) = return $ [F64, F64] ==> I32 getInstrType _ (FRelOp BS64 _) = return $ [F64, F64] ==> I32
getInstrType _ (FRelOp (BS128 _) _) = return $ [V128, V128] ==> V128
getInstrType _ I32WrapI64 = return $ I64 ==> I32 getInstrType _ I32WrapI64 = return $ I64 ==> I32
getInstrType _ (ITruncFU BS32 BS32) = return $ F32 ==> I32 getInstrType _ (ITruncFU BS32 BS32) = return $ F32 ==> I32
getInstrType _ (ITruncFU BS32 BS64) = return $ F64 ==> I32 getInstrType _ (ITruncFU BS32 BS64) = return $ F64 ==> I32
@@ -504,17 +593,71 @@ getInstrType _ (FConvertIU BS32 BS32) = return $ I32 ==> F32
getInstrType _ (FConvertIU BS32 BS64) = return $ I64 ==> F32 getInstrType _ (FConvertIU BS32 BS64) = return $ I64 ==> F32
getInstrType _ (FConvertIU BS64 BS32) = return $ I32 ==> F64 getInstrType _ (FConvertIU BS64 BS32) = return $ I32 ==> F64
getInstrType _ (FConvertIU BS64 BS64) = return $ I64 ==> F64 getInstrType _ (FConvertIU BS64 BS64) = return $ I64 ==> F64
getInstrType _ (FConvertIU (BS128 _) (BS128 _)) = return $ V128 ==> V128
getInstrType _ (FConvertIS BS32 BS32) = return $ I32 ==> F32 getInstrType _ (FConvertIS BS32 BS32) = return $ I32 ==> F32
getInstrType _ (FConvertIS BS32 BS64) = return $ I64 ==> F32 getInstrType _ (FConvertIS BS32 BS64) = return $ I64 ==> F32
getInstrType _ (FConvertIS BS64 BS32) = return $ I32 ==> F64 getInstrType _ (FConvertIS BS64 BS32) = return $ I32 ==> F64
getInstrType _ (FConvertIS BS64 BS64) = return $ I64 ==> F64 getInstrType _ (FConvertIS BS64 BS64) = return $ I64 ==> F64
getInstrType _ (FConvertIS (BS128 _) (BS128 _)) = return $ V128 ==> V128
getInstrType _ F32DemoteF64 = return $ F64 ==> F32 getInstrType _ F32DemoteF64 = return $ F64 ==> F32
getInstrType _ F64PromoteF32 = return $ F32 ==> F64 getInstrType _ F64PromoteF32 = return $ F32 ==> F64
getInstrType _ (IReinterpretF BS32) = return $ F32 ==> I32 getInstrType _ (IReinterpretF BS32) = return $ F32 ==> I32
getInstrType _ (IReinterpretF BS64) = return $ F64 ==> I64 getInstrType _ (IReinterpretF BS64) = return $ F64 ==> I64
getInstrType _ (FReinterpretI BS32) = return $ I32 ==> F32 getInstrType _ (FReinterpretI BS32) = return $ I32 ==> F32
getInstrType _ (FReinterpretI BS64) = return $ I64 ==> F64 getInstrType _ (FReinterpretI BS64) = return $ I64 ==> F64
getInstrType _ I8x16Swizzle =
return $ [V128, V128] ==> V128
getInstrType _ (I8x16Shuffle idxs) = do
when (any (>= 32) idxs) $ throwError LaneIndexOutOfRange
return $ [V128, V128] ==> V128
getInstrType _ (V128Splat shape) =
return $ getShapeElemType shape ==> V128
getInstrType _ (V128ExtractLane shape idx _) = do
when (idx >= lanesCount shape) $ throwError LaneIndexOutOfRange
return $ V128 ==> getShapeElemType shape
getInstrType _ (V128ReplaceLane shape idx) = do
when (idx >= lanesCount shape) $ throwError LaneIndexOutOfRange
return $ [V128, getShapeElemType shape] ==> V128
getInstrType _ (V128AllTrue _) =
return $ V128 ==> I32
getInstrType _ V128AnyTrue =
return $ V128 ==> I32
getInstrType _ V128BitSelect =
return $ [V128, V128, V128] ==> V128
getInstrType _ (V128BitMask _) =
return $ V128 ==> I32
getInstrType _ (V128Narrow _ _ _) =
return $ [V128, V128] ==> V128
getInstrType _ F64x2PromoteLowF32x4 =
return $ V128 ==> V128
getInstrType _ F32x4DemoteF64x2Zero =
return $ V128 ==> V128
getInstrType _ (V128IExtend _ _ _ _) =
return $ V128 ==> V128
getInstrType _ (I32x4TruncSatF _ _) =
return $ V128 ==> V128
getInstrType _ I32x4DotI16x8S =
return $[V128, V128] ==> V128
getInstrType _ I16x8Q15MulrSatS =
return $ [V128, V128] ==> V128
getShapeElemType :: SimdShape -> ValueType
getShapeElemType I8x16 = I32
getShapeElemType I16x8 = I32
getShapeElemType I32x4 = I32
getShapeElemType I64x2 = I64
getShapeElemType F32x4 = F32
getShapeElemType F64x2 = F64
lanesCount :: SimdShape -> Natural
lanesCount shape = case shape of
I8x16 -> 16
I16x8 -> 8
I32x4 -> 4
I64x2 -> 2
F32x4 -> 4
F64x2 -> 2
I128x1 -> 1
replace :: (Eq a) => a -> a -> [a] -> [a] replace :: (Eq a) => a -> a -> [a] -> [a]
replace _ _ [] = [] replace _ _ [] = []
@@ -575,6 +718,7 @@ isConstExpression ((I32Const _):rest) = isConstExpression rest
isConstExpression ((I64Const _):rest) = isConstExpression rest isConstExpression ((I64Const _):rest) = isConstExpression rest
isConstExpression ((F32Const _):rest) = isConstExpression rest isConstExpression ((F32Const _):rest) = isConstExpression rest
isConstExpression ((F64Const _):rest) = isConstExpression rest isConstExpression ((F64Const _):rest) = isConstExpression rest
isConstExpression ((V128Const _):rest) = isConstExpression rest
isConstExpression ((RefNull _):rest) = isConstExpression rest isConstExpression ((RefNull _):rest) = isConstExpression rest
isConstExpression ((RefFunc _):rest) = isConstExpression rest isConstExpression ((RefFunc _):rest) = isConstExpression rest
isConstExpression ((GetGlobal idx):rest) = do isConstExpression ((GetGlobal idx):rest) = do
@@ -604,7 +748,7 @@ ctxFromModule locals labels returns m =
Ctx { Ctx {
types, types,
funcs = getFuncTypes m, funcs = getFuncTypes m,
tables = tableImports ++ map (\(Table t) -> t) tables, tableTypes = tableImports ++ map (\(Table t) -> t) tables,
elems = map elemType elems, elems = map elemType elems,
datas = map dataMode datas, datas = map dataMode datas,
mems = memsImports ++ map (\(Memory l) -> l) mems, mems = memsImports ++ map (\(Memory l) -> l) mems,
@@ -706,8 +850,6 @@ elemsShouldBeValid m@Module { elems, functions, tables, imports } =
where where
isElemValid :: Ctx -> ElemSegment -> ValidationResult isElemValid :: Ctx -> ElemSegment -> ValidationResult
isElemValid ctx (ElemSegment elemType mode elements) = do isElemValid ctx (ElemSegment elemType mode elements) = do
unless (elemType == FuncRef)
$ throwError $ RefTypeMismatch FuncRef elemType
forM_ elements $ \elem -> runChecker ctx $ do forM_ elements $ \elem -> runChecker ctx $ do
arr <- getExpressionType elem arr <- getExpressionType elem
isConstExpression elem isConstExpression elem
@@ -722,6 +864,9 @@ elemsShouldBeValid m@Module { elems, functions, tables, imports } =
let tableImports = filter isTableImport imports let tableImports = filter isTableImport imports
when (tableIdx >= fromIntegral (length tableImports + length tables)) $ do when (tableIdx >= fromIntegral (length tableImports + length tables)) $ do
throwError $ TableIndexOutOfRange tableIdx throwError $ TableIndexOutOfRange tableIdx
let TableType _ tableType = tableTypes ctx !! (fromIntegral tableIdx)
when (tableType /= elemType) $ do
throwError $ RefTypeMismatch elemType tableType
_ -> return () _ -> return ()
isValidRef :: ElemType -> Arrow -> Bool isValidRef :: ElemType -> Arrow -> Bool
+2 -2
View File
@@ -17,9 +17,9 @@ import qualified Data.List as List
main :: IO () main :: IO ()
main = do main = do
files <- files <-
filter (not . List.isPrefixOf "simd") . filter (List.isSuffixOf ".wast") filter (List.isSuffixOf ".wast")
<$> Directory.listDirectory "tests/spec" <$> Directory.listDirectory "tests/spec"
-- let files = ["bulk.wast"] -- let files = ["align.wast"]
scriptTestCases <- (`mapM` files) $ \file -> do scriptTestCases <- (`mapM` files) $ \file -> do
test <- LBS.readFile ("tests/spec/" ++ file) test <- LBS.readFile ("tests/spec/" ++ file)
return $ testCase file $ do return $ testCase file $ do
+1 -1
View File
@@ -56,7 +56,7 @@ library
, happy:happy >=1.9.4 && < 1.21 , happy:happy >=1.9.4 && < 1.21
build-depends: build-depends:
array >=0.5 && < 0.6 array >=0.5 && < 0.6
, base >=4.6 && < 5 , base >=4.11 && < 5
, bytestring >=0.10 && < 0.12 , bytestring >=0.10 && < 0.12
, cereal >=0.5 && < 0.6 , cereal >=0.5 && < 0.6
, containers >=0.5 && < 0.7 , containers >=0.5 && < 0.7