forked from GitHub/haskell-wasm
1027 lines
36 KiB
Haskell
1027 lines
36 KiB
Haskell
{-# LANGUAGE DuplicateRecordFields #-}
|
|
{-# LANGUAGE NamedFieldPuns #-}
|
|
{-# LANGUAGE OverloadedStrings #-}
|
|
{-# LANGUAGE GADTs #-}
|
|
{-# LANGUAGE RankNTypes #-}
|
|
{-# LANGUAGE TypeFamilies #-}
|
|
{-# LANGUAGE DataKinds #-}
|
|
{-# LANGUAGE TypeOperators #-}
|
|
{-# LANGUAGE PolyKinds #-}
|
|
{-# LANGUAGE TypeApplications #-}
|
|
{-# LANGUAGE UndecidableInstances #-}
|
|
{-# LANGUAGE TypeInType #-}
|
|
{-# LANGUAGE TypeSynonymInstances #-}
|
|
{-# LANGUAGE FlexibleInstances #-}
|
|
{-# LANGUAGE ScopedTypeVariables #-}
|
|
|
|
module Language.Wasm.Builder (
|
|
GenMod,
|
|
genMod,
|
|
global, typedef, fun, funRec, declare, implement, table, memory, dataSegment,
|
|
importFunction, importGlobal, importMemory, importTable,
|
|
export,
|
|
nextFuncIndex, setGlobalInitializer,
|
|
GenFun,
|
|
Glob, Loc, Fn(..), Mem, Tbl, Label,
|
|
param, local, label,
|
|
ret,
|
|
arg,
|
|
i32, i64, f32, f64,
|
|
i32c, i64c, f32c, f64c,
|
|
add, inc, sub, dec, mul, div_u, div_s, rem_u, rem_s, and, or, xor, shl, shr_u, shr_s, rotl, rotr,
|
|
clz, ctz, popcnt,
|
|
eq, ne, lt_s, lt_u, gt_s, gt_u, le_s, le_u, ge_s, ge_u,
|
|
eqz,
|
|
div_f, min_f, max_f, copySign,
|
|
abs_f, neg_f, ceil_f, floor_f, trunc_f, nearest_f, sqrt_f,
|
|
lt_f, gt_f, le_f, ge_f,
|
|
wrap, trunc_s, trunc_u, extend_s, extend_u, convert_s, convert_u, demote, promote, reinterpret,
|
|
load, load8_u, load8_s, load16_u, load16_s, load32_u, load32_s,
|
|
store, store8, store16, store32,
|
|
memorySize, growMemory,
|
|
nop, Language.Wasm.Builder.drop, select,
|
|
call, callIndirect, finish, br, brIf, brTable,
|
|
if', loop, {-block,-} when, for, while,
|
|
trap, unreachable,
|
|
appendExpr, after,
|
|
Producer, OutType, produce, Consumer, (.=)
|
|
) where
|
|
|
|
import Prelude hiding (and, or)
|
|
import qualified Data.List as List
|
|
import qualified Data.Maybe as Maybe
|
|
import Control.Monad.State (State, execState, get, gets, put, modify)
|
|
import Control.Monad.Reader (ReaderT, ask, runReaderT)
|
|
import Numeric.Natural
|
|
import Data.Word (Word32, Word64)
|
|
import Data.Int (Int32, Int64)
|
|
import Data.Proxy
|
|
|
|
import qualified Data.Text.Lazy as TL
|
|
import qualified Data.ByteString.Lazy as LBS
|
|
|
|
import Language.Wasm.Structure
|
|
|
|
data FuncDef = FuncDef {
|
|
args :: [ValueType],
|
|
returns :: [ValueType],
|
|
locals :: [ValueType],
|
|
instrs :: Expression
|
|
} deriving (Show, Eq)
|
|
|
|
type GenFun = ReaderT Natural (State FuncDef)
|
|
|
|
genExpr :: Natural -> GenFun a -> Expression
|
|
genExpr deep gen = instrs $ flip execState (FuncDef [] [] [] []) $ runReaderT gen deep
|
|
|
|
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
|
|
|
|
data TypedExpr
|
|
= ExprI32 (GenFun (Proxy I32))
|
|
| ExprI64 (GenFun (Proxy I64))
|
|
| ExprF32 (GenFun (Proxy F32))
|
|
| ExprF64 (GenFun (Proxy F64))
|
|
|
|
class Producer expr where
|
|
type OutType expr
|
|
asTypedExpr :: expr -> TypedExpr
|
|
asValueType :: expr -> ValueType
|
|
produce :: expr -> GenFun (OutType expr)
|
|
|
|
instance (ValueTypeable t) => Producer (Loc t) where
|
|
type OutType (Loc t) = Proxy t
|
|
asTypedExpr e = case getValueType (t e) of
|
|
I32 -> ExprI32 (produce e >> return Proxy)
|
|
I64 -> ExprI64 (produce e >> return Proxy)
|
|
F32 -> ExprF32 (produce e >> return Proxy)
|
|
F64 -> ExprF64 (produce e >> return Proxy)
|
|
where
|
|
t :: Loc t -> Proxy t
|
|
t _ = Proxy
|
|
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
|
|
asTypedExpr e = case getValueType (t e) of
|
|
I32 -> ExprI32 (produce e >> return Proxy)
|
|
I64 -> ExprI64 (produce e >> return Proxy)
|
|
F32 -> ExprF32 (produce e >> return Proxy)
|
|
F64 -> ExprF64 (produce e >> return Proxy)
|
|
where
|
|
t :: Glob t -> Proxy t
|
|
t _ = Proxy
|
|
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
|
|
asTypedExpr e = case getValueType (t e) of
|
|
I32 -> ExprI32 (produce e >> return Proxy)
|
|
I64 -> ExprI64 (produce e >> return Proxy)
|
|
F32 -> ExprF32 (produce e >> return Proxy)
|
|
F64 -> ExprF64 (produce e >> return Proxy)
|
|
where
|
|
t :: GenFun (Proxy t) -> Proxy t
|
|
t _ = Proxy
|
|
asValueType e = getValueType (t e)
|
|
where
|
|
t :: GenFun (Proxy t) -> Proxy t
|
|
t _ = Proxy
|
|
produce = id
|
|
|
|
ret :: (Producer expr) => expr -> GenFun (OutType expr)
|
|
ret = produce
|
|
|
|
arg :: (Producer expr) => expr -> GenFun ()
|
|
arg e = produce e >> return ()
|
|
|
|
getSize :: ValueType -> BitSize
|
|
getSize I32 = BS32
|
|
getSize I64 = BS64
|
|
getSize F32 = BS32
|
|
getSize F64 = BS64
|
|
|
|
type family IsInt i :: Bool where
|
|
IsInt (Proxy I32) = True
|
|
IsInt (Proxy I64) = True
|
|
IsInt any = False
|
|
|
|
type family IsFloat i :: Bool where
|
|
IsFloat (Proxy F32) = True
|
|
IsFloat (Proxy F64) = True
|
|
IsFloat any = False
|
|
|
|
nop :: GenFun ()
|
|
nop = appendExpr [Nop]
|
|
|
|
drop :: (Producer val) => val -> GenFun ()
|
|
drop val = do
|
|
produce val
|
|
appendExpr [Drop]
|
|
|
|
select :: (Producer a, Producer b, OutType a ~ OutType b, Producer pred, OutType pred ~ Proxy I32) => pred -> a -> b -> GenFun (OutType a)
|
|
select pred a b = select' (produce pred) (produce a) (produce b)
|
|
where
|
|
select' :: GenFun pred -> GenFun val -> GenFun val -> GenFun val
|
|
select' pred a b = do
|
|
a
|
|
res <- b
|
|
pred
|
|
appendExpr [Select]
|
|
return res
|
|
|
|
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)
|
|
|
|
iUnOp :: (Producer a, IsInt (OutType a) ~ True) => IUnOp -> a -> GenFun (OutType a)
|
|
iUnOp op a = after [IUnOp (getSize $ asValueType a) op] (produce a)
|
|
|
|
iRelOp :: (Producer a, Producer b, OutType a ~ OutType b, IsInt (OutType a) ~ True) => IRelOp -> a -> b -> GenFun (Proxy I32)
|
|
iRelOp op a b = do
|
|
produce a
|
|
produce b
|
|
appendExpr [IRelOp (getSize $ asValueType a) op]
|
|
return Proxy
|
|
|
|
add :: (Producer a, Producer b, OutType a ~ OutType b) => a -> b -> GenFun (OutType a)
|
|
add a b = do
|
|
produce a
|
|
case asValueType a of
|
|
I32 -> after [IBinOp BS32 IAdd] (produce b)
|
|
I64 -> after [IBinOp BS64 IAdd] (produce b)
|
|
F32 -> after [FBinOp BS32 FAdd] (produce b)
|
|
F64 -> after [FBinOp BS64 FAdd] (produce b)
|
|
|
|
inc :: (Consumer a, Producer a, Integral i) => i -> a -> GenFun ()
|
|
inc i a = case asTypedExpr a of
|
|
ExprI32 e -> a .= (e `add` i32c i)
|
|
ExprI64 e -> a .= (e `add` i64c i)
|
|
ExprF32 e -> a .= (e `add` f32c (fromIntegral i))
|
|
ExprF64 e -> a .= (e `add` f64c (fromIntegral i))
|
|
|
|
sub :: (Producer a, Producer b, OutType a ~ OutType b) => a -> b -> GenFun (OutType a)
|
|
sub a b = do
|
|
produce a
|
|
case asValueType a of
|
|
I32 -> after [IBinOp BS32 ISub] (produce b)
|
|
I64 -> after [IBinOp BS64 ISub] (produce b)
|
|
F32 -> after [FBinOp BS32 FSub] (produce b)
|
|
F64 -> after [FBinOp BS64 FSub] (produce b)
|
|
|
|
dec :: (Consumer a, Producer a, Integral i) => i -> a -> GenFun ()
|
|
dec i a = case asTypedExpr a of
|
|
ExprI32 e -> a .= (e `sub` i32c i)
|
|
ExprI64 e -> a .= (e `sub` i64c i)
|
|
ExprF32 e -> a .= (e `sub` f32c (fromIntegral i))
|
|
ExprF64 e -> a .= (e `sub` f64c (fromIntegral i))
|
|
|
|
mul :: (Producer a, Producer b, OutType a ~ OutType b) => a -> b -> GenFun (OutType a)
|
|
mul a b = do
|
|
produce a
|
|
case asValueType a of
|
|
I32 -> after [IBinOp BS32 IMul] (produce b)
|
|
I64 -> after [IBinOp BS64 IMul] (produce b)
|
|
F32 -> after [FBinOp BS32 FMul] (produce b)
|
|
F64 -> after [FBinOp BS64 FMul] (produce b)
|
|
|
|
div_u :: (Producer a, Producer b, OutType a ~ OutType b, IsInt (OutType a) ~ True) => a -> b -> GenFun (OutType a)
|
|
div_u = iBinOp IDivU
|
|
|
|
div_s :: (Producer a, Producer b, OutType a ~ OutType b, IsInt (OutType a) ~ True) => a -> b -> GenFun (OutType a)
|
|
div_s = iBinOp IDivS
|
|
|
|
rem_u :: (Producer a, Producer b, OutType a ~ OutType b, IsInt (OutType a) ~ True) => a -> b -> GenFun (OutType a)
|
|
rem_u = iBinOp IRemU
|
|
|
|
rem_s :: (Producer a, Producer b, OutType a ~ OutType b, IsInt (OutType a) ~ True) => a -> b -> GenFun (OutType a)
|
|
rem_s = iBinOp IRemS
|
|
|
|
and :: (Producer a, Producer b, OutType a ~ OutType b, IsInt (OutType a) ~ True) => a -> b -> GenFun (OutType a)
|
|
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
|
|
|
|
shl :: (Producer a, Producer b, OutType a ~ OutType b, IsInt (OutType a) ~ True) => a -> b -> GenFun (OutType a)
|
|
shl = iBinOp IShl
|
|
|
|
shr_u :: (Producer a, Producer b, OutType a ~ OutType b, IsInt (OutType a) ~ True) => a -> b -> GenFun (OutType a)
|
|
shr_u = iBinOp IShrU
|
|
|
|
shr_s :: (Producer a, Producer b, OutType a ~ OutType b, IsInt (OutType a) ~ True) => a -> b -> GenFun (OutType a)
|
|
shr_s = iBinOp IShrS
|
|
|
|
rotl :: (Producer a, Producer b, OutType a ~ OutType b, IsInt (OutType a) ~ True) => a -> b -> GenFun (OutType a)
|
|
rotl = iBinOp IRotl
|
|
|
|
rotr :: (Producer a, Producer b, OutType a ~ OutType b, IsInt (OutType a) ~ True) => a -> b -> GenFun (OutType a)
|
|
rotr = iBinOp IRotr
|
|
|
|
clz :: (Producer a, IsInt (OutType a) ~ True) => a -> GenFun (OutType a)
|
|
clz = iUnOp IClz
|
|
|
|
ctz :: (Producer a, IsInt (OutType a) ~ True) => a -> GenFun (OutType a)
|
|
ctz = iUnOp ICtz
|
|
|
|
popcnt :: (Producer a, IsInt (OutType a) ~ True) => a -> GenFun (OutType a)
|
|
popcnt = iUnOp IPopcnt
|
|
|
|
eq :: (Producer a, Producer b, OutType a ~ OutType b) => a -> b -> GenFun (Proxy I32)
|
|
eq a b = do
|
|
produce a
|
|
produce b
|
|
case asValueType a of
|
|
I32 -> appendExpr [IRelOp BS32 IEq]
|
|
I64 -> appendExpr [IRelOp BS64 IEq]
|
|
F32 -> appendExpr [FRelOp BS32 FEq]
|
|
F64 -> appendExpr [FRelOp BS64 FEq]
|
|
return Proxy
|
|
|
|
ne :: (Producer a, Producer b, OutType a ~ OutType b) => a -> b -> GenFun (Proxy I32)
|
|
ne a b = do
|
|
produce a
|
|
produce b
|
|
case asValueType a of
|
|
I32 -> appendExpr [IRelOp BS32 INe]
|
|
I64 -> appendExpr [IRelOp BS64 INe]
|
|
F32 -> appendExpr [FRelOp BS32 FNe]
|
|
F64 -> appendExpr [FRelOp BS64 FNe]
|
|
return Proxy
|
|
|
|
lt_s :: (Producer a, Producer b, OutType a ~ OutType b, IsInt (OutType a) ~ True) => a -> b -> GenFun (Proxy I32)
|
|
lt_s = iRelOp ILtS
|
|
|
|
lt_u :: (Producer a, Producer b, OutType a ~ OutType b, IsInt (OutType a) ~ True) => a -> b -> GenFun (Proxy I32)
|
|
lt_u = iRelOp ILtU
|
|
|
|
gt_s :: (Producer a, Producer b, OutType a ~ OutType b, IsInt (OutType a) ~ True) => a -> b -> GenFun (Proxy I32)
|
|
gt_s = iRelOp IGtS
|
|
|
|
gt_u :: (Producer a, Producer b, OutType a ~ OutType b, IsInt (OutType a) ~ True) => a -> b -> GenFun (Proxy I32)
|
|
gt_u = iRelOp IGtU
|
|
|
|
le_s :: (Producer a, Producer b, OutType a ~ OutType b, IsInt (OutType a) ~ True) => a -> b -> GenFun (Proxy I32)
|
|
le_s = iRelOp ILeS
|
|
|
|
le_u :: (Producer a, Producer b, OutType a ~ OutType b, IsInt (OutType a) ~ True) => a -> b -> GenFun (Proxy I32)
|
|
le_u = iRelOp ILeU
|
|
|
|
ge_s :: (Producer a, Producer b, OutType a ~ OutType b, IsInt (OutType a) ~ True) => a -> b -> GenFun (Proxy I32)
|
|
ge_s = iRelOp IGeS
|
|
|
|
ge_u :: (Producer a, Producer b, OutType a ~ OutType b, IsInt (OutType a) ~ True) => a -> b -> GenFun (Proxy I32)
|
|
ge_u = iRelOp IGeU
|
|
|
|
eqz :: (Producer a, IsInt (OutType a) ~ True) => a -> GenFun (Proxy I32)
|
|
eqz a = do
|
|
produce a
|
|
case asValueType a of
|
|
I32 -> appendExpr [I32Eqz]
|
|
I64 -> appendExpr [I64Eqz]
|
|
_ -> error "Impossible by type constraint"
|
|
return Proxy
|
|
|
|
fBinOp :: (Producer a, Producer b, OutType a ~ OutType b, IsFloat (OutType a) ~ True) => FBinOp -> a -> b -> GenFun (OutType a)
|
|
fBinOp op a b = produce a >> after [FBinOp (getSize $ asValueType a) op] (produce b)
|
|
|
|
fUnOp :: (Producer a, IsFloat (OutType a) ~ True) => FUnOp -> a -> GenFun (OutType a)
|
|
fUnOp op a = after [FUnOp (getSize $ asValueType a) op] (produce a)
|
|
|
|
fRelOp :: (Producer a, Producer b, OutType a ~ OutType b, IsFloat (OutType a) ~ True) => FRelOp -> a -> b -> GenFun (Proxy I32)
|
|
fRelOp op a b = do
|
|
produce a
|
|
produce b
|
|
appendExpr [FRelOp (getSize $ asValueType a) op]
|
|
return Proxy
|
|
|
|
div_f :: (Producer a, Producer b, OutType a ~ OutType b, IsFloat (OutType a) ~ True) => a -> b -> GenFun (OutType a)
|
|
div_f = fBinOp FDiv
|
|
|
|
min_f :: (Producer a, Producer b, OutType a ~ OutType b, IsFloat (OutType a) ~ True) => a -> b -> GenFun (OutType a)
|
|
min_f = fBinOp FMin
|
|
|
|
max_f :: (Producer a, Producer b, OutType a ~ OutType b, IsFloat (OutType a) ~ True) => a -> b -> GenFun (OutType a)
|
|
max_f = fBinOp FMax
|
|
|
|
copySign :: (Producer a, Producer b, OutType a ~ OutType b, IsFloat (OutType a) ~ True) => a -> b -> GenFun (OutType a)
|
|
copySign = fBinOp FCopySign
|
|
|
|
abs_f :: (Producer a, IsFloat (OutType a) ~ True) => a -> GenFun (OutType a)
|
|
abs_f = fUnOp FAbs
|
|
|
|
neg_f :: (Producer a, IsFloat (OutType a) ~ True) => a -> GenFun (OutType a)
|
|
neg_f = fUnOp FNeg
|
|
|
|
ceil_f :: (Producer a, IsFloat (OutType a) ~ True) => a -> GenFun (OutType a)
|
|
ceil_f = fUnOp FCeil
|
|
|
|
floor_f :: (Producer a, IsFloat (OutType a) ~ True) => a -> GenFun (OutType a)
|
|
floor_f = fUnOp FFloor
|
|
|
|
trunc_f :: (Producer a, IsFloat (OutType a) ~ True) => a -> GenFun (OutType a)
|
|
trunc_f = fUnOp FTrunc
|
|
|
|
nearest_f :: (Producer a, IsFloat (OutType a) ~ True) => a -> GenFun (OutType a)
|
|
nearest_f = fUnOp FAbs
|
|
|
|
sqrt_f :: (Producer a, IsFloat (OutType a) ~ True) => a -> GenFun (OutType a)
|
|
sqrt_f = fUnOp FAbs
|
|
|
|
lt_f :: (Producer a, Producer b, OutType a ~ OutType b, IsFloat (OutType a) ~ True) => a -> b -> GenFun (Proxy I32)
|
|
lt_f = fRelOp FLt
|
|
|
|
gt_f :: (Producer a, Producer b, OutType a ~ OutType b, IsFloat (OutType a) ~ True) => a -> b -> GenFun (Proxy I32)
|
|
gt_f = fRelOp FGt
|
|
|
|
le_f :: (Producer a, Producer b, OutType a ~ OutType b, IsFloat (OutType a) ~ True) => a -> b -> GenFun (Proxy I32)
|
|
le_f = fRelOp FLe
|
|
|
|
ge_f :: (Producer a, Producer b, OutType a ~ OutType b, IsFloat (OutType a) ~ True) => a -> b -> GenFun (Proxy I32)
|
|
ge_f = fRelOp FGe
|
|
|
|
i32c :: (Integral i) => i -> GenFun (Proxy I32)
|
|
i32c i = appendExpr [I32Const $ asWord32 $ fromIntegral i] >> return Proxy
|
|
|
|
i64c :: (Integral i) => i -> GenFun (Proxy I64)
|
|
i64c i = appendExpr [I64Const $ asWord64 $ fromIntegral i] >> return Proxy
|
|
|
|
f32c :: Float -> GenFun (Proxy F32)
|
|
f32c f = appendExpr [F32Const f] >> return Proxy
|
|
|
|
f64c :: Double -> GenFun (Proxy F64)
|
|
f64c d = appendExpr [F64Const d] >> return Proxy
|
|
|
|
wrap :: (Producer i, OutType i ~ Proxy I64) => i -> GenFun (Proxy I32)
|
|
wrap big = do
|
|
produce big
|
|
appendExpr [I32WrapI64]
|
|
return Proxy
|
|
|
|
trunc_u :: (Producer f, IsFloat (OutType f) ~ True, IsInt (Proxy t) ~ True, ValueTypeable t) => Proxy t -> f -> GenFun (Proxy t)
|
|
trunc_u t float = do
|
|
produce float
|
|
appendExpr [ITruncFU (getSize $ getValueType t) (getSize $ asValueType float)]
|
|
return Proxy
|
|
|
|
trunc_s :: (Producer f, IsFloat (OutType f) ~ True, IsInt (Proxy t) ~ True, ValueTypeable t) => Proxy t -> f -> GenFun (Proxy t)
|
|
trunc_s t float = do
|
|
produce float
|
|
appendExpr [ITruncFS (getSize $ getValueType t) (getSize $ asValueType float)]
|
|
return Proxy
|
|
|
|
extend_u :: (Producer i, OutType i ~ Proxy I32) => i -> GenFun (Proxy I64)
|
|
extend_u small = do
|
|
produce small
|
|
appendExpr [I64ExtendUI32]
|
|
return Proxy
|
|
|
|
extend_s :: (Producer i, OutType i ~ Proxy I32) => i -> GenFun (Proxy I64)
|
|
extend_s small = do
|
|
produce small
|
|
appendExpr [I64ExtendSI32]
|
|
return Proxy
|
|
|
|
convert_u :: (Producer i, IsInt (OutType i) ~ True, IsFloat (Proxy t) ~ True, ValueTypeable t) => Proxy t -> i -> GenFun (Proxy t)
|
|
convert_u t int = do
|
|
produce int
|
|
appendExpr [FConvertIU (getSize $ getValueType t) (getSize $ asValueType int)]
|
|
return Proxy
|
|
|
|
convert_s :: (Producer i, IsInt (OutType i) ~ True, IsFloat (Proxy t) ~ True, ValueTypeable t) => Proxy t -> i -> GenFun (Proxy t)
|
|
convert_s t int = do
|
|
produce int
|
|
appendExpr [FConvertIS (getSize $ getValueType t) (getSize $ asValueType int)]
|
|
return Proxy
|
|
|
|
demote :: (Producer f, OutType f ~ Proxy F64) => f -> GenFun (Proxy F32)
|
|
demote f = do
|
|
produce f
|
|
appendExpr [F32DemoteF64]
|
|
return Proxy
|
|
|
|
promote :: (Producer f, OutType f ~ Proxy F32) => f -> GenFun (Proxy F64)
|
|
promote f = do
|
|
produce f
|
|
appendExpr [F64PromoteF32]
|
|
return Proxy
|
|
|
|
type family SameSize a b where
|
|
SameSize (Proxy I32) (Proxy F32) = True
|
|
SameSize (Proxy I64) (Proxy F64) = True
|
|
SameSize (Proxy F32) (Proxy I32) = True
|
|
SameSize (Proxy F64) (Proxy I64) = True
|
|
SameSize a b = False
|
|
|
|
reinterpret :: (ValueTypeable t, Producer val, SameSize (Proxy t) (OutType val) ~ True) => Proxy t -> val -> GenFun (Proxy t)
|
|
reinterpret t val = do
|
|
case (getValueType t, asValueType val) of
|
|
(I32, F32) -> appendExpr [IReinterpretF BS32]
|
|
(I64, F64) -> appendExpr [IReinterpretF BS64]
|
|
(F32, I32) -> appendExpr [FReinterpretI BS32]
|
|
(F64, I64) -> appendExpr [FReinterpretI BS64]
|
|
_ -> error "Impossible by type constraint"
|
|
return Proxy
|
|
|
|
load :: (ValueTypeable t, Producer addr, OutType addr ~ Proxy I32, Integral offset, Integral align)
|
|
=> Proxy t
|
|
-> addr
|
|
-> offset
|
|
-> align
|
|
-> GenFun (Proxy t)
|
|
load t addr offset align = do
|
|
produce addr
|
|
case getValueType t of
|
|
I32 -> appendExpr [I32Load $ MemArg (fromIntegral offset) (fromIntegral align)]
|
|
I64 -> appendExpr [I64Load $ MemArg (fromIntegral offset) (fromIntegral align)]
|
|
F32 -> appendExpr [F32Load $ MemArg (fromIntegral offset) (fromIntegral align)]
|
|
F64 -> appendExpr [F64Load $ MemArg (fromIntegral offset) (fromIntegral align)]
|
|
return Proxy
|
|
|
|
load8_u :: (ValueTypeable t, IsInt (Proxy t) ~ True, Producer addr, OutType addr ~ Proxy I32, Integral offset, Integral align)
|
|
=> Proxy t
|
|
-> addr
|
|
-> offset
|
|
-> align
|
|
-> GenFun (Proxy t)
|
|
load8_u t addr offset align = do
|
|
produce addr
|
|
case getValueType t of
|
|
I32 -> appendExpr [I32Load8U $ MemArg (fromIntegral offset) (fromIntegral align)]
|
|
I64 -> appendExpr [I64Load8U $ MemArg (fromIntegral offset) (fromIntegral align)]
|
|
_ -> error "Impossible by type constraint"
|
|
return Proxy
|
|
|
|
load8_s :: (ValueTypeable t, IsInt (Proxy t) ~ True, Producer addr, OutType addr ~ Proxy I32, Integral offset, Integral align)
|
|
=> Proxy t
|
|
-> addr
|
|
-> offset
|
|
-> align
|
|
-> GenFun (Proxy t)
|
|
load8_s t addr offset align = do
|
|
produce addr
|
|
case getValueType t of
|
|
I32 -> appendExpr [I32Load8S $ MemArg (fromIntegral offset) (fromIntegral align)]
|
|
I64 -> appendExpr [I64Load8S $ MemArg (fromIntegral offset) (fromIntegral align)]
|
|
_ -> error "Impossible by type constraint"
|
|
return Proxy
|
|
|
|
load16_u :: (ValueTypeable t, IsInt (Proxy t) ~ True, Producer addr, OutType addr ~ Proxy I32, Integral offset, Integral align)
|
|
=> Proxy t
|
|
-> addr
|
|
-> offset
|
|
-> align
|
|
-> GenFun (Proxy t)
|
|
load16_u t addr offset align = do
|
|
produce addr
|
|
case getValueType t of
|
|
I32 -> appendExpr [I32Load16U $ MemArg (fromIntegral offset) (fromIntegral align)]
|
|
I64 -> appendExpr [I64Load16U $ MemArg (fromIntegral offset) (fromIntegral align)]
|
|
_ -> error "Impossible by type constraint"
|
|
return Proxy
|
|
|
|
load16_s :: (ValueTypeable t, IsInt (Proxy t) ~ True, Producer addr, OutType addr ~ Proxy I32, Integral offset, Integral align)
|
|
=> Proxy t
|
|
-> addr
|
|
-> offset
|
|
-> align
|
|
-> GenFun (Proxy t)
|
|
load16_s t addr offset align = do
|
|
produce addr
|
|
case getValueType t of
|
|
I32 -> appendExpr [I32Load16S $ MemArg (fromIntegral offset) (fromIntegral align)]
|
|
I64 -> appendExpr [I64Load16S $ MemArg (fromIntegral offset) (fromIntegral align)]
|
|
_ -> error "Impossible by type constraint"
|
|
return Proxy
|
|
|
|
load32_u :: (ValueTypeable t, IsInt (Proxy t) ~ True, Producer addr, OutType addr ~ Proxy I32, Integral offset, Integral align)
|
|
=> Proxy t
|
|
-> addr
|
|
-> offset
|
|
-> align
|
|
-> GenFun (Proxy t)
|
|
load32_u t addr offset align = do
|
|
produce addr
|
|
appendExpr [I64Load32U $ MemArg (fromIntegral offset) (fromIntegral align)]
|
|
return Proxy
|
|
|
|
load32_s :: (ValueTypeable t, IsInt (Proxy t) ~ True, Producer addr, OutType addr ~ Proxy I32, Integral offset, Integral align)
|
|
=> Proxy t
|
|
-> addr
|
|
-> offset
|
|
-> align
|
|
-> GenFun (Proxy t)
|
|
load32_s t addr offset align = do
|
|
produce addr
|
|
appendExpr [I64Load32S $ MemArg (fromIntegral offset) (fromIntegral align)]
|
|
return Proxy
|
|
|
|
store :: (Producer addr, OutType addr ~ Proxy I32, Producer val, Integral offset, Integral align)
|
|
=> addr
|
|
-> val
|
|
-> offset
|
|
-> align
|
|
-> GenFun ()
|
|
store addr val offset align = do
|
|
produce addr
|
|
produce val
|
|
case asValueType val of
|
|
I32 -> appendExpr [I32Store $ MemArg (fromIntegral offset) (fromIntegral align)]
|
|
I64 -> appendExpr [I64Store $ MemArg (fromIntegral offset) (fromIntegral align)]
|
|
F32 -> appendExpr [F32Store $ MemArg (fromIntegral offset) (fromIntegral align)]
|
|
F64 -> appendExpr [F64Store $ MemArg (fromIntegral offset) (fromIntegral align)]
|
|
|
|
store8 :: (Producer addr, OutType addr ~ Proxy I32, Producer val, IsInt (OutType val) ~ True, Integral offset, Integral align)
|
|
=> addr
|
|
-> val
|
|
-> offset
|
|
-> align
|
|
-> GenFun ()
|
|
store8 addr val offset align = do
|
|
produce addr
|
|
produce val
|
|
case asValueType val of
|
|
I32 -> appendExpr [I32Store8 $ MemArg (fromIntegral offset) (fromIntegral align)]
|
|
I64 -> appendExpr [I64Store8 $ MemArg (fromIntegral offset) (fromIntegral align)]
|
|
_ -> error "Impossible by type constraint"
|
|
|
|
store16 :: (Producer addr, OutType addr ~ Proxy I32, Producer val, IsInt (OutType val) ~ True, Integral offset, Integral align)
|
|
=> addr
|
|
-> val
|
|
-> offset
|
|
-> align
|
|
-> GenFun ()
|
|
store16 addr val offset align = do
|
|
produce addr
|
|
produce val
|
|
case asValueType val of
|
|
I32 -> appendExpr [I32Store16 $ MemArg (fromIntegral offset) (fromIntegral align)]
|
|
I64 -> appendExpr [I64Store16 $ MemArg (fromIntegral offset) (fromIntegral align)]
|
|
_ -> error "Impossible by type constraint"
|
|
|
|
store32 :: (Producer addr, OutType addr ~ Proxy I32, Producer val, OutType val ~ Proxy I64, Integral offset, Integral align)
|
|
=> addr
|
|
-> val
|
|
-> offset
|
|
-> align
|
|
-> GenFun ()
|
|
store32 addr val offset align = do
|
|
produce addr
|
|
produce val
|
|
appendExpr [I64Store32 $ MemArg (fromIntegral offset) (fromIntegral align)]
|
|
|
|
memorySize :: GenFun (Proxy I32)
|
|
memorySize = appendExpr [CurrentMemory] >> return Proxy
|
|
|
|
growMemory :: (Producer size, OutType size ~ Proxy I32) => size -> GenFun ()
|
|
growMemory size = produce size >> appendExpr [GrowMemory]
|
|
|
|
call :: (Returnable res) => Fn res -> [GenFun a] -> GenFun res
|
|
call (Fn idx) args = sequence_ args >> appendExpr [Call idx] >> return returnableValue
|
|
|
|
callIndirect :: (Producer index, OutType index ~ Proxy I32, Returnable res) => TypeDef res -> index -> [GenFun a] -> GenFun res
|
|
callIndirect (TypeDef idx) index args = do
|
|
sequence_ args
|
|
produce index
|
|
appendExpr [CallIndirect idx]
|
|
return returnableValue
|
|
|
|
br :: Label t -> GenFun ()
|
|
br (Label labelDeep) = do
|
|
deep <- ask
|
|
appendExpr [Br $ deep - labelDeep]
|
|
|
|
brIf :: (Producer pred, OutType pred ~ Proxy I32) => pred -> Label t -> GenFun ()
|
|
brIf pred (Label labelDeep) = do
|
|
produce pred
|
|
deep <- ask
|
|
appendExpr [BrIf $ deep - labelDeep]
|
|
|
|
brTable :: (Producer selector, OutType selector ~ Proxy I32) => selector -> [Label t] -> Label t -> GenFun ()
|
|
brTable selector labels (Label labelDeep) = do
|
|
produce selector
|
|
deep <- ask
|
|
appendExpr [BrTable (map (\(Label d) -> deep - d) labels) $ deep - labelDeep]
|
|
|
|
finish :: (Producer val) => val -> GenFun ()
|
|
finish val = do
|
|
produce val
|
|
appendExpr [Return]
|
|
|
|
newtype Label i = Label Natural deriving (Show, Eq)
|
|
|
|
when :: (Producer pred, OutType pred ~ Proxy I32)
|
|
=> pred
|
|
-> GenFun ()
|
|
-> GenFun ()
|
|
when pred body = if' () pred body (return ())
|
|
|
|
for :: (Producer pred, OutType pred ~ Proxy I32) => GenFun () -> pred -> GenFun () -> GenFun () -> GenFun ()
|
|
for initer pred after body = do
|
|
initer
|
|
let loopBody = do
|
|
body
|
|
after
|
|
loopLabel <- label
|
|
if' () pred (br loopLabel) (return ())
|
|
if' () pred (loop () loopBody) (return ())
|
|
|
|
while :: (Producer pred, OutType pred ~ Proxy I32) => pred -> GenFun () -> GenFun ()
|
|
while pred body = do
|
|
let loopBody = do
|
|
body
|
|
loopLabel <- label
|
|
if' () pred (br loopLabel) (return ())
|
|
if' () pred (loop () loopBody) (return ())
|
|
|
|
label :: GenFun (Label t)
|
|
label = Label <$> ask
|
|
|
|
if' :: (Producer pred, OutType pred ~ Proxy I32, Returnable res)
|
|
=> res
|
|
-> pred
|
|
-> GenFun res
|
|
-> GenFun res
|
|
-> GenFun res
|
|
if' res pred true false = do
|
|
produce pred
|
|
deep <- (+1) <$> ask
|
|
appendExpr [If (asResultValue res) (genExpr deep $ true) (genExpr deep $ false)]
|
|
return returnableValue
|
|
|
|
loop :: (Returnable res) => res -> GenFun res -> GenFun res
|
|
loop res body = do
|
|
deep <- (+1) <$> ask
|
|
appendExpr [Loop (asResultValue res) (genExpr deep $ body)]
|
|
return returnableValue
|
|
|
|
-- block :: (Returnable res) => res -> GenFun res -> GenFun res
|
|
-- block res body = do
|
|
-- deep <- (+1) <$> ask
|
|
-- appendExpr [Block (asResultValue res) (genExpr deep $ body)]
|
|
-- return returnableValue
|
|
|
|
trap :: Proxy t -> GenFun (Proxy t)
|
|
trap t = do
|
|
appendExpr [Unreachable]
|
|
return t
|
|
|
|
unreachable :: GenFun ()
|
|
unreachable = appendExpr [Unreachable]
|
|
|
|
class Consumer loc where
|
|
infixr 2 .=
|
|
(.=) :: (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]
|
|
|
|
newtype TypeDef t = TypeDef Natural deriving (Show, Eq)
|
|
|
|
typedef :: (Returnable res) => res -> [ValueType] -> GenMod (TypeDef res)
|
|
typedef res args = do
|
|
let t = FuncType args (asResultValue res)
|
|
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 $ TypeDef $ fromIntegral idx
|
|
|
|
newtype Fn a = Fn Natural deriving (Show, Eq)
|
|
|
|
class Returnable a where
|
|
asResultValue :: a -> [ValueType]
|
|
returnableValue :: a
|
|
|
|
instance (ValueTypeable t) => Returnable (Proxy t) where
|
|
asResultValue t = [getValueType t]
|
|
returnableValue = Proxy
|
|
|
|
instance Returnable () where
|
|
asResultValue _ = []
|
|
returnableValue = ()
|
|
|
|
funRec :: (Returnable res) => res -> (Fn res -> GenFun res) -> GenMod (Fn res)
|
|
funRec res generator = do
|
|
st@GenModState { target = m@Module { types, functions }, funcIdx } <- get
|
|
let FuncDef { args, locals, instrs } = execState (runReaderT (generator (Fn funcIdx)) 0) $ FuncDef [] [] [] []
|
|
let t = FuncType args (asResultValue res)
|
|
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 $ Fn funcIdx
|
|
|
|
fun :: (Returnable res) => res -> GenFun res -> GenMod (Fn res)
|
|
fun res = funRec res . const
|
|
|
|
declare :: (Returnable res) => res -> [ValueType] -> GenMod (Fn res)
|
|
declare res args = do
|
|
st@GenModState { target = m@Module { types, functions }, funcIdx } <- get
|
|
let t = FuncType args (asResultValue res)
|
|
let (idx, inserted) = Maybe.fromMaybe (length types, types ++ [t]) $ (\i -> (i, types)) <$> List.findIndex (== t) types
|
|
let err = error "Declared function doesn't have implementation"
|
|
put $ st {
|
|
target = m { functions = functions ++ [Function (fromIntegral idx) err err], types = inserted },
|
|
funcIdx = funcIdx + 1
|
|
}
|
|
return $ Fn funcIdx
|
|
|
|
implement :: (Returnable res) => Fn res -> GenFun res -> GenMod (Fn res)
|
|
implement (Fn funcIdx) generator = do
|
|
st@GenModState { target = m@Module { types, functions, imports } } <- get
|
|
let FuncDef { args, locals, instrs } = execState (runReaderT generator 0) $ FuncDef [] [] [] []
|
|
let locIdx = fromIntegral funcIdx - (length $ filter isFuncImport imports)
|
|
let (l, inst : r) = splitAt locIdx functions
|
|
let typeIdx = funcType inst
|
|
let FuncType ps _ = types !! fromIntegral typeIdx
|
|
if args /= ps then error "Arguments list in implementation doesn't match with declared type" else return ()
|
|
put $ st { target = m { functions = l ++ [Function typeIdx locals instrs] ++ r } }
|
|
return $ Fn funcIdx
|
|
|
|
nextFuncIndex :: GenMod Natural
|
|
nextFuncIndex = gets 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)
|
|
|
|
importFunction :: (Returnable res) => TL.Text -> TL.Text -> res -> [ValueType] -> GenMod (Fn res)
|
|
importFunction mod name res params = do
|
|
st@GenModState { target = m@Module { types, imports }, funcIdx } <- get
|
|
let t = FuncType params (asResultValue res)
|
|
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 (Fn funcIdx)
|
|
|
|
importGlobal :: (ValueTypeable t) => TL.Text -> TL.Text -> Proxy t -> GenMod (Glob t)
|
|
importGlobal mod name t = do
|
|
st@GenModState { target = m@Module { imports }, globIdx } <- get
|
|
put $ st {
|
|
target = m { imports = imports ++ [Import mod name $ ImportGlobal $ Const $ getValueType t] },
|
|
globIdx = globIdx + 1
|
|
}
|
|
return $ Glob globIdx
|
|
|
|
importMemory :: TL.Text -> TL.Text -> Natural -> Maybe Natural -> GenMod Mem
|
|
importMemory mod name min max = do
|
|
modify $ \(st@GenModState { target = m }) -> st {
|
|
target = m { imports = imports m ++ [Import mod name $ ImportMemory $ Limit min max] }
|
|
}
|
|
return $ Mem 0
|
|
|
|
importTable :: TL.Text -> TL.Text -> Natural -> Maybe Natural -> GenMod Tbl
|
|
importTable mod name min max = do
|
|
modify $ \(st@GenModState { target = m }) -> st {
|
|
target = m { imports = imports m ++ [Import mod name $ ImportTable $ TableType (Limit min max) FuncRef] }
|
|
}
|
|
return $ Tbl 0
|
|
|
|
class Exportable e where
|
|
type AfterExport e
|
|
export :: TL.Text -> e -> GenMod (AfterExport e)
|
|
|
|
instance (Exportable e) => Exportable (GenMod e) where
|
|
type AfterExport (GenMod e) = AfterExport e
|
|
export name def = do
|
|
ent <- def
|
|
export name ent
|
|
|
|
instance Exportable (Fn t) where
|
|
type AfterExport (Fn t) = Fn t
|
|
export name (Fn funIdx) = do
|
|
modify $ \(st@GenModState { target = m }) -> st {
|
|
target = m { exports = exports m ++ [Export name $ ExportFunc funIdx] }
|
|
}
|
|
return (Fn funIdx)
|
|
|
|
instance Exportable (Glob t) where
|
|
type AfterExport (Glob t) = Glob t
|
|
export name g@(Glob idx) = do
|
|
modify $ \(st@GenModState { target = m }) -> st {
|
|
target = m { exports = exports m ++ [Export name $ ExportGlobal idx] }
|
|
}
|
|
return g
|
|
|
|
instance Exportable Mem where
|
|
type AfterExport Mem = Mem
|
|
export name (Mem memIdx) = do
|
|
modify $ \(st@GenModState { target = m }) -> st {
|
|
target = m { exports = exports m ++ [Export name $ ExportMemory memIdx] }
|
|
}
|
|
return (Mem memIdx)
|
|
|
|
instance Exportable Tbl where
|
|
type AfterExport Tbl = Tbl
|
|
export name (Tbl tableIdx) = do
|
|
modify $ \(st@GenModState { target = m }) -> st {
|
|
target = m { exports = exports m ++ [Export name $ ExportTable tableIdx] }
|
|
}
|
|
return (Tbl tableIdx)
|
|
|
|
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
|
|
idx <- gets globIdx
|
|
modify $ \(st@GenModState { target = m }) -> st {
|
|
target = m { globals = globals m ++ [Global (mkType $ getValueType t) (initWith t val)] },
|
|
globIdx = idx + 1
|
|
}
|
|
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 }
|
|
}
|
|
|
|
newtype Mem = Mem Natural deriving (Show, Eq)
|
|
|
|
memory :: Natural -> Maybe Natural -> GenMod Mem
|
|
memory min max = do
|
|
modify $ \(st@GenModState { target = m }) -> st {
|
|
target = m { mems = mems m ++ [Memory $ Limit min max] }
|
|
}
|
|
return $ Mem 0
|
|
|
|
newtype Tbl = Tbl Natural deriving (Show, Eq)
|
|
|
|
table :: Natural -> Maybe Natural -> GenMod Tbl
|
|
table min max = do
|
|
modify $ \(st@GenModState { target = m }) -> st {
|
|
target = m { tables = tables m ++ [Table $ TableType (Limit min max) FuncRef] }
|
|
}
|
|
return $ Tbl 0
|
|
|
|
dataSegment :: (Producer offset, OutType offset ~ Proxy I32) => offset -> LBS.ByteString -> GenMod ()
|
|
dataSegment offset bytes =
|
|
modify $ \(st@GenModState { target = m }) -> st {
|
|
target = m { datas = datas m ++ [DataSegment 0 (genExpr 0 (produce offset)) bytes] }
|
|
}
|
|
|
|
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 <- importFunction "rts" "gc" () [I32]
|
|
memory 10 Nothing
|
|
|
|
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 i32 $ do
|
|
size <- param i32
|
|
(size `add` i32c 3) `and` i32c 0xFFFFFFFC
|
|
alloc <- funRec i32 $ \self -> do
|
|
size <- param i32
|
|
alignedSize <- local i32
|
|
addr <- local i32
|
|
alignedSize .= call aligned [arg size]
|
|
if' i32 ((heapNext `add` alignedSize) `lt_u` heapEnd)
|
|
(do
|
|
addr .= heapNext
|
|
heapNext .= heapNext `add` alignedSize
|
|
ret addr
|
|
)
|
|
(do
|
|
call gc []
|
|
call self [arg size]
|
|
)
|
|
return ()
|