using primitive's ByteArray for memory instance's backend storage in the interpreter instead of IOVector

This commit is contained in:
Ilya Rezvov
2020-02-19 14:36:03 -08:00
parent f8d008bb04
commit 915b705ed2
2 changed files with 34 additions and 21 deletions
+33 -21
View File
@@ -1,6 +1,7 @@
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeApplications #-}
module Language.Wasm.Interpreter (
Value(..),
@@ -27,9 +28,10 @@ import qualified Data.ByteString.Lazy as LBS
import Data.Maybe (fromMaybe, isNothing)
import Data.Vector (Vector, (!), (!?), (//))
import Data.Vector.Storable.Mutable (IOVector)
import qualified Data.Vector as Vector
import qualified Data.Vector.Storable.Mutable as IOVector
import qualified Data.Primitive.ByteArray as ByteArray
import qualified Data.Primitive.Types as Primitive
import qualified Control.Monad.Primitive as Primitive
import Data.IORef (IORef, newIORef, readIORef, writeIORef)
import Data.Word (Word8, Word16, Word32, Word64)
import Data.Int (Int32, Int64)
@@ -164,9 +166,11 @@ data TableInstance = TableInstance {
elements :: Vector (Maybe Address)
}
type MemoryStore = ByteArray.MutableByteArray (Primitive.PrimState IO)
data MemoryInstance = MemoryInstance {
lim :: Limit,
memory :: IORef (IOVector Word8)
memory :: IORef MemoryStore
}
data GlobalInstance = GIConst ValueType Value | GIMut ValueType (IORef Value)
@@ -454,7 +458,9 @@ allocMems mems = Vector.fromList <$> mapM allocMem mems
where
allocMem :: Memory -> IO MemoryInstance
allocMem (Memory lim@(Limit from to)) = do
mem <- IOVector.replicate (fromIntegral from * pageSize) 0
let size = fromIntegral from * pageSize
mem <- ByteArray.newByteArray size
ByteArray.setByteArray @Word64 mem 0 (size `div` 8) 0
memory <- newIORef mem
return MemoryInstance {
lim,
@@ -496,7 +502,7 @@ initialize inst Module {elems, datas, start} store = do
let table = TableInstance lim (elems // zip [from..] (map Just funcs))
return st { tableInstances = tableInstances st Vector.// [(idx, table)] }
checkData :: Store -> DataSegment -> Initialize (Int, IOVector Word8, LBS.ByteString)
checkData :: Store -> DataSegment -> Initialize (Int, MemoryStore, LBS.ByteString)
checkData st DataSegment {memIndex, offset, chunk} = do
VI32 val <- liftIO $ evalConstExpr inst st offset
let from = fromIntegral val
@@ -504,13 +510,13 @@ initialize inst Module {elems, datas, start} store = do
let last = from + (fromIntegral $ LBS.length chunk)
let MemoryInstance _ memory = memInstances st ! idx
mem <- liftIO $ readIORef memory
let len = IOVector.length mem
len <- ByteArray.getSizeofMutableByteArray mem
Monad.when (last > len) $ throwError "data segment does not fit"
return (from, mem, chunk)
initData :: (Int, IOVector Word8, LBS.ByteString) -> Initialize ()
initData :: (Int, MemoryStore, LBS.ByteString) -> Initialize ()
initData (from, mem, chunk) =
mapM_ (\(i,b) -> IOVector.write mem i b) $ zip [from..] $ LBS.unpack chunk
mapM_ (\(i,b) -> ByteArray.writeByteArray mem i b) $ zip [from..] $ LBS.unpack chunk
instantiate :: Store -> Imports -> Valid.ValidModule -> IO (Either String (ModuleInstance, Store))
instantiate st imps mod = runExceptT $ do
@@ -583,28 +589,30 @@ eval budget store FunctionInstance { funcType, moduleInstance, code = Function {
Done ctx' -> go ctx' rest
command -> return command
makeLoadInstr :: (Bits i, Integral i) => EvalCtx -> Natural -> Int -> ([Value] -> i -> EvalResult) -> IO EvalResult
makeLoadInstr :: (Primitive.Prim i, Bits i, Integral i) => EvalCtx -> Natural -> Int -> ([Value] -> i -> EvalResult) -> IO EvalResult
makeLoadInstr ctx@EvalCtx{ stack = (VI32 v:rest) } offset byteWidth cont = do
let MemoryInstance { memory = memoryRef } = memInstances store ! (memaddrs moduleInstance ! 0)
memory <- readIORef memoryRef
let addr = fromIntegral v + fromIntegral offset
let readByte idx = do
byte <- IOVector.read memory $ addr + idx
byte <- ByteArray.readByteArray @Word8 memory $ addr + idx
return $ fromIntegral byte `shiftL` (idx * 8)
if addr + byteWidth > IOVector.length memory
len <- ByteArray.getSizeofMutableByteArray memory
if addr + byteWidth > len
then return Trap
else cont rest . sum <$> mapM readByte [0..byteWidth-1]
makeLoadInstr _ _ _ _ = error "Incorrect value on top of stack for memory instruction"
makeStoreInstr :: (Bits i, Integral i) => EvalCtx -> Natural -> Int -> i -> IO EvalResult
makeStoreInstr :: (Primitive.Prim i, Bits i, Integral i) => EvalCtx -> Natural -> Int -> i -> IO EvalResult
makeStoreInstr ctx@EvalCtx{ stack = (VI32 va:rest) } offset byteWidth v = do
let MemoryInstance { memory = memoryRef } = memInstances store ! (memaddrs moduleInstance ! 0)
memory <- readIORef memoryRef
let addr = fromIntegral $ va + fromIntegral offset
let writeByte idx = do
let byte = fromIntegral $ v `shiftR` (idx * 8) .&. 0xFF
IOVector.write memory (addr + idx) byte
if addr + byteWidth > IOVector.length memory
ByteArray.writeByteArray @Word8 memory (addr + idx) byte
len <- ByteArray.getSizeofMutableByteArray memory
if addr + byteWidth > len
then return Trap
else do
mapM_ writeByte [0..byteWidth-1]
@@ -769,19 +777,23 @@ eval budget store FunctionInstance { funcType, moduleInstance, code = Function {
step ctx@EvalCtx{ stack = st } CurrentMemory = do
let MemoryInstance { memory = memoryRef } = memInstances store ! (memaddrs moduleInstance ! 0)
memory <- readIORef memoryRef
let size = fromIntegral $ IOVector.length memory `div` pageSize
return $ Done ctx { stack = VI32 size : st }
size <- ((`quot` pageSize) . fromIntegral) <$> ByteArray.getSizeofMutableByteArray memory
return $ Done ctx { stack = VI32 (fromIntegral size) : st }
step ctx@EvalCtx{ stack = (VI32 n:rest) } GrowMemory = do
let MemoryInstance { lim = limit@(Limit _ maxLen), memory = memoryRef } = memInstances store ! (memaddrs moduleInstance ! 0)
memory <- readIORef memoryRef
let size = fromIntegral $ IOVector.length memory `quot` pageSize
size <- (`quot` pageSize) <$> ByteArray.getSizeofMutableByteArray memory
let growTo = size + fromIntegral n
let w64PageSize = fromIntegral $ pageSize `div` 8
result <- (
if fromMaybe True ((growTo <=) . fromIntegral <$> maxLen) && growTo <= 0xFFFF
then do
mem' <- IOVector.grow memory $ fromIntegral n * pageSize
writeIORef memoryRef mem'
return size
then (
if n == 0 then return size else do
mem' <- ByteArray.resizeMutableByteArray memory $ growTo * pageSize
ByteArray.setByteArray @Word64 mem' (size * w64PageSize) (fromIntegral n * w64PageSize) 0
writeIORef memoryRef mem'
return size
)
else return $ -1
)
return $ Done ctx { stack = VI32 (asWord32 $ fromIntegral result) : rest }
+1
View File
@@ -43,6 +43,7 @@ library
, transformers >=0.4 && <0.6
, utf8-string >=1.0
, cereal >= 0.5
, primitive >= 0.7
, vector >= 0.12
, ieee754 >= 0.8
, deepseq >= 1.4