ghc-9.10.1: GHC/StgToJS/Closure.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
module GHC.StgToJS.Closure
( closureInfoStat
, closure
, conClosure
, Closure (..)
, newClosure
, assignClosure
, CopyCC (..)
, copyClosure
, mkClosure
-- $names
, allocData
, allocClsA
, dataName
, clsName
, dataFieldName
, varName
, jsClosureCount
)
where
import GHC.Prelude
import GHC.Data.FastString
import GHC.StgToJS.Heap
import GHC.StgToJS.Types
import GHC.StgToJS.Utils
import GHC.StgToJS.Regs (stack,sp)
import GHC.JS.Make
import GHC.JS.JStg.Syntax
import GHC.JS.JStg.Monad
import GHC.JS.Ident
import GHC.Types.Unique.Map
import Data.Array
import Data.Monoid
import qualified Data.Bits as Bits
closureInfoStat :: Bool -> ClosureInfo -> JStgStat
closureInfoStat debug (ClosureInfo obj rs name layout ctype srefs)
= setObjInfoL debug obj rs layout ty name tag srefs
where
!ty = case ctype of
CIThunk -> Thunk
CIFun {} -> Fun
CICon {} -> Con
CIBlackhole -> Blackhole
CIPap -> Pap
CIStackFrame -> StackFrame
!tag = case ctype of
CIThunk -> 0
CIFun arity nregs -> mkArityTag arity nregs
CICon con -> con
CIBlackhole -> 0
CIPap -> 0
CIStackFrame -> 0
setObjInfoL :: Bool -- ^ debug: output symbol names
-> Ident -- ^ the object name
-> CIRegs -- ^ things in registers
-> CILayout -- ^ layout of the object
-> ClosureType -- ^ closure type
-> FastString -- ^ object name, for printing
-> Int -- ^ `a' argument, depends on type (arity, conid)
-> CIStatic -- ^ static refs
-> JStgStat
setObjInfoL debug obj rs layout t n a
= setObjInfo debug obj t n field_types a size rs
where
size = case layout of
CILayoutVariable -> (-1)
CILayoutUnknown sz -> sz
CILayoutFixed sz _ -> sz
field_types = case layout of
CILayoutVariable -> []
CILayoutUnknown size -> to_type_list (replicate size ObjV)
CILayoutFixed _ fs -> to_type_list fs
to_type_list = concatMap (\x -> replicate (varSize x) (fromEnum x))
setObjInfo :: Bool -- ^ debug: output all symbol names
-> Ident -- ^ the thing to modify
-> ClosureType -- ^ closure type
-> FastString -- ^ object name, for printing
-> [Int] -- ^ list of item types in the object, if known (free variables, datacon fields)
-> Int -- ^ extra 'a' parameter, for constructor tag or arity
-> Int -- ^ object size, -1 (number of vars) for unknown
-> CIRegs -- ^ things in registers
-> CIStatic -- ^ static refs
-> JStgStat
setObjInfo debug obj t name fields a size regs static
| debug = appS "h$setObjInfo" [ toJExpr obj
, toJExpr t
, toJExpr name
, toJExpr fields
, toJExpr a
, toJExpr size
, toJExpr (regTag regs)
, toJExpr static
]
| otherwise = appS "h$o" [ toJExpr obj
, toJExpr t
, toJExpr a
, toJExpr size
, toJExpr (regTag regs)
, toJExpr static
]
where
regTag CIRegsUnknown = -1
regTag (CIRegs skip types) =
let nregs = sum $ fmap varSize types
in skip + (nregs `Bits.shiftL` 8)
-- | Special case of closures that do not need to generate any @fresh@ names
closure :: ClosureInfo -- ^ object being info'd see @ciVar@
-> (JSM JStgStat) -- ^ rhs
-> JSM JStgStat
closure ci body = do f <- (jFunction' (ciVar ci) body)
return $ f `mappend` closureInfoStat False ci
conClosure :: Ident -> FastString -> CILayout -> Int -> JSM JStgStat
conClosure symbol name layout constr = closure ci body
where
ci = (ClosureInfo symbol (CIRegs 0 [PtrV]) name layout (CICon constr) mempty)
body = pure . returnS $ stack .! sp
-- | Used to pass arguments to newClosure with some safety
data Closure = Closure
{ clEntry :: JStgExpr
, clField1 :: JStgExpr
, clField2 :: JStgExpr
, clMeta :: JStgExpr
, clCC :: Maybe JStgExpr
}
newClosure :: Closure -> JStgExpr
newClosure Closure{..} =
let xs = [ (closureEntry_ , clEntry)
, (closureField1_, clField1)
, (closureField2_, clField2)
, (closureMeta_ , clMeta)
]
in case clCC of
-- CC field is optional (probably to minimize code size as we could assign
-- null_, but we get the same effect implicitly)
Nothing -> ValExpr (jhFromList xs)
Just cc -> ValExpr (jhFromList $ (closureCC_,cc) : xs)
assignClosure :: JStgExpr -> Closure -> JStgStat
assignClosure t Closure{..} = BlockStat
[ closureEntry t |= clEntry
, closureField1 t |= clField1
, closureField2 t |= clField2
, closureMeta t |= clMeta
] <> case clCC of
Nothing -> mempty
Just cc -> closureCC t |= cc
data CopyCC = CopyCC | DontCopyCC
copyClosure :: CopyCC -> JStgExpr -> JStgExpr -> JStgStat
copyClosure copy_cc t s = BlockStat
[ closureEntry t |= closureEntry s
, closureField1 t |= closureField1 s
, closureField2 t |= closureField2 s
, closureMeta t |= closureMeta s
] <> case copy_cc of
DontCopyCC -> mempty
CopyCC -> closureCC t |= closureCC s
mkClosure :: JStgExpr -> [JStgExpr] -> JStgExpr -> Maybe JStgExpr -> Closure
mkClosure entry fields meta cc = Closure
{ clEntry = entry
, clField1 = x1
, clField2 = x2
, clMeta = meta
, clCC = cc
}
where
x1 = case fields of
[] -> null_
x:_ -> x
x2 = case fields of
[] -> null_
[_] -> null_
[_,x] -> x
_:x:xs -> ValExpr . JHash . listToUniqMap $ zip (fmap dataFieldName [1..]) (x:xs)
-------------------------------------------------------------------------------
-- Name Caches
-------------------------------------------------------------------------------
-- $names
-- | Cache "dXXX" field names
dataFieldCache :: Array Int FastString
dataFieldCache = listArray (0,nFieldCache) (fmap (mkFastString . ('d':) . show) [(0::Int)..nFieldCache])
-- | Data names are used in the AST, and logging has determined that 255 is the maximum number we see.
nFieldCache :: Int
nFieldCache = 255
-- | We use this in the RTS to determine the number of generated closures. These closures use the names
-- cached here, so we bind them to the same number.
jsClosureCount :: Int
jsClosureCount = 24
dataFieldName :: Int -> FastString
dataFieldName i
| i < 0 || i > nFieldCache = mkFastString ('d' : show i)
| otherwise = dataFieldCache ! i
-- | Cache "h$dXXX" names
dataCache :: Array Int FastString
dataCache = listArray (0,jsClosureCount) (fmap (mkFastString . ("h$d"++) . show) [(0::Int)..jsClosureCount])
dataName :: Int -> FastString
dataName i
| i < 0 || i > nFieldCache = mkFastString ("h$d" ++ show i)
| otherwise = dataCache ! i
allocData :: Int -> JStgExpr
allocData i = toJExpr (global (dataName i))
-- | Cache "h$cXXX" names
clsCache :: Array Int FastString
clsCache = listArray (0,jsClosureCount) (fmap (mkFastString . ("h$c"++) . show) [(0::Int)..jsClosureCount])
clsName :: Int -> FastString
clsName i
| i < 0 || i > jsClosureCount = mkFastString ("h$c" ++ show i)
| otherwise = clsCache ! i
allocClsA :: Int -> JStgExpr
allocClsA i = toJExpr (global (clsName i))
-- | Cache "xXXX" names
varCache :: Array Int Ident
varCache = listArray (0,jsClosureCount) (fmap (global . mkFastString . ('x':) . show) [(0::Int)..jsClosureCount])
varName :: Int -> Ident
varName i
| i < 0 || i > jsClosureCount = global $ mkFastString ('x' : show i)
| otherwise = varCache ! i