ghc-9.14.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.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
-- | Generate statements to set infotable field values for the given ClosureInfo
--
-- Depending on debug flag, it generates h$setObjInfo(...) or h$o(...). The
-- latter form doesn't store the pretty-printed name in the closure to save
-- space.
closureInfoStat :: Bool -> ClosureInfo -> JStgStat
closureInfoStat debug ci
= setObjInfoL debug (ciVar ci) (ciRegs ci) (ciLayout ci) ty (ciName ci) tag (ciStatic ci)
where
!ty = case ciType ci of
CIThunk -> Thunk
CIFun {} -> Fun
CICon {} -> Con
CIBlackhole -> Blackhole
CIPap -> Pap
CIStackFrame -> StackFrame
!tag = case ciType ci 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
{ ciVar = symbol
, ciRegs = CIRegs 0 [PtrV]
, ciName = name
, ciLayout = layout
, ciType = CICon constr
, ciStatic = mempty
}
body = pure returnStack
-- | Used to pass arguments to newClosure with some safety
data Closure = Closure
{ clInfo :: JStgExpr -- ^ InfoTable object
, clField1 :: JStgExpr -- ^ Payload field 1
, clField2 :: JStgExpr -- ^ Payload field 2
, clMeta :: JStgExpr
, clCC :: Maybe JStgExpr
}
newClosure :: Closure -> JStgExpr
newClosure Closure{..} =
let xs = [ (closureInfo_ , clInfo)
, (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
[ closureInfo t |= clInfo
, 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
[ closureInfo t |= closureInfo 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 info fields meta cc = Closure
{ clInfo = info
, 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 (name . mkFastString . ('x':) . show) [(0::Int)..jsClosureCount])
varName :: Int -> Ident
varName i
| i < 0 || i > jsClosureCount = name $ mkFastString ('x' : show i)
| otherwise = varCache ! i